From 23fe0e1663b5bce563390c600b69ac2902bb94d7 Mon Sep 17 00:00:00 2001 From: Mudit Vats Date: Tue, 1 Sep 2020 16:04:04 -0700 Subject: [PATCH 1/5] Developer Experience Release. Added LMS and code refactor. --- AMTHIClient/Include/GetControlModeCommand.h | 76 - AMTHIClient/Src/GetControlModeCommand.cpp | 51 - CMakeLists.txt | 56 +- MicroLMS/CMakeLists.txt | 198 + MicroLMS/MicroLMS/main.c | 148 + MicroLMS/MicroLMS/resource.h | 15 + MicroLMS/MicroLMS/stdafx.cpp | 8 + MicroLMS/MicroLMS/stdafx.h | 15 + MicroLMS/MicroLMS/targetver.h | 13 + MicroLMS/core/utils.c | 78 + MicroLMS/core/utils.h | 43 + MicroLMS/heci/HECILinux.c | 434 + MicroLMS/heci/HECILinux.h | 128 + MicroLMS/heci/HECIWin.c | 377 + MicroLMS/heci/HECIWin.h | 49 + MicroLMS/heci/HECI_if.h | 199 + MicroLMS/heci/LMEConnection.c | 554 ++ MicroLMS/heci/LMEConnection.h | 185 + MicroLMS/heci/LMS_if.h | 173 + MicroLMS/heci/LMS_if_constants.h | 92 + MicroLMS/heci/PTHICommand.c | 1457 +++ MicroLMS/heci/PTHICommand.h | 815 ++ MicroLMS/heci/StatusCodeDefinitions.h | 403 + MicroLMS/heci/mei.h | 57 + MicroLMS/microstack/ILibAsyncServerSocket.c | 614 ++ MicroLMS/microstack/ILibAsyncServerSocket.h | 155 + MicroLMS/microstack/ILibAsyncSocket.c | 1677 ++++ MicroLMS/microstack/ILibAsyncSocket.h | 195 + MicroLMS/microstack/ILibLMS.c | 834 ++ MicroLMS/microstack/ILibLMS.h | 26 + MicroLMS/microstack/ILibParsers.c | 8763 +++++++++++++++++++ MicroLMS/microstack/ILibParsers.h | 1302 +++ MicroLMS/microstack/ILibRemoteLogging.h | 95 + README.md | 82 +- activation.cpp | 321 + activation.h | 33 + args.cpp | 95 + args.h | 30 + commands.cpp | 704 +- commands.h | 35 +- info.cpp | 217 + info.h | 32 + lms.cmake.in | 15 - lms.cpp | 2 +- lms.h | 2 +- main.cpp | 341 +- network.cpp | 196 + network.h | 24 + port.h | 37 + usage.cpp | 61 + usage.h | 23 + utils.cpp | 70 + utils.h | 28 + 53 files changed, 20738 insertions(+), 895 deletions(-) delete mode 100644 AMTHIClient/Include/GetControlModeCommand.h delete mode 100644 AMTHIClient/Src/GetControlModeCommand.cpp create mode 100644 MicroLMS/CMakeLists.txt create mode 100644 MicroLMS/MicroLMS/main.c create mode 100644 MicroLMS/MicroLMS/resource.h create mode 100644 MicroLMS/MicroLMS/stdafx.cpp create mode 100644 MicroLMS/MicroLMS/stdafx.h create mode 100644 MicroLMS/MicroLMS/targetver.h create mode 100644 MicroLMS/core/utils.c create mode 100644 MicroLMS/core/utils.h create mode 100644 MicroLMS/heci/HECILinux.c create mode 100644 MicroLMS/heci/HECILinux.h create mode 100644 MicroLMS/heci/HECIWin.c create mode 100644 MicroLMS/heci/HECIWin.h create mode 100644 MicroLMS/heci/HECI_if.h create mode 100644 MicroLMS/heci/LMEConnection.c create mode 100644 MicroLMS/heci/LMEConnection.h create mode 100644 MicroLMS/heci/LMS_if.h create mode 100644 MicroLMS/heci/LMS_if_constants.h create mode 100644 MicroLMS/heci/PTHICommand.c create mode 100644 MicroLMS/heci/PTHICommand.h create mode 100644 MicroLMS/heci/StatusCodeDefinitions.h create mode 100644 MicroLMS/heci/mei.h create mode 100644 MicroLMS/microstack/ILibAsyncServerSocket.c create mode 100644 MicroLMS/microstack/ILibAsyncServerSocket.h create mode 100644 MicroLMS/microstack/ILibAsyncSocket.c create mode 100644 MicroLMS/microstack/ILibAsyncSocket.h create mode 100644 MicroLMS/microstack/ILibLMS.c create mode 100644 MicroLMS/microstack/ILibLMS.h create mode 100644 MicroLMS/microstack/ILibParsers.c create mode 100644 MicroLMS/microstack/ILibParsers.h create mode 100644 MicroLMS/microstack/ILibRemoteLogging.h create mode 100644 activation.cpp create mode 100644 activation.h create mode 100644 args.cpp create mode 100644 args.h create mode 100644 info.cpp create mode 100644 info.h delete mode 100644 lms.cmake.in create mode 100644 network.cpp create mode 100644 network.h create mode 100644 port.h create mode 100644 usage.cpp create mode 100644 usage.h create mode 100644 utils.cpp create mode 100644 utils.h diff --git a/AMTHIClient/Include/GetControlModeCommand.h b/AMTHIClient/Include/GetControlModeCommand.h deleted file mode 100644 index d2caefd..0000000 --- a/AMTHIClient/Include/GetControlModeCommand.h +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ -/* - * Copyright (C) 2010-2019 Intel Corporation - */ -/*++ - -@file: GetControlModeCommand.h - ---*/ - -#ifndef __GET_CONTROL_MODE_COMMAND_H__ -#define __GET_CONTROL_MODE_COMMAND_H__ - -#include "AMTHICommand.h" -#include "MEIparser.h" - -namespace Intel -{ -namespace MEI_Client -{ -namespace AMTHI_Client -{ - struct GET_CONTROL_MODE_RESPONSE - { - uint32_t ControlMode; - - void parse (std::vector::const_iterator& itr, const std::vector::const_iterator end) - { - Intel::MEI_Client::parseData(*this, itr, end); - } - }; - - class GetControlModeRequest; - class GetControlModeCommand : public AMTHICommand - { - public: - - GetControlModeCommand(); - virtual ~GetControlModeCommand() {} - - virtual void reTransact(); - GET_CONTROL_MODE_RESPONSE getResponse(); - - private: - virtual void parseResponse(const std::vector& buffer); - - std::shared_ptr> m_response; - - static const uint32_t RESPONSE_COMMAND_NUMBER = 0x0480006B; - }; - - class GetControlModeRequest : public AMTHICommandRequest - { - public: - GetControlModeRequest() {} - virtual ~GetControlModeRequest() {} - - private: - static const uint32_t REQUEST_COMMAND_NUMBER = 0x0400006B; - virtual unsigned int requestHeaderCommandNumber() - { - //this is the command number (taken from the AMTHI document) - return REQUEST_COMMAND_NUMBER; - } - - virtual uint32_t requestDataSize() - { - return 0; - } - virtual std::vector SerializeData(); - }; -} // namespace AMTHI_Client -} // namespace MEI_Client -} // namespace Intel - -#endif //__GET_CONTROL_MODE_COMMAND_H__ \ No newline at end of file diff --git a/AMTHIClient/Src/GetControlModeCommand.cpp b/AMTHIClient/Src/GetControlModeCommand.cpp deleted file mode 100644 index 571ff7f..0000000 --- a/AMTHIClient/Src/GetControlModeCommand.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ -/* - * Copyright (C) 2010-2019 Intel Corporation - */ -/*++ - -@file: GetControlModeCommand.cpp - ---*/ - -#include "GetControlModeCommand.h" -#include "StatusCodeDefinitions.h" -#include - -using namespace std; - -using namespace Intel::MEI_Client::AMTHI_Client; - -GetControlModeCommand::GetControlModeCommand() -{ - shared_ptr tmp(new GetControlModeRequest()); - m_request = tmp; - Transact(); -} - -void GetControlModeCommand::reTransact() -{ - shared_ptr tmp(new GetControlModeRequest()); - m_request = tmp; - Transact(); -} - -GET_CONTROL_MODE_RESPONSE GetControlModeCommand::getResponse() -{ - return m_response->getResponse(); -} - -void -GetControlModeCommand::parseResponse(const vector& buffer) -{ - shared_ptr> tmp( - new AMTHICommandResponse(buffer, RESPONSE_COMMAND_NUMBER)); - m_response = tmp; -} - -std::vector -GetControlModeRequest::SerializeData() -{ - vector output; - return output; -} diff --git a/CMakeLists.txt b/CMakeLists.txt index e2e0383..5e14006 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ if (UNIX) #Secure library usage and secure compile flags add_definitions (-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -Wformat -Wformat-security) - add_definitions (-fno-strict-overflow -fno-delete-null-pointer-checks -fwrapv) + add_definitions (-fno-strict-overflow -fno-delete-null-pointer-checks -fwrapv -fpermissive) else (UNIX) add_definitions (/GS /sdl) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NXCompat /DynamicBase") @@ -39,21 +39,10 @@ else (UNIX) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") endif (UNIX) - -# Download and unpack lms at configure time -configure_file(lms.cmake.in - lms-download/CMakeLists.txt) -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lms-download ) -execute_process(COMMAND ${CMAKE_COMMAND} --build . - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lms-download ) - -# Add MEIClient directly to our build. This adds -# the following targets: LmsMEIClient and LIBMETEE -add_subdirectory(${CMAKE_BINARY_DIR}/lms-src/MEIClient - ${CMAKE_BINARY_DIR}/lms-build/MEIClient) -add_dependencies(LmsMEIClient libmetee) - +# Add MicroLMS directly to our build. This adds +# the following targets: MicroLMS +add_subdirectory(MicroLMS) +#add_dependencies(rpc MicroLMS) if (UNIX) @@ -63,6 +52,7 @@ set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) # Find Boost +set(Boost_USE_STATIC_LIBS ON) find_package(Boost COMPONENTS system REQUIRED) # Find OpenSSL @@ -94,17 +84,28 @@ endif (UNIX) # ccu-poc add_executable (rpc - AMTHIClient/Include/GetControlModeCommand.h - AMTHIClient/Src/GetControlModeCommand.cpp + info.h + info.cpp + args.h + args.cpp + usage.h + usage.cpp + port.h + utils.h + utils.cpp + network.h + network.cpp commands.h commands.cpp + activation.h + activation.cpp lms.h lms.cpp main.cpp ) target_include_directories(rpc PUBLIC - "AMTHIClient/Include/" + "MicroLMS/heci" ) if (UNIX) @@ -112,20 +113,33 @@ if (UNIX) add_dependencies(rpc cpprestsdk) target_link_libraries (rpc PRIVATE - LmsMEIClient + MicroLMS cpprest ) else (UNIX) +add_dependencies(rpc MicroLMS ) + target_link_libraries (rpc PRIVATE - LmsMEIClient + MicroLMS iphlpapi cpprestsdk::cpprest cpprestsdk::cpprestsdk_zlib_internal cpprestsdk::cpprestsdk_boost_internal cpprestsdk::cpprestsdk_brotli_internal ${Boost_LIBRARIES} + DbgHelp.lib + Iphlpapi.lib + Setupapi.lib + ws2_32.lib + Psapi.lib + Crypt32.lib + Wintrust.lib + Version.lib + Wtsapi32.lib + Gdiplus.lib + Userenv.lib ) endif (UNIX) diff --git a/MicroLMS/CMakeLists.txt b/MicroLMS/CMakeLists.txt new file mode 100644 index 0000000..263960c --- /dev/null +++ b/MicroLMS/CMakeLists.txt @@ -0,0 +1,198 @@ +cmake_minimum_required (VERSION 3.4) + +########################################################### +# Set project options +# + +# set the project name +project(MicroLMS) + +set (CMAKE_CXX_STANDARD 11) + +set (CMAKE_POSITION_INDEPENDENT_CODE ON) + +# set to ON to build library, OFF to build executable +set (BUILD_LIBRARY ON) + +include_directories( + ${PROJECT_BINARY_DIR} + MicroLMS + core + heci) + +########################################################### +# Compiler / linker options +# + +if (UNIX) +# +# Linux Compiler and Linker Options + +# Find threads [unix it pthreads] +set(CMAKE_THREAD_PREFER_PTHREAD TRUE) +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads REQUIRED) + +if (${BUILD_LIBRARY}) +add_definitions(-D BUILD_LIBRARY) +endif (${BUILD_LIBRARY}) + +add_definitions( -D_POSIX ) + +else (UNIX) +# Windows Compiler and Linker Options +if (${BUILD_LIBRARY}) +add_definitions(/D BUILD_LIBRARY) +endif (${BUILD_LIBRARY}) + +add_definitions (/GS /GL /W3 /Gy /Zc:wchar_t /Zi /Zc:inline /fp:precise /WX- /Zc:forScope /Gd /Oi /FC /EHsc /nologo ) +add_definitions (/D WIN32 /D WIN64 /D NDEBUG /D _CONSOLE /D MICROSTACK_NO_STDAFX /D WINSOCK2 /D MICROSTACK_NOTLS /D _UNICODE /D UNICODE) +add_definitions (/errorReport:prompt /diagnostics:column) + +add_compile_options ($<$:/O1>) +add_compile_options (/MT$<$:d>) +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /Gm-") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /Gm /Od /D _DEBUG /D MEMORY_CHECK /D __STDC__ /D _CRT_SECURE_NO_WARNINGS /D ILibWebServer_SESSION_TRACKING /Gd /MDd") + +set (CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} + /MANIFEST + /LTCG:incremental + /NXCOMPAT + /DynamicBase + /MACHINE:X64 + /OPT:REF + /INCREMENTAL:NO + /SUBSYSTEM:CONSOLE + /MANIFESTUAC:\"level='asInvoker' uiAccess='false'\" + /OPT:ICF + /ERRORREPORT:PROMPT + /NOLOGO + /TLBID:1" +) +endif (UNIX) + +########################################################### +# Create the binary +# + +if (UNIX) +# +# Linux Library and Executable Build +# + +if (BUILD_LIBRARY) +# Create a library +add_library ( + MicroLMS + STATIC + MicroLMS/main.c + core/utils.c + heci/HECILinux.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS ) + +else (BUILD_LIBRARY) +# Create an executable +add_executable ( + MicroLMS + MicroLMS/main.c + core/utils.c + heci/HECILinux.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS + PRIVATE + pthread + ) + +endif (BUILD_LIBRARY) + + +else (UNIX) +# +# Windows Library and Executable Build +# + +if (BUILD_LIBRARY) +# Create a library +add_library ( + MicroLMS + STATIC + MicroLMS/main.c + MicroLMS/stdafx.cpp + core/utils.c + heci/HECIWin.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS ) + +else (BUILD_LIBRARY) +# Create an executable +add_executable ( + MicroLMS + MicroLMS/main.c + MicroLMS/stdafx.cpp + core/utils.c + heci/HECIWin.c + heci/LMEConnection.c + heci/PTHICommand.c + microstack/ILibAsyncServerSocket.c + microstack/ILibAsyncSocket.c + microstack/ILibLMS.c + microstack/ILibParsers.c + ) + +target_link_libraries ( + MicroLMS + PRIVATE + DbgHelp.lib + Iphlpapi.lib + Setupapi.lib + ws2_32.lib + Psapi.lib + Crypt32.lib + Wintrust.lib + Version.lib + Wtsapi32.lib + Gdiplus.lib + Userenv.lib +) + +endif (BUILD_LIBRARY) + + + + + + + + + + + + +endif (UNIX) + diff --git a/MicroLMS/MicroLMS/main.c b/MicroLMS/MicroLMS/main.c new file mode 100644 index 0000000..0d20ec6 --- /dev/null +++ b/MicroLMS/MicroLMS/main.c @@ -0,0 +1,148 @@ +/* +Copyright 2006 - 2013 Intel Corporation + +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. +*/ + +#if defined(WIN32) || defined (_WIN32_WCE) +#ifndef MICROSTACK_NO_STDAFX +#include "stdafx.h" +#endif +#else +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#if defined(WIN32) +#define _CRTDBG_MAP_ALLOC +#ifdef _DEBUG +#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) +#define new DEBUG_NEW +#endif +#endif + +#if defined(WINSOCK2) + #include + #include +#elif defined(WINSOCK1) + #include + #include +#endif + +#include +#include "../microstack/ILibParsers.h" +#include "../microstack/ILibLMS.h" + +#if defined(WIN32) & !defined(_CONSOLE) +#include "resource.h" +#endif + +#if defined(WIN32) && defined (_DEBUG) +#include +#endif + +// The following macros set and clear, respectively, given bits +// of the C runtime library debug flag, as specified by a bitmask. +#ifdef _DEBUG +#define SET_CRT_DEBUG_FIELD(a) \ + _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) +#define CLEAR_CRT_DEBUG_FIELD(a) \ + _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) +#else +#define SET_CRT_DEBUG_FIELD(a) ((void) 0) +#define CLEAR_CRT_DEBUG_FIELD(a) ((void) 0) +#endif + +#ifdef MEMORY_CHECK +#ifdef WIN32 +// This routine place comments at the head of a section of debug output +void OutputHeading( const char * explanation ) +{ + _RPT1( _CRT_WARN, "\n\n%s:\n**************************************************************************\n", explanation ); +} +#endif +#endif + +void *Chain = NULL; +struct ILibLMS_StateModule *MicroLMS = NULL; + +extern void ctrl_SendSubscriptionEvent(char *data, int datalen); +void BreakSink(int s) +{ + UNREFERENCED_PARAMETER( s ); + signal(SIGINT, SIG_IGN); // To ignore any more ctrl-c interrupts + ILibStopChain(Chain); +} + +#if defined(_POSIX) || defined (_CONSOLE) + +#if defined (BUILD_LIBRARY) +int main_micro_lms() +#else +int main(int argc, char **argv) +#endif + +#else +DWORD WINAPI GPMain(LPVOID lpParameter) +#endif +{ +// Shutdown on Ctrl + C +signal(SIGINT, BreakSink); + +#ifdef _POSIX + signal(SIGPIPE, SIG_IGN); +#ifdef _DEBUG + //mtrace(); +#endif +#endif + +#ifdef MEMORY_CHECK +#ifdef WIN32 + //SET_CRT_DEBUG_FIELD( _CRTDBG_DELAY_FREE_MEM_DF ); + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF); +#endif +#endif + + Chain = ILibCreateChain(); + MicroLMS = ILibLMS_Create(Chain); + if (MicroLMS != NULL) + { + printf("Starting MicroLMS.\r\n"); + ILibStartChain(Chain); + printf("Stopping MicroLMS.\r\n"); + } + else + { + printf("Unable to launch MicroLMS. Check that Intel ME is present, MEI driver installed and run this executable as administrator.\r\n"); + } + +#ifdef MEMORY_CHECK +#ifdef WIN32 + OutputHeading("Generating the final memory leak report\r\n"); + _CrtCheckMemory(); + _CrtDumpMemoryLeaks(); +#endif +#endif + +#ifdef _POSIX +#ifdef _DEBUG + //muntrace(); +#endif +#endif + +#ifdef _POSIX + exit(EXIT_SUCCESS); +#else + return 0; +#endif +} + diff --git a/MicroLMS/MicroLMS/resource.h b/MicroLMS/MicroLMS/resource.h new file mode 100644 index 0000000..6d31350 --- /dev/null +++ b/MicroLMS/MicroLMS/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by MeshMessenger.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MicroLMS/MicroLMS/stdafx.cpp b/MicroLMS/MicroLMS/stdafx.cpp new file mode 100644 index 0000000..09fe45b --- /dev/null +++ b/MicroLMS/MicroLMS/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// TinyMesh.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/MicroLMS/MicroLMS/stdafx.h b/MicroLMS/MicroLMS/stdafx.h new file mode 100644 index 0000000..b005a83 --- /dev/null +++ b/MicroLMS/MicroLMS/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/MicroLMS/MicroLMS/targetver.h b/MicroLMS/MicroLMS/targetver.h new file mode 100644 index 0000000..6fe8eb7 --- /dev/null +++ b/MicroLMS/MicroLMS/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + diff --git a/MicroLMS/core/utils.c b/MicroLMS/core/utils.c new file mode 100644 index 0000000..1c30a05 --- /dev/null +++ b/MicroLMS/core/utils.c @@ -0,0 +1,78 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif + +#if defined(WINSOCK2) + #include + #include + #include + #include +#elif defined(WINSOCK1) + #include + #include + #include + #include +#endif + +#include "utils.h" +#if defined(_MESHAGENT) +#include "meshcore.h" +#include "meshinfo.h" +#else +#define PB_SESSIONKEY 9 +#define PB_AESCRYPTO 8 +#endif + +#include +#include + +char* __fastcall util_tohex_lower(char* data, int len, char* out) +{ + char utils_HexTable2[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for (i = 0; i < len; i++) + { + *(p++) = utils_HexTable2[((unsigned char)data[i]) >> 4]; + *(p++) = utils_HexTable2[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} + +#ifdef WIN32 +BOOL GetWindowsDllPath(const char* dllName, wchar_t* out) +{ + char buf[MAX_PATH]; +#if _WIN64 + if (SHGetSpecialFolderPathA(NULL, buf, CSIDL_SYSTEM, FALSE) == FALSE) return FALSE; +#else + if (SHGetSpecialFolderPathA(NULL, buf, CSIDL_SYSTEMX86, FALSE) == FALSE) return FALSE; +#endif + + if(strcat_s(buf, MAX_PATH, dllName)!= 0) return FALSE; + int str_len = MultiByteToWideChar(CP_UTF8, 0, buf, -1, out, 0); + if(MultiByteToWideChar(CP_UTF8, 0, buf, -1, out, MAX_PATH) == 0) return FALSE; + + return TRUE; + +} +#endif + +// These are all missing MINCORE function that are re-implemented here. +#ifdef WIN32 +BOOL util_MoveFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName) { return MoveFileA(lpExistingFileName, lpNewFileName); } +BOOL util_CopyFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName, _In_ BOOL bFailIfExists) { return CopyFileA(lpExistingFileName, lpNewFileName, bFailIfExists); } +#endif + diff --git a/MicroLMS/core/utils.h b/MicroLMS/core/utils.h new file mode 100644 index 0000000..dd03029 --- /dev/null +++ b/MicroLMS/core/utils.h @@ -0,0 +1,43 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if !defined(__MeshUtils__) +#define __MeshUtils__ + +#include "../microstack/ILibParsers.h" + +#if !defined(WIN32) +#define __fastcall +#endif + +// Debugging features +#if defined(_DEBUG) + // Display & log + //char spareDebugMemory[4000]; + //int spareDebugLen; + //#define MSG(x) printf("%s",x);//mdb_addevent(x, (int)strlen(x)); + //#define MSG(...) spareDebugLen = snprintf(spareDebugMemory, 4000, __VA_ARGS__);printf("%s",spareDebugMemory);//mdb_addevent(spareDebugMemory, spareDebugLen); + + // Display only + #define MSG(...) printf(__VA_ARGS__); fflush(NULL) + #define DEBUGSTATEMENT(x) x +#else + #ifndef MSG + #define MSG(...) + #endif + #define DEBUGSTATEMENT(x) +#endif + +char* __fastcall util_tohex_lower(char* data, int len, char* out); + +#ifdef WIN32 +int __fastcall util_crc(unsigned char *buffer, int len, int initial_value); +BOOL util_MoveFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName); +BOOL util_CopyFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName, _In_ BOOL bFailIfExists); +BOOL GetWindowsDllPath(const char* dllName, wchar_t* out); +#endif + +#endif diff --git a/MicroLMS/heci/HECILinux.c b/MicroLMS/heci/HECILinux.c new file mode 100644 index 0000000..cffcfea --- /dev/null +++ b/MicroLMS/heci/HECILinux.c @@ -0,0 +1,434 @@ +/****************************************************************************** + * Intel Management Engine Interface (Intel MEI) Linux driver + * Intel MEI Interface Header + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Corporation. + * linux-mei@linux.intel.com + * http://www.intel.com + * + * BSD LICENSE + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HECILinux.h" +#include "../core/utils.h" + +/***************************************************************************** + * Intel Management Engine Interface + *****************************************************************************/ + +#ifdef _HECIDEBUG + #define mei_msg(_me, fmt, ARGS...) do { printf(fmt, ##ARGS); } while (0) + #define mei_err(_me, fmt, ARGS...) do { printf(fmt, ##ARGS); } while (0) +#else + #define mei_msg(_me, fmt, ARGS...) + #define mei_err(_me, fmt, ARGS...) +#endif + +static void mei_deinit(struct mei *cl) +{ + // mei_err(cl, "mei_deinit()\n"); + if (cl->initialized == false) return; + cl->initialized = false; + if (cl->fd != -1) close(cl->fd); + cl->fd = -1; + cl->buf_size = 0; + cl->prot_ver = 0; + sem_destroy(&(cl->Lock)); +} + +static bool mei_init(struct mei *me, const uuid_le *guid, unsigned char req_protocol_version, bool verbose) +{ + int result; + struct mei_client *cl; + struct mei_connect_client_data data; + + mei_deinit(me); + + me->verbose = verbose; + + // open me + me->fd = open("/dev/mei0", O_RDWR); + if (me->fd == -1) { + // mei_err(me, "Cannot establish a handle to the Intel MEI driver\n"); + goto err; + } + memcpy(&me->guid, guid, sizeof(*guid)); + memset(&data, 0, sizeof(data)); + me->initialized = true; + + memcpy(&data.in_client_uuid, &me->guid, sizeof(me->guid)); + result = ioctl(me->fd, IOCTL_MEI_CONNECT_CLIENT, &data); + if (result) { + mei_err(me, "IOCTL_MEI_CONNECT_CLIENT receive message. err=%d,%d\n", result, errno); + goto err; + } + cl = &data.out_client_properties; + //mei_msg(me, "max_message_length %d\n", cl->max_msg_length); + //mei_msg(me, "protocol_version %d\n", cl->protocol_version); + + if ((req_protocol_version > 0) && (cl->protocol_version != req_protocol_version)) { + mei_err(me, "Intel MEI protocol version not supported\n"); + goto err; + } + + me->buf_size = cl->max_msg_length; + me->prot_ver = cl->protocol_version; + sem_init(&(me->Lock), 0, 1); + + mei_msg(me, "mei init succ"); + return true; +err: + mei_deinit(me); + return false; +} + +static ssize_t mei_recv_msg(struct mei *me, unsigned char *buffer, ssize_t len, unsigned long timeout) +{ + ssize_t rc; + + mei_msg(me, "call read length = %zd\n", len); + rc = read(me->fd, buffer, len); + if (rc < 0) { + mei_err(me, "read failed with status %zd %s\n", rc, strerror(errno)); + mei_deinit(me); + } else { + mei_msg(me, "read succeeded with result %zd\n", rc); + } + return rc; +} + +static ssize_t mei_send_msg(struct mei *me, const unsigned char *buffer, ssize_t len, unsigned long timeout) +{ + struct timeval tv; + ssize_t written; + ssize_t rc; + fd_set set; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000000; + + mei_msg(me, "call write length = %zd, cmd=%d\n", len, (int)buffer[0]); + + sem_wait(&(me->Lock)); + + written = write(me->fd, buffer, len); + if (written < 0) { + rc = -errno; + mei_err(me, "write failed with status %zd %s\n", written, strerror(errno)); + goto out; + } + + FD_ZERO(&set); + FD_SET(me->fd, &set); + rc = select(me->fd + 1 , NULL, &set, NULL, &tv); + if (rc > 0 && FD_ISSET(me->fd, &set)) { + mei_msg(me, "write success\n"); + } else if (rc == 0) { + mei_err(me, "write failed on timeout with status 0, timeout = %ld, written=%ld, cmd=%d\n", timeout, written, (int)buffer[0]); + goto out; + } else { // rc < 0 + mei_err(me, "write failed on select with status %zd\n", rc); + goto out; + } + + rc = written; +out: + sem_post(&(me->Lock)); + mei_msg(me, "call write written = %zd\n", written); + if (rc < 0) mei_deinit(me); + return rc; +} + +/*************************************************************************** + * Intel Advanced Management Technology Host Interface + ***************************************************************************/ + +struct amt_host_if_msg_header { + struct amt_version version; + uint16_t _reserved; + uint32_t command; + uint32_t length; +} __attribute__((packed)); + +struct amt_host_if_resp_header { + struct amt_host_if_msg_header header; + uint32_t status; + unsigned char data[0]; +} __attribute__((packed)); + +const uuid_le MEI_IAMTHIF = {.b={0x28, 0x00, 0xf8, 0x12, 0xb7, 0xb4, 0x2d, 0x4b, 0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c}}; +const uuid_le MEI_LMEIF = {.b={0xdb, 0xa4, 0x33, 0x67, 0x76, 0x04, 0x7b, 0x4e, 0xb3, 0xaf, 0xbc, 0xfc, 0x29, 0xbe, 0xe7, 0xa7}}; + +#define AMT_HOST_IF_CODE_VERSIONS_REQUEST 0x0400001A +#define AMT_HOST_IF_CODE_VERSIONS_RESPONSE 0x0480001A + +const struct amt_host_if_msg_header CODE_VERSION_REQ = { + .version = {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, + ._reserved = 0, + .command = AMT_HOST_IF_CODE_VERSIONS_REQUEST, + .length = 0 +}; + + +static bool amt_host_if_init(struct amt_host_if *acmd, unsigned long send_timeout, bool verbose, int client) +{ + acmd->send_timeout = (send_timeout) ? send_timeout : 20000; + if (client == 0) { acmd->initialized = mei_init(&acmd->mei_cl, &MEI_IAMTHIF, 0, verbose); } + else if (client == 1) { acmd->initialized = mei_init(&acmd->mei_cl, &MEI_LMEIF, 0, verbose); } + return acmd->initialized; +} + +static void amt_host_if_deinit(struct amt_host_if *acmd) +{ + mei_deinit(&acmd->mei_cl); + acmd->initialized = false; +} + +static uint32_t amt_verify_code_versions(const struct amt_host_if_resp_header *resp) +{ + uint32_t status = AMT_STATUS_SUCCESS; + struct amt_code_versions *code_ver; + size_t code_ver_len; + uint32_t ver_type_cnt; + uint32_t len; + uint32_t i; + + code_ver = (struct amt_code_versions *)resp->data; + /* length - sizeof(status) */ + code_ver_len = resp->header.length - sizeof(uint32_t); + ver_type_cnt = code_ver_len - + sizeof(code_ver->bios) - + sizeof(code_ver->count); + if (code_ver->count != ver_type_cnt / sizeof(struct amt_version_type)) { + status = AMT_STATUS_INTERNAL_ERROR; + goto out; + } + + for (i = 0; i < code_ver->count; i++) { + len = code_ver->versions[i].description.length; + + if (len > AMT_UNICODE_STRING_LEN) { + status = AMT_STATUS_INTERNAL_ERROR; + goto out; + } + + len = code_ver->versions[i].version.length; + if (code_ver->versions[i].version.string[len] != '\0' || + len != strlen(code_ver->versions[i].version.string)) { + status = AMT_STATUS_INTERNAL_ERROR; + goto out; + } + } +out: + return status; +} + +static uint32_t amt_verify_response_header(uint32_t command, const struct amt_host_if_msg_header *resp_hdr, uint32_t response_size) +{ + if (response_size < sizeof(struct amt_host_if_resp_header)) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (response_size != (resp_hdr->length + + sizeof(struct amt_host_if_msg_header))) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (resp_hdr->command != command) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (resp_hdr->_reserved != 0) { + return AMT_STATUS_INTERNAL_ERROR; + } else if (resp_hdr->version.major != AMT_MAJOR_VERSION || + resp_hdr->version.minor < AMT_MINOR_VERSION) { + return AMT_STATUS_INTERNAL_ERROR; + } + return AMT_STATUS_SUCCESS; +} + +static uint32_t amt_host_if_call(struct amt_host_if *acmd, const unsigned char *command, ssize_t command_sz, uint8_t **read_buf, uint32_t rcmd, unsigned int expected_sz) +{ + uint32_t in_buf_sz; + uint32_t out_buf_sz; + ssize_t written; + uint32_t status; + struct amt_host_if_resp_header *msg_hdr; + + in_buf_sz = acmd->mei_cl.buf_size; + *read_buf = (uint8_t *)malloc(sizeof(uint8_t) * in_buf_sz); + if (*read_buf == NULL) return AMT_STATUS_SDK_RESOURCES; + memset(*read_buf, 0, in_buf_sz); + msg_hdr = (struct amt_host_if_resp_header *)*read_buf; + + written = mei_send_msg(&acmd->mei_cl, command, command_sz, acmd->send_timeout); + if (written != command_sz) + return AMT_STATUS_INTERNAL_ERROR; + + out_buf_sz = mei_recv_msg(&acmd->mei_cl, *read_buf, in_buf_sz, 2000); + if (out_buf_sz <= 0) + return AMT_STATUS_HOST_IF_EMPTY_RESPONSE; + + status = msg_hdr->status; + if (status != AMT_STATUS_SUCCESS) + return status; + + status = amt_verify_response_header(rcmd, &msg_hdr->header, out_buf_sz); + if (status != AMT_STATUS_SUCCESS) + return status; + + if (expected_sz && expected_sz != out_buf_sz) + return AMT_STATUS_INTERNAL_ERROR; + + return AMT_STATUS_SUCCESS; +} + + +static uint32_t amt_get_code_versions(struct amt_host_if *cmd, struct amt_code_versions *versions) +{ + struct amt_host_if_resp_header *response = NULL; + uint32_t status; + + status = amt_host_if_call(cmd, + (const unsigned char *)&CODE_VERSION_REQ, + sizeof(CODE_VERSION_REQ), + (uint8_t **)&response, + AMT_HOST_IF_CODE_VERSIONS_RESPONSE, 0); + + if (status != AMT_STATUS_SUCCESS) + goto out; + + status = amt_verify_code_versions(response); + if (status != AMT_STATUS_SUCCESS) + goto out; + + memcpy(versions, response->data, sizeof(struct amt_code_versions)); +out: + if (response != NULL) + free(response); + + return status; +} + +/************************** end of amt_host_if_command ***********************/ + +int MEI_globalSetup = 0; +struct MEImodule MEI_global; + +bool heci_Init(struct MEImodule* module, int client) +{ + if (module == NULL && client != 0) return false; + if (module == NULL) { module = &MEI_global; if (MEI_globalSetup == 1) return true; } + memset(module, 0 , sizeof(struct MEImodule)); + if (!amt_host_if_init(&(module->acmd), 5000, module->verbose, client)) return false; + if (module == &MEI_global) MEI_globalSetup = 1; + module->inited = true; + if (client == 0) module->status = amt_get_code_versions(&(module->acmd), &(module->ver)); + return true; +} + +void heci_Deinit(struct MEImodule* module) +{ + if (module == NULL) { module = &MEI_global; MEI_globalSetup = 0; } + amt_host_if_deinit(&(module->acmd)); + memset(module, 0, sizeof(struct MEImodule)); +} + +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + if (module == NULL) module = &MEI_global; + return mei_recv_msg(&(module->acmd.mei_cl), buffer, len, timeout); +} + +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + if (module == NULL) module = &MEI_global; + return mei_send_msg(&(module->acmd.mei_cl), buffer, len, timeout); +} + +unsigned int heci_GetBufferSize(struct MEImodule* module) +{ + if (module == NULL) module = &MEI_global; + if (module->inited) return module->acmd.mei_cl.buf_size; + return -1; +} + +unsigned char heci_GetProtocolVersion(struct MEImodule* module) +{ + if (module == NULL) module = &MEI_global; + if (module->inited) return module->acmd.mei_cl.prot_ver; + return 0; +} + +// Get the version of MEI from the last MEI init. +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version) +{ + version->major = AMT_MAJOR_VERSION; + version->minor = AMT_MINOR_VERSION; + return true; +} + +bool heci_IsInitialized(struct MEImodule* module) +{ + return module->inited; +} + diff --git a/MicroLMS/heci/HECILinux.h b/MicroLMS/heci/HECILinux.h new file mode 100644 index 0000000..9bde522 --- /dev/null +++ b/MicroLMS/heci/HECILinux.h @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (C) 2004-2008 Intel Corp. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of Intel Corp. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Intel Corp. OR THE CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *******************************************************************************/ + +#ifndef __HECI_LINUX_H__ +#define __HECI_LINUX_H__ + +#include "HECI_if.h" +#include "mei.h" +#include +#include + +#ifndef bool +#define bool int +#endif + +/*************************************************************************** + * Intel Advanced Management Technology ME Client + ***************************************************************************/ + +#define AMT_MAJOR_VERSION 1 +#define AMT_MINOR_VERSION 1 + +#define AMT_STATUS_SUCCESS 0x0 +#define AMT_STATUS_INTERNAL_ERROR 0x1 +#define AMT_STATUS_NOT_READY 0x2 +#define AMT_STATUS_INVALID_AMT_MODE 0x3 +#define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4 + +#define AMT_STATUS_HOST_IF_EMPTY_RESPONSE 0x4000 +#define AMT_STATUS_SDK_RESOURCES 0x1004 + + +#define AMT_BIOS_VERSION_LEN 65 +#define AMT_VERSIONS_NUMBER 50 +#define AMT_UNICODE_STRING_LEN 20 + +typedef enum +{ + PTHI_CLIENT, + LMS_CLIENT, + MKHI_CLIENT +} HECI_CLIENTS; + + +struct amt_unicode_string { + uint16_t length; + char string[AMT_UNICODE_STRING_LEN]; +} __attribute__((packed)); + +struct amt_version_type { + struct amt_unicode_string description; + struct amt_unicode_string version; +} __attribute__((packed)); + +struct amt_version { + uint8_t major; + uint8_t minor; +} __attribute__((packed)); + +struct amt_code_versions { + uint8_t bios[AMT_BIOS_VERSION_LEN]; + uint32_t count; + struct amt_version_type versions[AMT_VERSIONS_NUMBER]; +} __attribute__((packed)); + +struct mei { + uuid_le guid; + bool initialized; + bool verbose; + unsigned int buf_size; + unsigned char prot_ver; + int fd; + sem_t Lock; +}; + +struct amt_host_if { + struct mei mei_cl; + unsigned long send_timeout; + bool initialized; +}; + +struct MEImodule +{ + struct amt_code_versions ver; + struct amt_host_if acmd; + unsigned int i; + unsigned int status; + bool verbose; + bool inited; +}; + +bool heci_Init(struct MEImodule* module, int client); +void heci_Deinit(struct MEImodule* module); +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +unsigned int heci_GetBufferSize(struct MEImodule* module); +unsigned char heci_GetProtocolVersion(struct MEImodule* module); +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version); +bool heci_IsInitialized(struct MEImodule* module); + +#endif // __HECI_LINUX_H__ diff --git a/MicroLMS/heci/HECIWin.c b/MicroLMS/heci/HECIWin.c new file mode 100644 index 0000000..f885fa3 --- /dev/null +++ b/MicroLMS/heci/HECIWin.c @@ -0,0 +1,377 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#include +#include +#include +#include +#include "HECIWin.h" +#include "heci_if.h" + +#define false 0 +#define true 1 +#define HECI_MAX_LINE_LEN 300 + +DEFINE_GUID(GUID_DEVINTERFACE_HECI, 0xE2D1FF34, 0x3458, 0x49A9, 0x88, 0xDA, 0x8E, 0x69, 0x15, 0xCE, 0x9B, 0xE5); +DEFINE_GUID(HECI_PTHI_GUID , 0x12F80028, 0xB4B7, 0x4b2d, 0xAC, 0xA8, 0x46, 0xE0, 0xFF, 0x65, 0x81, 0x4c); +DEFINE_GUID(LMS_GUID , 0x6733a4db, 0x0476, 0x4e7b, 0xb3, 0xaf, 0xbc, 0xfc, 0x29, 0xbe, 0xe7, 0xa7); +DEFINE_GUID(MKHI_GUID , 0x8E6A6715, 0x9ABC, 0x4043, 0x88, 0xEF, 0x9E, 0x39, 0xC6, 0xF6, 0x3E, 0x0F); + +//VOID _displayHECIError(UINT32 errorCode,DWORD lastError); +//VOID _displayHECIData(UINT32 messageId); +int heci_doIoctl(struct MEImodule* module, DWORD code, void *inbuf, int inlen, void *outbuf, int outlen); + +struct MEImodule MEI_global; + +/***************************** public functions *****************************/ + +unsigned int heci_GetBufferSize(struct MEImodule* module) { if (module != NULL) return module->_bufSize; else return MEI_global._bufSize; } +unsigned char heci_GetProtocolVersion(struct MEImodule* module) { if (module != NULL) return module->_protocolVersion; else return MEI_global._protocolVersion; } +bool heci_IsInitialized(struct MEImodule* module) { if (module != NULL) return module->_initialized; else return MEI_global._initialized; } + +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version) +{ + if (module == NULL) module = &MEI_global; + + if (module->m_haveHeciVersion) + { + memcpy_s(version, sizeof(HECI_VERSION), &(module->m_heciVersion), sizeof(HECI_VERSION)); + return true; + } + + return false; +} + +bool heci_Init(struct MEImodule* module, HECI_CLIENTS client) +{ + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail = NULL; + HDEVINFO hDeviceInfo; + DWORD bufferSize; + SP_DEVICE_INTERFACE_DATA interfaceData; + LONG ii = 0; + int result; + HECI_CLIENT properties; + GUID guid; + + if (client == PTHI_CLIENT) guid = HECI_PTHI_GUID; + if (client == LMS_CLIENT) guid = LMS_GUID; + if (client == MKHI_CLIENT) guid = MKHI_GUID; + if (module == NULL) module = &MEI_global; + module->_verbose = false; + + if (module->_initialized) { + heci_Deinit(module); + } + + // Find all devices that have our interface + hDeviceInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_HECI, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hDeviceInfo == INVALID_HANDLE_VALUE) { + if (module->_verbose) { + //_displayHECIError(GET_CLASS_DEVS,GetLastError()); + } + return false; //GET_CLASS_DEVS; + } + + // Setup the interface data struct + interfaceData.cbSize = sizeof(interfaceData); + for (ii = 0; + SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, (LPGUID)&GUID_DEVINTERFACE_HECI, ii, &interfaceData); + ++ii) { + // Found our device instance + if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, NULL, 0, &bufferSize, NULL)) { + DWORD err = GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) { + if (module->_verbose) { + //_displayHECIError(GET_INTERFACE_DETAIL,err); + } + continue; + } + } + + // Allocate a big enough buffer to get detail data + deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(bufferSize); + if (deviceDetail == NULL) { + if (module->_verbose) { + //_displayHECIError(ALLOCATE_MEMORY_ERROR,0); + } + continue; + } + + // Setup the device interface struct + deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Try again to get the device interface detail info + if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, deviceDetail, bufferSize, NULL, NULL)) + { + /* + if (_verbose) + { + DWORD err = GetLastError(); + _displayHECIError(GET_INTERFACE_DETAIL,err); + } + */ + free(deviceDetail); + deviceDetail = NULL; + continue; + } + + break; + } + SetupDiDestroyDeviceInfoList(hDeviceInfo); + + if (deviceDetail == NULL) { + if (module->_verbose) { + //_displayHECIError(FIND_HECI_FAILURE,0); + } + return false; //FIND_HECI_FAILURE; + } + + module->_handle = CreateFile(deviceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + free(deviceDetail); + + if (module->_handle == INVALID_HANDLE_VALUE) { + if (module->_verbose) { + //_displayHECIError(CREATE_HECI_FILE_FAILURE,GetLastError()); + } + return false; //CREATE_HECI_FILE_FAILURE; + } + module->_initialized = true; + + result = heci_doIoctl(module, (DWORD)IOCTL_HECI_GET_VERSION, NULL, 0, &(module->m_heciVersion), sizeof(HECI_VERSION)); + if (result != sizeof(HECI_VERSION)) { + if (module->_verbose) { + //_displayHECIError(GET_HECI_DRIVER_VERSION_FAILURE,0); + } + heci_Deinit(module); + return false; //GET_HECI_DRIVER_VERSION_FAILURE; + } + module->m_haveHeciVersion = true; + + if (module->_verbose) { + //_displayHECIData(HECI_DRIVER_VERSION); + //_ftprintf(stdout,_T("%d.%d.%d.%d\n"), (m_heciVersion).major, (m_heciVersion).minor, (m_heciVersion).hotfix, (m_heciVersion).build); + } + + memset(&properties, 0, sizeof(properties)); + result = heci_doIoctl(module, (DWORD)IOCTL_HECI_CONNECT_CLIENT, (void*)(&guid), sizeof(GUID), &properties, sizeof(properties)); + if (result != sizeof(properties)) + { + if (module->_verbose) { + //_displayHECIError(HECI_CONNECT_TO_PTHI_CLIENT_FAILURE,0); + } + //Deinit(); + return false; //HECI_CONNECT_TO_PTHI_CLIENT_FAILURE; + } + module->_bufSize = properties.MaxMessageLength; + + return true; +} + +void heci_Deinit(struct MEImodule* module) +{ + if (module == NULL) module = &MEI_global; + if (module->_initialized == false) return; + module->_initialized = false; + module->_bufSize = 0; + + if (module->_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(module->_handle); + module->_handle = INVALID_HANDLE_VALUE; + } +} + +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + DWORD bytesRead = 0; + int res; + HANDLE h_event = NULL; + OVERLAPPED overlapped; + DWORD error; + DWORD eventRes; + + if (module == NULL) module = &MEI_global; + if ((h_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == 0) goto out; + overlapped.hEvent = h_event; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + + res = ReadFile(module->_handle, buffer, len, &bytesRead, &overlapped); + error = GetLastError(); + if ((0 == res) && (ERROR_IO_PENDING != error)) { + if (module->_verbose) { + //_displayHECIError(READ_FILE,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + + eventRes = WaitForSingleObject(h_event, timeout); + if (eventRes == WAIT_TIMEOUT) { + bytesRead = 0; + goto out; + } + + res = GetOverlappedResult(module->_handle, &overlapped, &bytesRead, true); + + if (res == 0) { + if (module->_verbose) { + //_displayHECIError(READ_FILE,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + +out: + if (h_event != NULL) CloseHandle(h_event); + if (bytesRead <= 0) heci_Deinit(module); + + return bytesRead; +} + +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout) // Timeout default is 2000 +{ + DWORD bytesWritten = 0; + int res; + HANDLE h_event = NULL; + OVERLAPPED overlapped; + DWORD lastError; + DWORD eventRes; + + if (module == NULL) module = &MEI_global; + if ((h_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == 0) goto out; + overlapped.hEvent = h_event; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + + res = WriteFile(module->_handle, buffer, len, &bytesWritten, &overlapped); + + lastError = GetLastError(); + if ((0 == res) && (ERROR_IO_PENDING !=lastError )) { + if (module->_verbose) { + //_displayHECIError(WRITE_FILE,GetLastError()); + } + bytesWritten = (DWORD)-1; + goto out; + } + + eventRes = WaitForSingleObject(h_event, timeout); + if (eventRes == WAIT_TIMEOUT) { + if (module->_verbose) { + //_displayHECIError(WRITE_FILE_TIME_OUT,0); + } + bytesWritten = 0; + goto out; + } + + res = GetOverlappedResult(module->_handle, &overlapped, &bytesWritten, false); + + if (res == 0) { + if (module->_verbose) { + //_displayHECIError(WRITE_FILE,GetLastError()); + } + bytesWritten = (DWORD)-1; + goto out; + } + +out: + if (h_event != NULL) CloseHandle(h_event); + if (bytesWritten <= 0) heci_Deinit(module); + + return bytesWritten; +} + +int heci_doIoctl(struct MEImodule* module, DWORD code, void *inbuf, int inlen, void *outbuf, int outlen) +{ + int res; + DWORD bytesRead = 0; + HANDLE h_event = NULL; + OVERLAPPED overlapped; + + if (module == NULL) module = &MEI_global; + if (!module->_initialized) return -1; + + if ((h_event = CreateEvent(NULL, FALSE, FALSE, NULL)) == 0) goto out; + overlapped.hEvent = h_event; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + + res = DeviceIoControl(module->_handle, code, inbuf, inlen, outbuf, outlen, &bytesRead, &overlapped); + + if ((0 == res) && (ERROR_IO_PENDING != GetLastError())) { + if (module->_verbose) { + //_displayHECIError(IOCTL_COMMAND,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + + WaitForSingleObject(h_event, INFINITE); + + res = GetOverlappedResult(module->_handle, &overlapped, &bytesRead, true); + if (res == 0) { + if (module->_verbose) { + //_displayHECIError(IOCTL_COMMAND,GetLastError()); + } + bytesRead = (DWORD)-1; + goto out; + } + +out: + if (h_event != NULL) CloseHandle(h_event); + if (bytesRead == (DWORD)-1) heci_Deinit(module); + + return bytesRead; +} + +TCHAR *_getErrMsg(DWORD err) +{ + static TCHAR buffer[1024]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + 0, + buffer, + sizeof(buffer) - 1, + 0); + + return buffer; +} + +/* +// Display a HECI error message +void _displayHECIError(UINT32 errorCode, DWORD lastError) +{ + TCHAR str[HECI_MAX_LINE_LEN]; + TCHAR *msg; + LoadString(GetModuleHandle(NULL), HECI_ERROR_MESSAGE, str, sizeof(str)/sizeof(TCHAR)); + _ftprintf(stderr, _T("%s"), str); + _ftprintf(stderr, _T("%s"), L" "); + LoadString(GetModuleHandle(NULL), errorCode , str, sizeof(str)/sizeof(TCHAR)); + if(0!= lastError) + { + msg = _getErrMsg(lastError); + _ftprintf(stderr, _T("%s (%d): %s\n"),str, lastError, msg); + } + else + { + _ftprintf(stderr, _T("%s\n"),str); + } +} + +// Display a HECI data message +void _displayHECIData(UINT32 messageId) +{ + TCHAR str[HECI_MAX_LINE_LEN]; + LoadString(GetModuleHandle(NULL), messageId , str, sizeof(str)/sizeof(TCHAR)); + _ftprintf(stdout,_T("%s"),str); + _ftprintf(stdout,_T("%s"),L" "); +} +*/ + +#endif diff --git a/MicroLMS/heci/HECIWin.h b/MicroLMS/heci/HECIWin.h new file mode 100644 index 0000000..457ffc9 --- /dev/null +++ b/MicroLMS/heci/HECIWin.h @@ -0,0 +1,49 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef __HECI_WIN_H__ +#define __HECI_WIN_H__ + +#include "HECI_if.h" + +#include +#include +#define bool int + +typedef enum +{ + PTHI_CLIENT, + LMS_CLIENT, + MKHI_CLIENT +} HECI_CLIENTS; + +struct MEImodule +{ + bool _initialized; + bool _verbose; + unsigned int _bufSize; + unsigned char _protocolVersion; + int _fd; + bool m_haveHeciVersion; + HECI_VERSION m_heciVersion; + HANDLE _handle; + OVERLAPPED overlapped; +}; + +bool heci_Init(struct MEImodule* module, HECI_CLIENTS client); +void heci_Deinit(struct MEImodule* module); +int heci_ReceiveMessage(struct MEImodule* module, unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +int heci_SendMessage(struct MEImodule* module, const unsigned char *buffer, int len, unsigned long timeout); // Timeout default is 2000 +unsigned int heci_GetBufferSize(struct MEImodule* module); +unsigned char heci_GetProtocolVersion(struct MEImodule* module); +bool heci_GetHeciVersion(struct MEImodule* module, HECI_VERSION *version); +bool heci_IsInitialized(struct MEImodule* module); + +#endif // __HECI_WIN_H__ + +#endif diff --git a/MicroLMS/heci/HECI_if.h b/MicroLMS/heci/HECI_if.h new file mode 100644 index 0000000..9b862e0 --- /dev/null +++ b/MicroLMS/heci/HECI_if.h @@ -0,0 +1,199 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef __HECI_INTRFACE_H__ +#define __HECI_INTRFACE_H__ + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef char CHAR; +typedef unsigned long ULONG; +typedef UINT32 AMT_STATUS; +typedef UINT32 AMT_BOOLEAN; + +typedef enum _HECI_STATUS { + HECI_STATUS_OK = 0x0, + HECI_STATUS_GENERAL_ERROR = 0x2000, + HECI_STATUS_LOCATE_DEVICE_ERROR, + HECI_STATUS_MEMORY_ACCESS_ERROR, + HECI_STATUS_WRITE_REGISTER_ERROR, + HECI_STATUS_MEMORY_ALLOCATION_ERROR, + HECI_STATUS_BUFFER_OVEREFLOW_ERROR, + HECI_STATUS_NOT_ENOUGH_MEMORY, + HECI_STATUS_MSG_TRANSMISSION_ERROR, + HECI_STATUS_VERSION_MISMATCH, + HECI_STATUS_UNEXPECTED_INTERRUPT_REASON, + HECI_STATUS_TIMEOUT_ERROR, + HECI_STATUS_UNEXPECTED_RESPONSE, + HECI_STATUS_UNKNOWN_MESSAGE, + HECI_STATUS_CANNOT_FOUND_HOST_CLIENT, + HECI_STATUS_CANNOT_FOUND_ME_CLIENT, + HECI_STATUS_CLIENT_ALREADY_CONNECTED, + HECI_STATUS_NO_FREE_CONNECTION, + HECI_STATUS_ILLEGAL_PARAMETER, + HECI_STATUS_FLOW_CONTROL_ERROR, + HECI_STATUS_NO_MESSAGE, + HECI_STATUS_BUFFER_TOO_LARGE, + HECI_STATUS_BUFFER_TOO_SMALL, + HECI_STATUS_BUFFER_NOT_EMPTY, + NUM_OF_HECI_STATUSES +} HECI_STATUS; + +#ifdef WIN32 + // Win32 code + + #define AMT_LOCAL_AGENT_STATUS_SUCCESS 0 + #define USAGE 1 + #define ERROR_MESSAGE 2 + #define VERSION_MESSAGE 3 + #define HECI_ERROR_MESSAGE 4 + #define UNKNOWN 5 + #define HECI_CONNECT_TO_FWU_CLIENT_FAILURE 6 + #define WRITE_FILE_TIME_OUT 7 + #define ME_FW_INFO 8 + #define ME_MODE 9 + #define IOCTL_COMMAND 10 + #define WRITE_FILE 11 + #define READ_FILE 12 + #define GET_CLASS_DEVS 13 + #define GET_INTERFACE_DETAIL 14 + #define ICH_VERSION 15 + #define FIND_HECI_FAILURE 16 + #define CREATE_HECI_FILE 17 + #define CREATE_HECI_FILE_FAILURE 17 + #define GET_HECI_DRIVER_VERSION_FAILURE 18 + #define LA_STATUS_INTERNAL_ERROR 19 + #define HECI_CONNECT_TO_PTHI_CLIENT_FAILURE 20 + #define LA_HECI_ERROR 21 + #define ALLOCATE_MEMORY_ERROR 22 + #define LA_HECI_NOT_INSTALLED_ERROR 23 + #define FW_BUFFER_IS_TO_SMALL 24 + #define SEND_DATA_TO_FW_FAILURE 25 + #define RECEIVE_DATA_FROM_FW_FAILURE 26 + #define GET_INFO_FROM_HECI_FAILURE 27 + #define MCH_VERSION 28 + #define OEM_VENDOR 29 + #define HECI_DRIVER_VERSION 30 + #define CODE_MAJOR_VERSION 31 + #define CODE_MINOR_VERSION 32 + #define CODE_HOTFIX_VERSION 33 + #define CODE_BUID_VERSION 34 + #define BIOS_VERSION 35 + #define AMT_CODE_VERSION 36 + #define AMT_MODE 37 + #define AMT_MODE_1 38 + #define IDS_STRING39 39 + #define AMT_MODE_2 39 + #define PROVISIONING_STATE 40 + #define STATE_PRE 41 + #define STATE_IN 42 + #define STATE_POST 43 + #define IDS_STRING44 44 + #define PARSE_KEYWORD_DISCOVERY_TEST 44 + #define DISCOVERY_PASS 45 + #define IDS_STRING46 46 + #define DISCOVERY_FAILED 46 + #define IDS_STRING47 47 + #define PARSE_KEYWORD_ACTIVATE 47 + #define PARSE_KEYWORD_OTP 48 + #define PARSE_KEYWORD_DNS 49 + #define PARSE_KEYWORD_VERBOSE 50 + #define INVALID_PARAM_INPUT 51 + #define USAGE_LOCAL_AGENT 52 + #define USAGE_OPTIONS 53 + #define USAGE_OPTIONS_OTP 54 + #define USAGE_OPTIONS_DNS 55 + #define USAGE_OPTIONS_DISCOVERY 56 + #define USAGE_OPTIONS_ACTIVATE 57 + #define USAGE_OPTIONS_VERBOSE 58 + #define WORD_ZTC 59 + #define WORD_ENABLED 60 + #define WORD_DISABLED 61 + #define WORD_PROVISIONING_TLS_MODE 62 + #define WORD_PKI 63 + #define WORD_PSK 64 + #define WORD_RNG_SEED_STATUS 65 + #define WORD_EXIST 66 + #define WORD_IN_PROGRESS 67 + #define WORD_NOT_EXIST 68 + #define WORD_AMT_CONFIG_ACTIVATE 69 + #define WORD_SUCCESS 70 + #define IDS_STRING71 71 + #define WORD_FAILURE 71 + #define WORD_NOT_READY 72 + #define IDS_STRING73 73 + #define HASH_ENTRY 73 + #define HECI_CONNECT_TO_WD_CLIENT_FAILURE 74 + #define CHANGE_TO_AMT_FAILURE 75 + #define IDS_STRING76 76 + #define WORD_CHANGE_TO_AMT 76 + #define FOUND 77 + #define CERT_HASHES_IN_FW 78 + #define NO_HANDLES_FOUND 79 + #define CERT_HASH 80 + #define FRIENDLY_NAME 81 + + #define FILE_DEVICE_HECI 0x8000 + #define HECI_IOCTL(index) CTL_CODE(FILE_DEVICE_HECI, index, METHOD_BUFFERED, FILE_READ_DATA) + #define IOCTL_HECI_GET_VERSION CTL_CODE(FILE_DEVICE_HECI, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + #define IOCTL_HECI_CONNECT_CLIENT CTL_CODE(FILE_DEVICE_HECI, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) + #define IOCTL_HECI_WD CTL_CODE(FILE_DEVICE_HECI, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS) +#endif + +#pragma pack(1) + +typedef struct _HECI_VERSION { + UINT8 major; + UINT8 minor; + UINT8 hotfix; + UINT16 build; +} HECI_VERSION; + +typedef struct _HECI_CLIENT { + UINT32 MaxMessageLength; + UINT8 ProtocolVersion; +} HECI_CLIENT; + +typedef union _MEFWCAPS_SKU +{ + UINT32 Data; + struct { + UINT32 Reserved :1; //Legacy + UINT32 Qst :1; //QST + UINT32 Asf :1; //ASF2 + UINT32 Amt :1; //AMT Professional + UINT32 AmtFund :1; //AMT Fundamental + UINT32 Tpm :1; //TPM + UINT32 Dt :1; + UINT32 Fps :1; //Fingerprint Sensor + UINT32 HomeIT :1; //Home IT + UINT32 Mctp :1; //MCTP + UINT32 WoX :1; + UINT32 PmcPatch :1; //PMC Patch + UINT32 Ve :1; //VE + UINT32 Tdt :1; //Theft Deterrent Technology + UINT32 Corp :1; //Corporate + UINT32 Reserved2 :17; + } Fields; +} MEFWCAPS_SKU; + +typedef enum _MEFWCAPS_MANAGEABILITY_SUPP +{ + MEFWCAPS_MANAGEABILITY_SUPP_NONE = 0, + MEFWCAPS_MANAGEABILITY_SUPP_AMT, + MEFWCAPS_MANAGEABILITY_SUPP_ASF, + MEFWCAPS_MANAGEABILITY_SUPP_CP +} MEFWCAPS_MANAGEABILITY_SUPP; + + +#pragma pack() + +#endif // __HECI_INTRFACE_H__ + +#endif diff --git a/MicroLMS/heci/LMEConnection.c b/MicroLMS/heci/LMEConnection.c new file mode 100644 index 0000000..1bd4129 --- /dev/null +++ b/MicroLMS/heci/LMEConnection.c @@ -0,0 +1,554 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "LMEConnection.h" +#include "LMS_if.h" +#include "../microstack/ILibParsers.h" + +#ifdef _POSIX +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#define MEI_IO_TIMEOUT 1000 + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +const unsigned int LME_RX_WINDOW_SIZE = 4096; +int LME_sendMessage(struct LMEConnection* module, unsigned char *buffer, int len); +void LME_doRX(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead); +void LME_apfGlobalRequest(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, int *status); +void LME_Deinit(struct LMEConnection* module); + +bool LME_IsInitialized(struct LMEConnection* module) +{ + // Lock il(_initLock); + bool ret = (bool)(module->initState == INIT_STATE_CONNECTED); + return ret; +} + +#ifdef WIN32 +DWORD WINAPI LME_Thread(void* obj) +#else +void LME_Thread(void* obj) +#endif +{ +#ifdef WIN32 + HECI_VERSION version; +#endif + int len = 0; + int bufsize = 0; + unsigned char* data = NULL; + struct LMEConnection* module = (struct LMEConnection*)obj; + + //printf("LMS THREAD START\r\n"); + + do { + bufsize = heci_GetBufferSize(&(module->mei)); + if ((data = (unsigned char*)malloc(bufsize)) == NULL) ILIBCRITICALEXIT(254); + + do { + len = heci_ReceiveMessage(&(module->mei), data, bufsize, 0xFFFFFFFF); + //printf("LMS THREAD READ LEN=%d\r\n", len); + if (len > 0 && data != NULL) LME_doRX(module, data, len); + } while (len >= 0); + + module->cb(module, module->cbParam, NULL, 0); + module->initState = INIT_STATE_DISCONNECTED; + free(data); + + // printf("LMS TRYING RECONNECT\r\n"); + + while (module->exit == 0 && module->initState == INIT_STATE_DISCONNECTED) + { + // Setup the MEI interface with the LME GUID + #ifdef WIN32 + if (heci_Init(&(module->mei), LMS_CLIENT) == TRUE && heci_GetHeciVersion(&(module->mei), &version) == TRUE && version.major >= LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_CONNECTED; } + #else + if (heci_Init(&(module->mei), 1) == TRUE && heci_GetProtocolVersion(&(module->mei)) >= LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_CONNECTED; } + #endif + + if (module->exit == 0 && module->initState == INIT_STATE_DISCONNECTED) + { + // printf("LMS THREAD SLEEP\r\n"); +#ifdef WIN32 + Sleep(2000); +#else + sleep(2); +#endif + } + //if (module->initState == INIT_STATE_CONNECTED) printf("LMS THREAD RECONNECT\r\n"); + } + } + while (module->exit == 0); + + // printf("LMS THREAD QUIT\r\n"); + module->exit = 2; + +#ifdef WIN32 + return 0; +#endif +} + +// Setup the LME connection +bool LME_Init(struct LMEConnection* module, MEICallback cb, void *param) +{ +#ifdef WIN32 + HECI_VERSION version; +#endif + + // Setup the state object + memset(module, 0, sizeof(struct LMEConnection)); + module->initState = INIT_STATE_DISCONNECTED; + module->cb = cb; + module->cbParam = param; + + // Setup the MEI interface with the LME GUID +#ifdef WIN32 + if (heci_Init(&(module->mei), LMS_CLIENT) == FALSE || heci_GetHeciVersion(&(module->mei), &version) == FALSE || version.major < LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_DISCONNECTED; return FALSE; } +#else + if (heci_Init(&(module->mei), 1) == FALSE || heci_GetProtocolVersion(&(module->mei)) < LMS_PROTOCOL_VERSION) { module->initState = INIT_STATE_DISCONNECTED; return FALSE; } +#endif + + module->initState = INIT_STATE_CONNECTED; + if ((module->txBuffer = (unsigned char*)malloc(LME_GetMeiBufferSize(module))) == NULL) ILIBCRITICALEXIT(254); + + // Create the thread that will read the MEI/LME stream + ILibSpawnNormalThread((voidfp)(&LME_Thread), module); + + return TRUE; +} + +// Disconnect the LME connection +void LME_Deinit(struct LMEConnection* module) +{ + //printf("LME_Deinit()\r\n"); + if (module == NULL) return; + if (module->initState == INIT_STATE_CONNECTED) { heci_Deinit(&(module->mei)); } + module->initState = INIT_STATE_DISCONNECTED; +} + +// Exit LME +void LME_Exit(struct LMEConnection* module) +{ + int l = 0; + + //printf("LME_Exit()\r\n"); + if (module == NULL) return; + if (module->exit == 0) module->exit = 1; + LME_Deinit(module); +#ifdef WIN32 + while (module->exit != 2 && l < 40) { Sleep(100); l++; } +#else + /* + while (module->exit != 2 && l < 4) + { + printf("LME_Holding %d\r\n", l); + Sleep(1); + l++; + } + */ +#endif + if (module->txBuffer != NULL && l < 40) { free(module->txBuffer); module->txBuffer = NULL; } +} + +// Send the APF disconnect message to the MEI +bool LME_Disconnect(struct LMEConnection* module, APF_DISCONNECT_REASON_CODE reasonCode) +{ + unsigned char buf[sizeof(APF_DISCONNECT_MESSAGE)]; + APF_DISCONNECT_MESSAGE *disconnectMessage = (APF_DISCONNECT_MESSAGE *)buf; + memset(disconnectMessage, 0, sizeof(buf)); + disconnectMessage->MessageType = APF_DISCONNECT; + disconnectMessage->ReasonCode = htonl(reasonCode); + return (LME_sendMessage(module, buf, sizeof(buf)) == sizeof(buf)); +} + +// Send the AFP service accept message to the MEI +bool LME_ServiceAccept(struct LMEConnection* module, char* serviceName) +{ + int len; + int res; + int servicenamelen = (int)strnlen_s(serviceName, _MAX_PATH); + unsigned char *buf; + unsigned char *pCurrent; + + if (!LME_IsInitialized(module)) return FALSE; + if ((buf = (unsigned char*)malloc(sizeof(APF_SERVICE_ACCEPT_MESSAGE) + servicenamelen)) == NULL) ILIBCRITICALEXIT(254); + pCurrent = buf; + + *pCurrent = APF_SERVICE_ACCEPT; + ++pCurrent; + *((unsigned int *)pCurrent) = htonl(servicenamelen); + pCurrent += 4; + + memcpy_s(pCurrent, sizeof(APF_SERVICE_ACCEPT_MESSAGE) + servicenamelen, serviceName, servicenamelen); + pCurrent += servicenamelen; + + len = (int)(pCurrent - buf); + res = LME_sendMessage(module, buf, len); + free(buf); + + return (res == len); +} + +bool LME_ProtocolVersion(struct LMEConnection* module, unsigned int majorversion, unsigned int minorversion, unsigned int triggerreason) +{ + APF_PROTOCOL_VERSION_MESSAGE protVersion; + memset(&protVersion, 0, sizeof(protVersion)); + protVersion.MessageType = APF_PROTOCOLVERSION; + protVersion.MajorVersion = htonl(majorversion); + protVersion.MinorVersion = htonl(minorversion); + protVersion.TriggerReason = htonl(triggerreason); + return (LME_sendMessage(module, (unsigned char *)&protVersion, sizeof(protVersion)) == sizeof(protVersion)); +} + +bool LME_TcpForwardReplySuccess(struct LMEConnection* module, unsigned int port) +{ + APF_TCP_FORWARD_REPLY_MESSAGE message; + memset(&message, 0, sizeof(message)); + message.MessageType = APF_REQUEST_SUCCESS; + message.PortBound = htonl(port); + return (LME_sendMessage(module, (unsigned char *)&message, sizeof(message)) == sizeof(message)); +} + +bool LME_SendShortMessage(struct LMEConnection* module, unsigned char buf) +{ + return (LME_sendMessage(module, &buf, sizeof(buf)) == sizeof(buf)); +} +#define pCurrentOffset ((int)(pCurrent - buf)) +bool LME_ChannelOpenForwardedRequest(struct LMEConnection* module, unsigned int senderChannel, char* connectedIP, unsigned int connectedPort, char* originatorIP, unsigned int originatorPort) +{ + int res; + int connectedIPlen = (int)strnlen_s(connectedIP, _MAX_PATH); + int originatorIPlen = (int)strnlen_s(originatorIP, _MAX_PATH); + unsigned char *buf; + unsigned char *pCurrent; + int mallocSize = 5 + APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED) + 16 + connectedIPlen + 8 + originatorIPlen + 4; + if (!LME_IsInitialized(module)) return FALSE; + + if ((buf = (unsigned char*)malloc(mallocSize)) == NULL) ILIBCRITICALEXIT(254); + pCurrent = buf; + + if (strnlen_s(originatorIP, _MAX_PATH) > 63) { free(buf); return FALSE; } + *pCurrent = APF_CHANNEL_OPEN; + ++pCurrent; + + *((unsigned int *)pCurrent) = htonl(APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED)); + pCurrent += sizeof(unsigned int); + + memcpy_s(pCurrent, mallocSize - pCurrentOffset, APF_OPEN_CHANNEL_REQUEST_FORWARDED, APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED)); + pCurrent += APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_FORWARDED); + + *((unsigned int *)pCurrent) = htonl(senderChannel); + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = htonl(LME_RX_WINDOW_SIZE); + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = 0xFFFFFFFF; + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = htonl(connectedIPlen); + pCurrent += sizeof(unsigned int); + + memcpy_s(pCurrent, mallocSize - pCurrentOffset, connectedIP, connectedIPlen); + pCurrent += connectedIPlen; + + *((unsigned int *)pCurrent) = htonl(connectedPort); + pCurrent += sizeof(unsigned int); + + *((unsigned int *)pCurrent) = htonl((unsigned int)originatorIPlen); + pCurrent += sizeof(unsigned int); + + memcpy_s(pCurrent, mallocSize - pCurrentOffset, originatorIP, originatorIPlen); + pCurrent += originatorIPlen; + + *((unsigned int *)pCurrent) = htonl(originatorPort); + pCurrent += sizeof(unsigned int); + + res = LME_sendMessage(module, buf, (int)(pCurrent - buf)); + free(buf); + + return (res == pCurrent - buf); +} + +bool LME_ChannelOpenReplySuccess(struct LMEConnection* module, unsigned int recipientChannel, unsigned int senderChannel) +{ + APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE message; + message.MessageType = APF_CHANNEL_OPEN_CONFIRMATION; + message.RecipientChannel = htonl(recipientChannel); + message.SenderChannel = htonl(senderChannel); + message.InitialWindowSize = htonl(LME_RX_WINDOW_SIZE); + message.Reserved = 0xFFFFFFFF; + return (LME_sendMessage(module, (unsigned char*)&message, sizeof(message)) == sizeof(message)); +} + +bool LME_ChannelOpenReplyFailure(struct LMEConnection* module, unsigned int recipientChannel, unsigned int reason) +{ + APF_CHANNEL_OPEN_FAILURE_MESSAGE message; + message.MessageType = APF_CHANNEL_OPEN_FAILURE; + message.RecipientChannel = htonl(recipientChannel); + message.ReasonCode = htonl(reason); + message.Reserved = 0x00000000; + message.Reserved2 = 0x00000000; + return (LME_sendMessage(module, (unsigned char*)&message, sizeof(message)) == sizeof(message)); +} + +bool LME_ChannelClose(struct LMEConnection* module, unsigned int recipientChannel, unsigned int senderChannel ) +{ + APF_CHANNEL_CLOSE_MESSAGE message; + UNREFERENCED_PARAMETER( senderChannel ); + + message.MessageType = APF_CHANNEL_CLOSE; + message.RecipientChannel = htonl(recipientChannel); + return (LME_sendMessage(module, (unsigned char*)&message, sizeof(message)) == sizeof(message)); +} + +int LME_ChannelData(struct LMEConnection* module, unsigned int recipientChannel, unsigned int len, unsigned char *buffer) +{ + APF_CHANNEL_DATA_MESSAGE *message; + if (len > (LME_GetMeiBufferSize(module) - sizeof(APF_CHANNEL_DATA_MESSAGE)) || module->txBuffer == NULL) return -1; + message = (APF_CHANNEL_DATA_MESSAGE*)module->txBuffer; + message->MessageType = APF_CHANNEL_DATA; + message->RecipientChannel = htonl(recipientChannel); + message->DataLength = htonl(len); + memcpy_s(module->txBuffer + sizeof(APF_CHANNEL_DATA_MESSAGE), LME_GetMeiBufferSize(module) - sizeof(APF_CHANNEL_DATA_MESSAGE), buffer, len); + return LME_sendMessage(module, (unsigned char *)message, sizeof(APF_CHANNEL_DATA_MESSAGE) + len) - sizeof(APF_CHANNEL_DATA_MESSAGE); +} + +bool LME_ChannelWindowAdjust(struct LMEConnection* module, unsigned int recipientChannel, unsigned int len) +{ + APF_WINDOW_ADJUST_MESSAGE message; + message.MessageType = APF_CHANNEL_WINDOW_ADJUST; + message.RecipientChannel = htonl(recipientChannel); + message.BytesToAdd = htonl(len); + return (LME_sendMessage(module, (unsigned char *)&message, sizeof(message)) == sizeof(message)); +} + +int LME_sendMessage(struct LMEConnection* module, unsigned char *buffer, int len) +{ + int result; + if (!LME_IsInitialized(module)) { return -1; } + result = heci_SendMessage(&(module->mei), buffer, len, MEI_IO_TIMEOUT); + if (result < 0) LME_Deinit(module); + return result; +} + +bool LME_checkMinMsgSize(unsigned char *buf, unsigned int bytesRead) +{ + switch (buf[0]) { + case APF_DISCONNECT: + if (bytesRead < sizeof(APF_DISCONNECT_MESSAGE)) { return FALSE; } + break; + case APF_SERVICE_REQUEST: + if (bytesRead < sizeof(APF_SERVICE_REQUEST_MESSAGE)) { return FALSE; } + if (bytesRead < (sizeof(APF_SERVICE_REQUEST_MESSAGE) + ntohl(((APF_SERVICE_REQUEST_MESSAGE *)buf)->ServiceNameLength))) { return FALSE; } + break; + case APF_USERAUTH_REQUEST: + if (bytesRead < (3 * sizeof(unsigned int))) { return FALSE; } + break; + case APF_GLOBAL_REQUEST: + if (bytesRead < (sizeof(APF_GENERIC_HEADER) + sizeof(UINT8))) { return FALSE; } + if (bytesRead < (sizeof(APF_GENERIC_HEADER) + sizeof(UINT8) + ntohl(((APF_GENERIC_HEADER *)buf)->StringLength))) { return FALSE; } + break; + case APF_CHANNEL_OPEN: + if (bytesRead < sizeof(APF_GENERIC_HEADER)) { return FALSE; } + if (bytesRead < (sizeof(APF_GENERIC_HEADER) + ntohl(((APF_GENERIC_HEADER *)buf)->StringLength))) { return FALSE; } + break; + case APF_CHANNEL_OPEN_CONFIRMATION: + if (bytesRead < sizeof(APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE)) { return FALSE; } + break; + case APF_CHANNEL_OPEN_FAILURE: + if (bytesRead < sizeof(APF_CHANNEL_OPEN_FAILURE_MESSAGE)) { return FALSE; } + break; + case APF_CHANNEL_CLOSE: + if (bytesRead < sizeof(APF_CHANNEL_CLOSE_MESSAGE)) { return FALSE; } + break; + case APF_CHANNEL_DATA: + if (bytesRead < sizeof(APF_CHANNEL_DATA_MESSAGE)) { return FALSE; } + if (bytesRead < (sizeof(APF_CHANNEL_DATA_MESSAGE) + ntohl(((APF_CHANNEL_DATA_MESSAGE *)buf)->DataLength))) { return FALSE; } + break; + case APF_CHANNEL_WINDOW_ADJUST: + if (bytesRead < sizeof(APF_WINDOW_ADJUST_MESSAGE)) { return FALSE; } + break; + case APF_PROTOCOLVERSION: + if (bytesRead < sizeof(APF_PROTOCOL_VERSION_MESSAGE)) { return FALSE; } + break; + default: + return FALSE; + } + return TRUE; +} + +void LME_doRX(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead) +{ + if (bytesRead == 0) return; + if (!LME_checkMinMsgSize(rxBuffer, bytesRead)) { LME_Deinit(module); return; } + module->cb(module, module->cbParam, rxBuffer, bytesRead); +} + +/* +void LME_apfChannelOpen(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, int *status) +{ + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + if (_strnicmp((char *)pHeader->String, APF_OPEN_CHANNEL_REQUEST_DIRECT, APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT)) == 0) + { + unsigned int senderChannel = 0; + + LME_apfChannelOpenDirect(module, rxBuffer, bytesRead, &senderChannel, status); + if (LME_IsInitialized(module) && (*status == 1)) { + if (plugin.retry(rxBuffer, bytesRead) != LMS_DROPPED) { LME_apfChannelOpenDirect(module, rxBuffer, bytesRead, NULL, status); } + } + + if (LME_IsInitialized(module) && (*status == 1)) { + LME_ChannelOpenReplyFailure(module, senderChannel, OPEN_FAILURE_REASON_CONNECT_FAILED); + } + } +} + +void LME_apfChannelOpenDirect(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, unsigned int *senderChannel, int *status) +{ + unsigned char *pCurrent; + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + if (bytesRead < sizeof(APF_GENERIC_HEADER) + + ntohl(pHeader->StringLength) + + 7 + (5 * sizeof(unsigned int))) { + ILIBMESSAGE("apfChannelOpenDirect: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + pCurrent = rxBuffer + sizeof(APF_GENERIC_HEADER) + + APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT); + + LMEChannelOpenRequestMessage channelOpenRequest; + channelOpenRequest.ChannelType = APF_CHANNEL_DIRECT; + + channelOpenRequest.SenderChannel = ntohl(*((unsigned int *)pCurrent)); + if (senderChannel) { + *senderChannel = channelOpenRequest.SenderChannel; + } + pCurrent += sizeof(unsigned int); + channelOpenRequest.InitialWindow = ntohl(*((unsigned int *)pCurrent)); + pCurrent += 2 * sizeof(unsigned int); + + unsigned int len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + channelOpenRequest.Address.append((char *)pCurrent, len); + pCurrent += len; + channelOpenRequest.Port = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + module->_cb(module, module->_cbParam, &channelOpenRequest, sizeof(channelOpenRequest), status); +} +*/ + +/* +void LME_apfUserAuthRequest(struct LMEConnection* module, unsigned char *rxBuffer, unsigned int bytesRead, int *status) +{ + unsigned char *pCurrent = rxBuffer; + + ++pCurrent; + + LMEUserAuthRequestMessage userAuthRequest; + + unsigned int len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest1: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + userAuthRequest.Username.append((char *)pCurrent, len); + pCurrent += len; + + if ((unsigned int)(bytesRead - (pCurrent - rxBuffer)) < sizeof(unsigned int)) { + ILIBMESSAGE("_apfUserAuthRequest2: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest3: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + userAuthRequest.ServiceName.append((char *)pCurrent, len); + pCurrent += len; + + if ((unsigned int)(bytesRead - (pCurrent - rxBuffer)) < sizeof(unsigned int)) { + ILIBMESSAGE("_apfUserAuthRequest4: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest5: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + userAuthRequest.MethodName.append((char *)pCurrent, len); + pCurrent += len; + + if (_strnicmp(userAuthRequest.MethodName.c_str(), APF_AUTH_PASSWORD, + userAuthRequest.MethodName.size()) == 0) { + + if ((unsigned int)(bytesRead - (pCurrent - rxBuffer)) < sizeof(unsigned int) + 1) { + ILIBMESSAGE("_apfUserAuthRequest6: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + ++pCurrent; + + len = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + + if ((bytesRead - (pCurrent - rxBuffer)) < len) { + ILIBMESSAGE("_apfUserAuthRequest7: Error receiving data from MEI\n"); + LME_Deinit(module); + return; + } + + AuthPasswordData authData; + authData.Password.append((char *)pCurrent, len); + pCurrent += len; + + userAuthRequest.MethodData = &authData; + } + + module->_cb(module, module->_cbParam, &userAuthRequest, sizeof(userAuthRequest), status); +} +*/ + +unsigned int LME_GetMeiBufferSize(struct LMEConnection* module) +{ + return heci_GetBufferSize(&(module->mei)); +} + +#endif diff --git a/MicroLMS/heci/LMEConnection.h b/MicroLMS/heci/LMEConnection.h new file mode 100644 index 0000000..9f51466 --- /dev/null +++ b/MicroLMS/heci/LMEConnection.h @@ -0,0 +1,185 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef __LME_CONNECTION_H__ +#define __LME_CONNECTION_H__ + +#ifdef WIN32 +#include "HECIWin.h" +#else +#include "HECILinux.h" +#endif + +#include "LMS_if.h" + +enum INIT_STATES { + INIT_STATE_DISCONNECTED = 0, + INIT_STATE_CONNECTING, + INIT_STATE_CONNECTED +}; + +struct LMEConnection; + +typedef void (*MEICallback)(struct LMEConnection* module, void *param, void *buffer, unsigned int len); + +struct LMEConnection +{ + unsigned char reqID; + unsigned char *txBuffer; + MEICallback cb; + void* cbParam; + enum INIT_STATES initState; + unsigned char protocolVer; + struct MEImodule mei; + unsigned char exit; +}; + +struct LMEDisconnectMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_DISCONNECT_REASON_CODE ReasonCode; +}; + +struct LMEServiceRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + char* ServiceName; +}; + +typedef enum APF_REQUEST_ENUM { + TCP_FORWARD_REQUEST, + TCP_FORWARD_CANCEL_REQUEST, + UDP_SEND_TO +} APF_REQUEST_TYPE; + +struct LMEGlobalRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; +}; + +struct LMEProtocolVersionMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int MajorVersion; + unsigned int MinorVersion; + APF_TRIGGER_REASON TriggerReason; +}; + +struct LMEUserAuthRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + char* Username; + char* ServiceName; + char* MethodName; + char* MethodData; +}; + +struct LMETcpForwardRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; + char* Address; + unsigned int Port; +}; + +struct LMETcpForwardCancelRequestMessage { + + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; + char* Address; + unsigned int Port; +}; + +struct LMEUdpSendToMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_REQUEST_TYPE RequestType; + char* Address; + unsigned int Port; + unsigned int DataLength; + unsigned char *Data; +}; + +typedef enum APF_CHANNEL_ENUM { + APF_CHANNEL_FORWARDED, + APF_CHANNEL_DIRECT +} APF_CHANNEL_TYPE; + +struct LMEChannelOpenRequestMessage +{ + APF_MESSAGE_TYPE MessageType; + APF_CHANNEL_TYPE ChannelType; + unsigned int SenderChannel; + unsigned int InitialWindow; + char* Address; + unsigned int Port; +}; + +struct LMEChannelOpenReplySuccessMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + unsigned int SenderChannel; + unsigned int InitialWindow; +}; + +struct LMEChannelOpenReplyFailureMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + OPEN_FAILURE_REASON ReasonCode; +}; + +struct LMEChannelCloseMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; +}; + +struct LMEChannelDataMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + unsigned int DataLength; + unsigned char *Data; +}; + +struct LMEChannelWindowAdjustMessage +{ + APF_MESSAGE_TYPE MessageType; + unsigned int RecipientChannel; + unsigned int BytesToAdd; +}; + +bool LME_Init(struct LMEConnection* module, MEICallback cb, void *param); +void LME_Deinit(struct LMEConnection* module); +bool LME_IsInitialized(struct LMEConnection* module); +bool LME_Disconnect(struct LMEConnection* module, APF_DISCONNECT_REASON_CODE reasonCode); +bool LME_ServiceAccept(struct LMEConnection* module, char* serviceName); +bool LME_ProtocolVersion(struct LMEConnection* module, unsigned int majorversion, unsigned int minorversion, unsigned int triggerreason); +bool LME_TcpForwardReplySuccess(struct LMEConnection* module, unsigned int port); +bool LME_SendShortMessage(struct LMEConnection* module, unsigned char buf); +bool LME_ChannelOpenForwardedRequest(struct LMEConnection* module, unsigned int sender, char* connectedIP, unsigned int connectedPort, char* originatorIP, unsigned int originatorPort); +bool LME_ChannelOpenReplySuccess(struct LMEConnection* module, unsigned int recipient, unsigned int sender); +bool LME_ChannelOpenReplyFailure(struct LMEConnection* module, unsigned int recipient, unsigned int reason); +bool LME_ChannelClose(struct LMEConnection* module, unsigned int recipient, unsigned int sender); +int LME_ChannelData(struct LMEConnection* module, unsigned int recipient, unsigned int len, unsigned char *buffer); +bool LME_ChannelWindowAdjust(struct LMEConnection* module, unsigned int recipient, unsigned int len); +void LME_Deinit(struct LMEConnection* module); +void LME_Exit(struct LMEConnection* module); +unsigned int LME_GetMeiBufferSize(struct LMEConnection* module); + +#define LME_UserAuthSuccess(module) LME_SendShortMessage(module, APF_USERAUTH_SUCCESS) +#define LME_TcpForwardReplyFailure(module) LME_SendShortMessage(module, APF_REQUEST_FAILURE) +#define LME_TcpForwardCancelReplySuccess(module) LME_SendShortMessage(module, APF_REQUEST_SUCCESS) +#define LME_TcpForwardCancelReplyFailure(module) LME_SendShortMessage(module, APF_REQUEST_FAILURE) + +#endif + +#endif diff --git a/MicroLMS/heci/LMS_if.h b/MicroLMS/heci/LMS_if.h new file mode 100644 index 0000000..a3fbd2c --- /dev/null +++ b/MicroLMS/heci/LMS_if.h @@ -0,0 +1,173 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef _LMS_IF_H_ +#define _LMS_IF_H_ + +#include "LMS_if_constants.h" + +#pragma pack(1) + +typedef struct { + unsigned char MessageType; +} APF_MESSAGE_HEADER; + + +/** + * APF_GENERIC_HEADER - generic request header (note that its not complete header per protocol (missing WantReply) + * + * @MessageType: + * @RequestStringLength: length of the string identifies the request + * @RequestString: the string that identifies the request + **/ + +typedef struct { + unsigned char MessageType; + unsigned int StringLength; + unsigned char String[0]; +} APF_GENERIC_HEADER; + +/** + * TCP forward reply message + * @MessageType - Protocol's Major version + * @PortBound - the TCP port was bound on the server + **/ +typedef struct { + unsigned char MessageType; + unsigned int PortBound; +} APF_TCP_FORWARD_REPLY_MESSAGE; + +/** + * response to ChannelOpen when channel open succeed + * @MessageType - APF_CHANNEL_OPEN_CONFIRMATION + * @RecipientChannel - channel number given in the open request + * @SenderChannel - channel number assigned by the sender + * @InitialWindowSize - Number of bytes in the window + * @Reserved - Reserved + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int SenderChannel; + unsigned int InitialWindowSize; + unsigned int Reserved; +} APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE; + +/** + * response to ChannelOpen when a channel open failed + * @MessageType - APF_CHANNEL_OPEN_FAILURE + * @RecipientChannel - channel number given in the open request + * @ReasonCode - code for the reason channel could not be open + * @Reserved - Reserved + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int ReasonCode; + unsigned int Reserved; + unsigned int Reserved2; +} APF_CHANNEL_OPEN_FAILURE_MESSAGE; + +/** + * close channel message + * @MessageType - APF_CHANNEL_CLOSE + * @RecipientChannel - channel number given in the open request + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; +} APF_CHANNEL_CLOSE_MESSAGE; + +/** + * used to send/receive data. + * @MessageType - APF_CHANNEL_DATA + * @RecipientChannel - channel number given in the open request + * @Length - Length of the data in the message + * @Data - The data in the message + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int DataLength; + // unsigned char Data[0]; +} APF_CHANNEL_DATA_MESSAGE; + +/** + * used to adjust receive window size. + * @MessageType - APF_WINDOW_ADJUST + * @RecipientChannel - channel number given in the open request + * @BytesToAdd - number of bytes to add to current window size value + **/ +typedef struct { + unsigned char MessageType; + unsigned int RecipientChannel; + unsigned int BytesToAdd; +} APF_WINDOW_ADJUST_MESSAGE; + +/** + * This message causes immediate termination of the connection with AMT. + * @ReasonCode - A Reason code for the disconnection event + * @Reserved - Reserved must be set to 0 + **/ +typedef struct { + unsigned char MessageType; + unsigned int ReasonCode; + unsigned short Reserved; +} APF_DISCONNECT_MESSAGE; + +/** + * Used to request a service identified by name + * @ServiceNameLength - The length of the service name string. + * @ServiceName - The name of the service being requested. + **/ +typedef struct { + unsigned char MessageType; + unsigned int ServiceNameLength; + unsigned char ServiceName[0]; +} APF_SERVICE_REQUEST_MESSAGE; + +/** + * Used to send a service accept identified by name + * @ServiceNameLength - The length of the service name string. + * @ServiceName - The name of the service being requested. + **/ +typedef struct { + unsigned char MessageType; + unsigned int ServiceNameLength; + unsigned char ServiceName[0]; +} APF_SERVICE_ACCEPT_MESSAGE; + +/** + * holds the protocl major and minor version implemented by AMT. + * @MajorVersion - Protocol's Major version + * @MinorVersion - Protocol's Minor version + * @Trigger - The open session reason + * @UUID - System Id + **/ +typedef struct { + unsigned char MessageType; + unsigned int MajorVersion; + unsigned int MinorVersion; + unsigned int TriggerReason; + unsigned char UUID[16]; + unsigned char Reserved[64]; +} APF_PROTOCOL_VERSION_MESSAGE; + + +/** + * holds the user authentication request success reponse. + **/ +typedef struct { + unsigned char MessageType; +} APF_USERAUTH_SUCCESS_MESSAGE; + +#pragma pack() + +#endif + +#endif diff --git a/MicroLMS/heci/LMS_if_constants.h b/MicroLMS/heci/LMS_if_constants.h new file mode 100644 index 0000000..0b1dda8 --- /dev/null +++ b/MicroLMS/heci/LMS_if_constants.h @@ -0,0 +1,92 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +#ifndef _LMS_IF_CONSTANTS_H_ +#define _LMS_IF_CONSTANTS_H_ + +#define LMS_PROTOCOL_VERSION 4 + +// +// messages opcodes +// +typedef enum { + APF_DISCONNECT = 1, + APF_SERVICE_REQUEST = 5, + APF_SERVICE_ACCEPT = 6, + APF_USERAUTH_REQUEST = 50, + APF_USERAUTH_FAILURE = 51, + APF_USERAUTH_SUCCESS = 52, + APF_GLOBAL_REQUEST = 80, + APF_REQUEST_SUCCESS = 81, + APF_REQUEST_FAILURE = 82, + APF_CHANNEL_OPEN = 90, + APF_CHANNEL_OPEN_CONFIRMATION = 91, + APF_CHANNEL_OPEN_FAILURE = 92, + APF_CHANNEL_WINDOW_ADJUST = 93, + APF_CHANNEL_DATA = 94, + APF_CHANNEL_CLOSE = 97, + APF_PROTOCOLVERSION = 192 +} APF_MESSAGE_TYPE; + +typedef enum { + APF_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, + APF_DISCONNECT_PROTOCOL_ERROR = 2, + APF_DISCONNECT_KEY_EXCHANGE_FAILED = 3, + APF_DISCONNECT_RESERVED = 4, + APF_DISCONNECT_MAC_ERROR = 5, + APF_DISCONNECT_COMPRESSION_ERROR = 6, + APF_DISCONNECT_SERVICE_NOT_AVAILABLE = 7, + APF_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8, + APF_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9, + APF_DISCONNECT_CONNECTION_LOST = 10, + APF_DISCONNECT_BY_APPLICATION = 11, + APF_DISCONNECT_TOO_MANY_CONNECTIONS = 12, + APF_DISCONNECT_AUTH_CANCELLED_BY_USER = 13, + APF_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14, + APF_DISCONNECT_ILLEGAL_USER_NAME = 15 +} APF_DISCONNECT_REASON_CODE; + +// +//strings used in global messages +// +#define APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST "tcpip-forward" +#define APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST "cancel-tcpip-forward" +#define APF_GLOBAL_REQUEST_STR_UDP_SEND_TO "udp-send-to@amt.intel.com" +#define APF_OPEN_CHANNEL_REQUEST_FORWARDED "forwarded-tcpip" +#define APF_OPEN_CHANNEL_REQUEST_DIRECT "direct-tcpip" + +// APF service names +#define APF_SERVICE_PFWD "pfwd@amt.intel.com" +#define APF_SERVICE_AUTH "auth@amt.intel.com" + +// APF Authentication method +#define APF_AUTH_NONE "none" +#define APF_AUTH_PASSWORD "password" + +//calculate string length without the NULL terminator +#define APF_STR_SIZE_OF(s) (sizeof(s)-1) + +// Trigger reason code +typedef enum { + USER_INITIATED_REQUEST = 1, + ALERT_REQUEST = 2, + HIT_PROVISIONING_REQUEST = 3, + PERIODIC_REQUEST = 4, + LME_REQUEST = 254 +} APF_TRIGGER_REASON; + +typedef enum { + OPEN_FAILURE_REASON_ADMINISTRATIVELY_PROHIBITED = 1, + OPEN_FAILURE_REASON_CONNECT_FAILED = 2, + OPEN_FAILURE_REASON_UNKNOWN_CHANNEL_TYPE = 3, + OPEN_FAILURE_REASON_RESOURCE_SHORTAGE = 4 +} OPEN_FAILURE_REASON; + +#endif + +#endif diff --git a/MicroLMS/heci/PTHICommand.c b/MicroLMS/heci/PTHICommand.c new file mode 100644 index 0000000..0ab42be --- /dev/null +++ b/MicroLMS/heci/PTHICommand.c @@ -0,0 +1,1457 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +//---------------------------------------------------------------------------- +// +// File: PTHICommand.cpp +// +//---------------------------------------------------------------------------- + +#include "PTHICommand.h" + +#if defined(WIN32) && defined (_DEBUG) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#include +#endif + +#include +#include "../microstack/ILibParsers.h" + + +AMT_STATUS _call(const unsigned char *command, UINT32 command_size, UINT8 **readBuffer, UINT32 rcmd, unsigned int expSize); // expSize default is 0 +AMT_STATUS _verifyResponseHeader(const UINT32 command, const PTHI_MESSAGE_HEADER *response_header, UINT32 response_size); +AMT_STATUS _verifyCodeVersions(const CFG_GET_CODE_VERSIONS_RESPONSE *response); +AMT_STATUS _verifyCurrentPowerPolicy(const CFG_GET_CURRENT_POWER_POLICY_RESPONSE *response); +AMT_STATUS _verifyGetDNSSuffixList(const CFG_GET_DNS_SUFFIX_LIST_RESPONSE *response); +AMT_STATUS _verifyRemoteAccessConnectionStatus(const CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *response); +AMT_STATUS _verifyHashHandles(const CFG_GET_HASH_HANDLES_RESPONSE *response); +AMT_STATUS _verifyGetCertificateHashEntry(const CFG_GET_CERTHASH_ENTRY_RESPONSE *response); +AMT_STATUS _verifyGetDnsSuffix(const CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *response); + +unsigned long m_sendTimeout = 2000; + + +const AMT_UUID AMT_UUID_LINK_STATE = + {0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01}; + + + +const PTHI_MESSAGE_HEADER GET_CODE_VERSION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{CODE_VERSIONS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_PROVISIONING_MODE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{PROVISIONING_MODE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_PROVISIONING_STATE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{PROVISIONING_STATE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_MAC_ADDRESSES_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_MAC_ADDRESSES_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_FEATURES_STATE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_FEATURES_STATE_REQUEST}}, (sizeof(CFG_GET_FEATURES_STATE_REQUEST) - sizeof(PTHI_MESSAGE_HEADER)) +}; + +const PTHI_MESSAGE_HEADER GET_CURRENT_POWER_POLICY_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_CURRENT_POWER_POLICY_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_LAST_HOST_RESET_REASON_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_LAST_HOST_RESET_REASON_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_LAN_INTERFACE_SETTINGS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_LAN_INTERFACE_SETTINGS_REQUEST}}, (sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST) - sizeof(PTHI_MESSAGE_HEADER)) +}; + +const PTHI_MESSAGE_HEADER GET_SECURITY_PARAMETERS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_SECURITY_PARAMETERS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_DNS_SUFFIX_LIST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_DNS_SUFFIX_LIST_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER SET_ENTERPRISE_ACCESS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{SET_ENTERPRISE_ACCESS_REQUEST}}, (sizeof(CFG_SET_ENTERPRISE_ACCESS_REQUEST) - sizeof(PTHI_MESSAGE_HEADER)) +}; + +const PTHI_MESSAGE_HEADER OPEN_USER_INITIATED_CONNECTION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{OPEN_USER_INITIATED_CONNECTION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER CLOSE_USER_INITIATED_CONNECTION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{CLOSE_USER_INITIATED_CONNECTION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_AMT_STATE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_AMT_STATE_REQUEST}}, sizeof(AMT_UUID) +}; + +const PTHI_MESSAGE_HEADER GENERATE_RNG_SEED_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GENERATE_RNG_SEED_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_RNG_SEED_STATUS_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_RNG_SEED_STATUS_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_ZERO_TOUCH_ENABLED_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_ZERO_TOUCH_ENABLED_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_PROVISIONING_TLS_MODE_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_PROVISIONING_TLS_MODE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER START_CONFIGURATION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{START_CONFIGURATION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER STOP_CONFIGURATION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{STOP_CONFIGURATION_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER ENUMERATE_HASH_HANDLES_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{ENUMERATE_HASH_HANDLES_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_CERTHASH_ENTRY_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_CERTHASH_ENTRY_REQUEST}}, sizeof(UINT32) +}; + +const PTHI_MESSAGE_HEADER GET_PKI_FQDN_SUFFIX_HEADER = { + {AMT_MAJOR_VERSION, AMT_MAJOR_VERSION}, 0, {{GET_PKI_FQDN_SUFFIX_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER SET_HOST_FQDN_HEADER = { + {AMT_MAJOR_VERSION, AMT_MAJOR_VERSION}, 0, {{SET_HOST_FQDN_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_LOCAL_SYSTEM_ACCOUNT_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_LOCAL_SYSTEM_ACCOUNT_REQUEST}}, 40 +}; + +const PTHI_MESSAGE_HEADER UNPROVISION_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{UNPROVISION_REQUEST}}, 4 +}; + +const PTHI_MESSAGE_HEADER GET_EHBC_STATE_REQUEST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_EHBC_STATE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_CONTROL_MODE_REQUEST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_CONTROL_MODE_REQUEST}}, 0 +}; + +const PTHI_MESSAGE_HEADER GET_UUID_REQUEST_HEADER = { + {AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{GET_UUID_REQUEST}}, 0 +}; + +AMT_STATUS _call(const unsigned char *command, UINT32 command_size, UINT8 **readBuffer, UINT32 rcmd, unsigned int expSize) +{ + UINT32 inBuffSize; + UINT32 outBuffSize = 0; + int bytesWritten; + AMT_STATUS status; + + inBuffSize = heci_GetBufferSize(NULL); + *readBuffer = (UINT8 *)malloc(sizeof(UINT8) * inBuffSize); + if (NULL == *readBuffer) + { + return PTSDK_STATUS_RESOURCES; + } + memset(*readBuffer, 0, inBuffSize); + + bytesWritten = heci_SendMessage(NULL, command, command_size, m_sendTimeout); + if ((UINT32)bytesWritten != command_size) + { + return AMT_STATUS_INTERNAL_ERROR; + } + outBuffSize = heci_ReceiveMessage(NULL, *readBuffer, inBuffSize, 2000); + if (0 == outBuffSize) + { + return 0xFFFF; //PTHI_STATUS_EMPTY_RESPONSE; + } + status = ((PTHI_RESPONSE_MESSAGE_HEADER *)*readBuffer)->Status; + if (status != AMT_STATUS_SUCCESS) + { + return status; + } + status = _verifyResponseHeader(rcmd, &(((PTHI_RESPONSE_MESSAGE_HEADER *)*readBuffer)->Header), outBuffSize); + if (status != AMT_STATUS_SUCCESS) + { + return status; + } + if ((expSize != 0) && (expSize != outBuffSize)) + { + return PTSDK_STATUS_INTERNAL_ERROR; + } + return AMT_STATUS_SUCCESS; +} + +/* +* Confirms the correctness of the response message header +* and the response message size +* Arguments: +* command - appropriate Host interface command +* response_header - reference to the response message header +* response_size - value that holds the actual size of the +* response message +* expected_size - value that holds the expected size of the +* response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyResponseHeader( + const UINT32 command, const PTHI_MESSAGE_HEADER *response_header, + UINT32 response_size) +{ + AMT_STATUS status = AMT_STATUS_SUCCESS; + + if (response_size < sizeof(PTHI_RESPONSE_MESSAGE_HEADER)) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_size != (response_header->Length + sizeof(PTHI_MESSAGE_HEADER))) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_header->Command.cmd.val != command) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_header->Reserved != 0) { + status = AMT_STATUS_INTERNAL_ERROR; + } else if (response_header->Version.MajorNumber != AMT_MAJOR_VERSION + || response_header->Version.MinorNumber < AMT_MINOR_VERSION) { + status = AMT_STATUS_INTERNAL_ERROR; + } + + return status; +} + +/* +* Confirms the correctness of the GetCodeVersions response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyCodeVersions(const CFG_GET_CODE_VERSIONS_RESPONSE *response) +{ + AMT_STATUS status = AMT_STATUS_SUCCESS; + UINT32 codeVerLen; + UINT32 ptVerTypeCount; + UINT32 len = 0; + UINT32 i; + + do { + codeVerLen = response->Header.Header.Length - sizeof(AMT_STATUS); + ptVerTypeCount = codeVerLen - sizeof(response->CodeVersions.BiosVersion)- sizeof(response->CodeVersions.VersionsCount); + if (response->CodeVersions.VersionsCount != (ptVerTypeCount/sizeof(AMT_VERSION_TYPE))) + { + status = AMT_STATUS_INTERNAL_ERROR; + break; + } + + for (i = 0; i < (response->CodeVersions.VersionsCount); i ++) + { + len = response->CodeVersions.Versions[i].Description.Length; + + if (len > UNICODE_STRING_LEN) + { + status = AMT_STATUS_INTERNAL_ERROR; + break; + } + + len = response->CodeVersions.Versions[i].Version.Length; + if (response->CodeVersions.Versions[i].Version.String[len] != '\0' || + (len != strnlen_s((CHAR *)(response->CodeVersions.Versions[i].Version.String), len))) + { + status = AMT_STATUS_INTERNAL_ERROR; + break; + } + } + } while (0); + + return status; +} + +/* +* GetVersions response message PTHI command +* Arguments: +* response - reference to the CODE_VERSIONS struct +* Return values: +* AMT_STATUS_SUCCESS - on success +* AMT_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS pthi_GetCodeVersions(CODE_VERSIONS *codeVersions) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_CODE_VERSION_HEADER); + unsigned char command[sizeof(GET_CODE_VERSION_HEADER)]; + AMT_STATUS status; + CFG_GET_CODE_VERSIONS_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_CODE_VERSION_HEADER), sizeof(GET_CODE_VERSION_HEADER)); + + status = _call(command, command_size, &readBuffer, CODE_VERSIONS_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_CODE_VERSIONS_RESPONSE *)readBuffer; + status = _verifyCodeVersions(tmp_response); + if (status != AMT_STATUS_SUCCESS) + { + break; + } + + memcpy_s(codeVersions, sizeof(CODE_VERSIONS), (char*)&(tmp_response->CodeVersions), sizeof(CODE_VERSIONS)); + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Calls to GetProvisioningMode Host interface command +* Arguments: +* mode - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetProvisioningMode(CFG_PROVISIONING_MODE *mode, AMT_BOOLEAN *legacy) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PROVISIONING_MODE_HEADER); + unsigned char command[sizeof(GET_PROVISIONING_MODE_HEADER)]; + AMT_STATUS status; + CFG_GET_PROVISIONING_MODE_RESPONSE* tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PROVISIONING_MODE_HEADER), sizeof(GET_PROVISIONING_MODE_HEADER)); + + status = _call(command, command_size, &readBuffer, PROVISIONING_MODE_RESPONSE, sizeof(CFG_GET_PROVISIONING_MODE_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_PROVISIONING_MODE_RESPONSE *)readBuffer; + + *mode = tmp_response->ProvisioningMode; + *legacy = tmp_response->LegacyMode; + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + + +/* +* Calls to GetProvisioningState Host interface command +* Arguments: +* state - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetProvisioningState(AMT_PROVISIONING_STATE *state) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PROVISIONING_STATE_HEADER); + unsigned char command[sizeof(GET_PROVISIONING_STATE_HEADER)]; + AMT_STATUS status; + CFG_GET_PROVISIONING_STATE_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PROVISIONING_STATE_HEADER), sizeof(GET_PROVISIONING_STATE_HEADER)); + + status = _call(command, command_size, &readBuffer, PROVISIONING_STATE_RESPONSE, sizeof(CFG_GET_PROVISIONING_STATE_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_PROVISIONING_STATE_RESPONSE *)readBuffer; + *state = tmp_response->ProvisioningState; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetProvisioningState Host interface command +* Arguments: +* state - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetMacAddresses(UINT8 DedicatedMac[6], UINT8 HostMac[6]) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_MAC_ADDRESSES_HEADER); + unsigned char command[sizeof(GET_MAC_ADDRESSES_HEADER)]; + AMT_STATUS status; + CFG_GET_MAC_ADDRESSES_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_MAC_ADDRESSES_HEADER), sizeof(GET_MAC_ADDRESSES_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_MAC_ADDRESSES_RESPONSE, sizeof(CFG_GET_MAC_ADDRESSES_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_MAC_ADDRESSES_RESPONSE *)readBuffer; + memcpy_s(DedicatedMac, 6, (char*)tmp_response->DedicatedMac, 6); + memcpy_s(HostMac, 6, (char*)tmp_response->HostMac, 6); + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetFeatureState Host interface command +* Arguments: +* requestID Indicates what feature status to query: +* 0 Redirection Sessions Status +* 1 System Defense Status +* 2 WebUI Status +* requestStatus The requested feature state(the size depand on the requestID).(OUT) +* +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetFeaturesState(UINT32 requestID, AMT_BOOLEAN (*requestStatus)[2]) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_FEATURES_STATE_REQUEST); + unsigned char command[sizeof(CFG_GET_FEATURES_STATE_REQUEST)]; + AMT_STATUS status; + CFG_GET_FEATURES_STATE_RESPONSE *tmp_response; + GET_FEATURES_REDIRECTION_SESSION_STATUS redirectionState; + GET_FEATURES_SYSTEM_DEFENSE_STATUS_RESPONSE systemDefenseState; + GET_FEATURES_WEB_UI_STATUS_RESPONSE webUIState; + + memcpy_s(command, sizeof(command), (char*)&GET_FEATURES_STATE_HEADER, sizeof(GET_FEATURES_STATE_HEADER)); + memcpy_s(command + sizeof(GET_FEATURES_STATE_HEADER), sizeof(command) - sizeof(GET_FEATURES_STATE_HEADER), (char*)&(requestID), sizeof(UINT32)); + + status = _call(command, command_size, &readBuffer, GET_FEATURES_STATE_RESPONSE, sizeof(CFG_GET_FEATURES_STATE_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_FEATURES_STATE_RESPONSE *)readBuffer; + + switch (requestID) + { + case REDIRECTION_SESSION: + redirectionState = tmp_response->Data.rs; + (*requestStatus)[0] = redirectionState.SolOpen; + (*requestStatus)[1] = redirectionState.IderOpen; + break; + + case SYSTEM_DEFENSE: + systemDefenseState = tmp_response->Data.sd; + (*requestStatus)[0] = systemDefenseState.SystemDefenseActivated; + break; + + case WEB_UI: + webUIState = tmp_response->Data.webUI; + (*requestStatus)[0] = webUIState.WebUiEnabled; + break; + } + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Calls to GetLastHostResetReason Host interface command +* Arguments: +* reason Indicates whether the last host reason was because of remote control operation(0) +* or other reason(1). (OUT) +* remoteControlTimeStamp In case the reason was due to remote control then this field +* indicates the timestamp of when the remote control command has been executed. +* (The timestamp is the number of seconds since 1/1/1970) +* +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetLastHostResetReason(UINT32 *reason, UINT32 *remoteControlTimeStamp) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_LAST_HOST_RESET_REASON_HEADER); + unsigned char command[sizeof(GET_LAST_HOST_RESET_REASON_HEADER)]; + AMT_STATUS status; + CFG_GET_LAST_HOST_RESET_REASON_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_LAST_HOST_RESET_REASON_HEADER), sizeof(GET_LAST_HOST_RESET_REASON_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_LAST_HOST_RESET_REASON_RESPONSE, sizeof(CFG_GET_LAST_HOST_RESET_REASON_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + tmp_response = (CFG_GET_LAST_HOST_RESET_REASON_RESPONSE *)readBuffer; + + *reason = tmp_response->Reason; + *remoteControlTimeStamp = tmp_response->RemoteControlTimeStamp; + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Calls to GetCurrentPowerPolicy Host interface command +* Arguments: +* policyName The power policy name. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetCurrentPowerPolicy(AMT_ANSI_STRING *policyName) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_CURRENT_POWER_POLICY_HEADER); + unsigned char command[sizeof(GET_CURRENT_POWER_POLICY_HEADER)]; + AMT_STATUS status; + CFG_GET_CURRENT_POWER_POLICY_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_CURRENT_POWER_POLICY_HEADER), sizeof(GET_CURRENT_POWER_POLICY_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_CURRENT_POWER_POLICY_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_CURRENT_POWER_POLICY_RESPONSE *)readBuffer; + status = _verifyCurrentPowerPolicy(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + policyName->Length = tmp_response->PolicyName.Length; + policyName->Buffer = (CHAR *)malloc(policyName->Length * sizeof(CHAR)); + if (NULL == policyName->Buffer) { + status = AMT_STATUS_INTERNAL_ERROR; + } else { + memcpy_s(policyName->Buffer, policyName->Length * sizeof(CHAR), (char*)&(tmp_response->PolicyName.Buffer), policyName->Length * sizeof(CHAR)); + } + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/* +* Confirms the correctness of the GetCurrentPowerPolicy response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyCurrentPowerPolicy(const CFG_GET_CURRENT_POWER_POLICY_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_CURRENT_POWER_POLICY_RESPONSE) + - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR *) + + response->PolicyName.Length)) + { + return PTSDK_STATUS_INTERNAL_ERROR; + } + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to GetLanInterfaceSttings Host interface command +* Arguments: +* interfaceSettings The interface to get the settings for. +* lanSettings reference to a pre allocated struct which will hold the lan settings. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetLanInterfaceSettings(UINT32 interfaceSettings, LAN_SETTINGS *lanSettings) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST); + unsigned char command[sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST)]; + AMT_STATUS status; + CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_LAN_INTERFACE_SETTINGS_HEADER), sizeof(GET_LAN_INTERFACE_SETTINGS_HEADER)); + memcpy_s(command + sizeof(GET_LAN_INTERFACE_SETTINGS_HEADER), sizeof(command) - sizeof(GET_LAN_INTERFACE_SETTINGS_HEADER), (char*)&(interfaceSettings), sizeof(UINT32)); + + status = _call(command, command_size, &readBuffer, GET_LAN_INTERFACE_SETTINGS_RESPONSE, sizeof(CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE)); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE *)readBuffer; + + lanSettings->Enabled = tmp_response->Enabled; + lanSettings->Ipv4Address = tmp_response->Ipv4Address; + lanSettings->DhcpEnabled = tmp_response->DhcpEnabled; + lanSettings->DhcpIpMode = tmp_response->DhcpIpMode; + lanSettings->LinkStatus = tmp_response->LinkStatus; + memcpy_s(lanSettings->MacAddress, sizeof(lanSettings->MacAddress), (char*)tmp_response->MacAddress, sizeof(tmp_response->MacAddress)); + + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + +/** +* Gets the HECI driver version +* Arguments: +* heciVersion - pointewr to HECI_VERSION struct (out) +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INVALID_PARAM - on failure +*/ +AMT_STATUS pthi_GetHeciVersion(HECI_VERSION *heciVersion) +{ + if (heci_GetHeciVersion(NULL, heciVersion)) return AMT_STATUS_SUCCESS; + return AMT_STATUS_INTERNAL_ERROR; +} + +/* +* Calls to GetSecurityParameters Host interface command +* Arguments: +* tlsEnabled true if AMT on TLS mode. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetTLSEnabled(AMT_BOOLEAN *tlsEnabled) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_SECURITY_PARAMETERS_HEADER); + unsigned char command[sizeof(GET_SECURITY_PARAMETERS_HEADER)]; + AMT_STATUS status; + CFG_GET_SECURITY_PARAMETERS_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_SECURITY_PARAMETERS_HEADER), sizeof(GET_SECURITY_PARAMETERS_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_SECURITY_PARAMETERS_RESPONSE, sizeof(CFG_GET_SECURITY_PARAMETERS_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_SECURITY_PARAMETERS_RESPONSE *)readBuffer; + *tlsEnabled = tmp_response->TLSEnabled; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetDNSSuffixList Host interface command +* Arguments: +* dnsSuffixList reference to list of DNS suffix strings. (OUT) +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +/* +AMT_STATUS pthi_GetDNSSuffixList(std::list &dnsSuffixList) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_DNS_SUFFIX_LIST_HEADER); + unsigned char command[command_size]; + memcpy(command, &(GET_DNS_SUFFIX_LIST_HEADER), sizeof(GET_DNS_SUFFIX_LIST_HEADER)); + + AMT_STATUS status = _call(command, command_size, &readBuffer, GET_DNS_SUFFIX_LIST_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) + { + break; + } + CFG_GET_DNS_SUFFIX_LIST_RESPONSE *tmp_response = (CFG_GET_DNS_SUFFIX_LIST_RESPONSE *)readBuffer; + status = _verifyGetDNSSuffixList(*tmp_response); + if (status != AMT_STATUS_SUCCESS) + { + break; + } + + char *current = (char *)tmp_response->Data; + while (current < (char *)tmp_response->Data + tmp_response->DataLength) + { + std::string dnsSuffix = current; + if (dnsSuffix.length() > tmp_response->DataLength) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + if (!dnsSuffix.empty()) + { + dnsSuffixList.push_back(dnsSuffix); + } + current += dnsSuffix.length() + 1; + } + } while (0); + + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} +*/ + +/* +* Confirms the correctness of the GetDNSSuffixList response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyGetDNSSuffixList(const CFG_GET_DNS_SUFFIX_LIST_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_DNS_SUFFIX_LIST_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) + response->DataLength)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to SetEnterpriseAccess Host interface command +* Arguments: +* Flags flags +* HostIPAddress host IP address for enterprise access +* EnterpriseAccess enterprise access mode +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_SetEnterpriseAccess(UINT8 Flags, UINT8 HostIPAddress[16], UINT8 EnterpriseAccess) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_SET_ENTERPRISE_ACCESS_REQUEST); + unsigned char command[sizeof(CFG_SET_ENTERPRISE_ACCESS_REQUEST)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(SET_ENTERPRISE_ACCESS_HEADER), sizeof(SET_ENTERPRISE_ACCESS_HEADER)); + memcpy_s(command + sizeof(SET_ENTERPRISE_ACCESS_HEADER), sizeof(command) - sizeof(SET_ENTERPRISE_ACCESS_HEADER), (char*)&(Flags), sizeof(UINT8)); + memcpy_s(command + sizeof(SET_ENTERPRISE_ACCESS_HEADER) + sizeof(UINT8), sizeof(command) - sizeof(SET_ENTERPRISE_ACCESS_HEADER) - sizeof(UINT8), (char*)HostIPAddress, (int)sizeof(HostIPAddress)); + memcpy_s(command + sizeof(SET_ENTERPRISE_ACCESS_HEADER) + sizeof(UINT8) + sizeof(HostIPAddress), sizeof(command) - sizeof(SET_ENTERPRISE_ACCESS_HEADER) - sizeof(UINT8) - sizeof(HostIPAddress), (char*)&(EnterpriseAccess), (int)sizeof(UINT8)); + + status = _call(command, command_size, &readBuffer, SET_ENTERPRISE_ACCESS_RESPONSE, sizeof(CFG_SET_ENTERPRISE_ACCESS_RESPONSE)); + + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Get FW last reset reason +* Arguments: +* reason - last FW reason +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetFWResetReason(UINT8 *MEResetReason) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(STATE_GET_AMT_STATE_REQUEST); + unsigned char command[sizeof(STATE_GET_AMT_STATE_REQUEST)]; + AMT_STATUS status; + STATE_GET_AMT_STATE_RESPONSE *tmp_response = NULL; + + memcpy_s(command, sizeof(command), (char*)&(GET_AMT_STATE_HEADER), sizeof(GET_AMT_STATE_HEADER)); + memcpy_s(command + sizeof(GET_AMT_STATE_HEADER), sizeof(command) - sizeof(GET_AMT_STATE_HEADER), (char*)&(AMT_UUID_LINK_STATE), sizeof(AMT_UUID)); + + status = _call(command, command_size, &readBuffer, GET_AMT_STATE_RESPONSE, sizeof(STATE_GET_AMT_STATE_RESPONSE)); + if (status != AMT_STATUS_SUCCESS) + { + if (readBuffer == NULL) return AMT_STATUS_INTERNAL_ERROR; + tmp_response = (STATE_GET_AMT_STATE_RESPONSE *)readBuffer; + *MEResetReason = tmp_response->StateData.LastMEResetReason; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* Calls to OpenUserInitiatedConnection Host interface command +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_OpenUserInitiatedConnection() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(OPEN_USER_INITIATED_CONNECTION_HEADER); + unsigned char command[sizeof(OPEN_USER_INITIATED_CONNECTION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(OPEN_USER_INITIATED_CONNECTION_HEADER), sizeof(OPEN_USER_INITIATED_CONNECTION_HEADER)); + status = _call(command, command_size, &readBuffer, OPEN_USER_INITIATED_CONNECTION_RESPONSE, sizeof(CFG_OPEN_USER_INITIATED_CONNECTION_RESPONSE)); + + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* Calls to CloseUserInitiatedConnection Host interface command +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_CloseUserInitiatedConnection() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CLOSE_USER_INITIATED_CONNECTION_HEADER); + unsigned char command[sizeof(CLOSE_USER_INITIATED_CONNECTION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), &(CLOSE_USER_INITIATED_CONNECTION_HEADER), sizeof(CLOSE_USER_INITIATED_CONNECTION_HEADER)); + status = _call(command, command_size, &readBuffer, CLOSE_USER_INITIATED_CONNECTION_RESPONSE, sizeof(CFG_CLOSE_USER_INITIATED_CONNECTION_RESPONSE)); + + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* Calls to GetRemoteAccessConnectionStatus Host interface command +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetRemoteAccessConnectionStatus(REMOTE_ACCESS_STATUS *remoteAccessStatus) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER); + unsigned char command[sizeof(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER)]; + CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *tmp_response; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER), sizeof(GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *)readBuffer; + status = _verifyRemoteAccessConnectionStatus(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + remoteAccessStatus->AmtNetworkConnectionStatus = tmp_response->AmtNetworkConnectionStatus; + remoteAccessStatus->RemoteAccessConnectionStatus = tmp_response->RemoteAccessConnectionStatus; + remoteAccessStatus->RemoteAccessConnectionTrigger = tmp_response->RemoteAccessConnectionTrigger; + + remoteAccessStatus->MpsHostname.Length = tmp_response->MpsHostname.Length; + remoteAccessStatus->MpsHostname.Buffer = (CHAR *)malloc(remoteAccessStatus->MpsHostname.Length * sizeof(CHAR)); + if (NULL == remoteAccessStatus->MpsHostname.Buffer) { + status = AMT_STATUS_INTERNAL_ERROR; + } else { + memcpy_s(remoteAccessStatus->MpsHostname.Buffer, + remoteAccessStatus->MpsHostname.Length * sizeof(CHAR), + &(tmp_response->MpsHostname.Buffer), + tmp_response->MpsHostname.Length * sizeof(CHAR)); + } + } while (0); + if (readBuffer != NULL) + { + free(readBuffer); + } + return status; +} + + +/* +* Confirms the correctness of the GetRemoteAccessConnectionStatus response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyRemoteAccessConnectionStatus(const CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR *) + response->MpsHostname.Length)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to GenerateRngKey Host interface command +* Arguments: +* None +* Return values: +* AMT_STATUS_SUCCESS - or AMT_STATUS_IN_PROGRESS on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GenerateRngKey() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GENERATE_RNG_SEED_HEADER); + unsigned char command[sizeof(GENERATE_RNG_SEED_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(GENERATE_RNG_SEED_HEADER), sizeof(GENERATE_RNG_SEED_HEADER)); + + status = _call(command, command_size, &readBuffer, GENERATE_RNG_SEED_RESPONSE, sizeof(CFG_GENERATE_RNG_SEED_RESPONSE)); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetRngSeedStatus Host interface command +* Arguments: +* rngStatus - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetRngSeedStatus(AMT_RNG_STATUS *rngStatus) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_RNG_SEED_STATUS_HEADER); + unsigned char command[sizeof(GET_RNG_SEED_STATUS_HEADER)]; + CFG_GET_RNG_SEED_STATUS_RESPONSE *tmp_response; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(GET_RNG_SEED_STATUS_HEADER), sizeof(GET_RNG_SEED_STATUS_HEADER)); + status = _call(command, command_size, &readBuffer, GET_RNG_SEED_STATUS_RESPONSE, sizeof(CFG_GET_RNG_SEED_STATUS_RESPONSE)); + if ((tmp_response = (CFG_GET_RNG_SEED_STATUS_RESPONSE *)readBuffer) == NULL) exit(254); + *rngStatus = tmp_response->RngStatus; + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to ZeroTouchEnabled Host interface command +* Arguments: +* zeroTouchEnabled - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetZeroTouchEnabled(AMT_BOOLEAN *zeroTouchEnabled) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_ZERO_TOUCH_ENABLED_HEADER); + unsigned char command[sizeof(GET_ZERO_TOUCH_ENABLED_HEADER)]; + AMT_STATUS status; + CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*) &(GET_ZERO_TOUCH_ENABLED_HEADER), sizeof(GET_ZERO_TOUCH_ENABLED_HEADER)); + status = _call(command, command_size, &readBuffer, GET_ZERO_TOUCH_ENABLED_RESPONSE, sizeof(CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE)); + if ((tmp_response = (CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE *)readBuffer) == NULL) exit(254); + *zeroTouchEnabled = tmp_response->ZeroTouchEnabled; + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to GetProvisioningTlsMode Host interface command +* Arguments: +* provisioningTlsMode - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetProvisioningTlsMode(AMT_PROVISIONING_TLS_MODE *provisioningTlsMode) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PROVISIONING_TLS_MODE_HEADER); + unsigned char command[sizeof(GET_PROVISIONING_TLS_MODE_HEADER)]; + AMT_STATUS status; + CFG_GET_PROVISIONING_TLS_MODE_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PROVISIONING_TLS_MODE_HEADER), sizeof(GET_PROVISIONING_TLS_MODE_HEADER)); + status = _call(command, command_size, &readBuffer, GET_PROVISIONING_TLS_MODE_RESPONSE, sizeof(CFG_GET_PROVISIONING_TLS_MODE_RESPONSE)); + if ((tmp_response = (CFG_GET_PROVISIONING_TLS_MODE_RESPONSE *)readBuffer) == NULL) exit(254); + *provisioningTlsMode = tmp_response->ProvisioningTlsMode; + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to StartConfiguration Host interface command +* Arguments: +* None +* Return values: +* AMT_STATUS_SUCCESS - or AMT_STATUS_CERTIFICATE_NOT_READY on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_StartConfiguration() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(START_CONFIGURATION_HEADER); + unsigned char command[sizeof(START_CONFIGURATION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*) &(START_CONFIGURATION_HEADER), sizeof(START_CONFIGURATION_HEADER)); + status = _call(command, command_size, &readBuffer, START_CONFIGURATION_RESPONSE, sizeof(CFG_START_CONFIGURATION_RESPONSE)); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to StopConfiguration Host interface command +* Arguments: +* None +* Return values: +* A status code that indicates the success or specific reason for failure of the operation is returned in the Status field of the response message. The following table lists the possible Status values. +* +* Status Description +* AMT_STATUS_INVALID_AMT_MODE Returned when FW not in in- provision state or when provisioning period expired +* AMT_STATUS_SUCCESS Request succeeded. +* AMT_STATUS_INTERNAL_ERROR An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error +* AMT_STATUS_INVALID_MESSAGE_LENGTH Length field of header is invalid. +* AMT_STATUS_NOT_READY Management controller has not progressed far enough in its initialization to process the command. +*/ +AMT_STATUS pthi_StopConfiguration() +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(STOP_CONFIGURATION_HEADER); + unsigned char command[sizeof(STOP_CONFIGURATION_HEADER)]; + AMT_STATUS status; + + memcpy_s(command, sizeof(command), (char*)&(STOP_CONFIGURATION_HEADER), sizeof(STOP_CONFIGURATION_HEADER)); + status = _call(command, command_size, &readBuffer, STOP_CONFIGURATION_RESPONSE, sizeof(CFG_STOP_CONFIGURATION_RESPONSE)); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to SetProvisioningServerOTP Host interface command +* Arguments: +* passwordOTP AMT_ANSI_STRING structure of OTP password +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_SetProvisioningServerOTP(AMT_ANSI_STRING passwordOTP) +{ + UINT8 *readBuffer = NULL; + UINT32 msgLength = sizeof(passwordOTP.Length) + (passwordOTP.Length * sizeof(CHAR)); + PTHI_MESSAGE_HEADER SET_PROVISIONING_SERVER_OTP_HEADER = {{AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{SET_PROVISIONING_SERVER_OTP_REQUEST}}, msgLength}; + UINT32 command_size; + unsigned char *command; + AMT_STATUS status; + + if (NULL == passwordOTP.Buffer) return PTSDK_STATUS_INVALID_PARAM; + + command_size = sizeof(SET_PROVISIONING_SERVER_OTP_HEADER) + msgLength; + command = (unsigned char *)malloc(command_size); + if (command == NULL) return PTSDK_STATUS_INTERNAL_ERROR; + memcpy_s(command, command_size, (char*)&SET_PROVISIONING_SERVER_OTP_HEADER, sizeof(SET_PROVISIONING_SERVER_OTP_HEADER)); + memcpy_s(command + sizeof(SET_PROVISIONING_SERVER_OTP_HEADER), command_size - sizeof(SET_PROVISIONING_SERVER_OTP_HEADER), (char*)&(passwordOTP.Length), sizeof(passwordOTP.Length)); + memcpy_s(command + sizeof(SET_PROVISIONING_SERVER_OTP_HEADER) + sizeof(passwordOTP.Length), command_size - sizeof(SET_PROVISIONING_SERVER_OTP_HEADER) - sizeof(passwordOTP.Length), passwordOTP.Buffer, passwordOTP.Length); + + status = _call(command, command_size, &readBuffer, SET_PROVISIONING_SERVER_OTP_RESPONSE, sizeof(CFG_SET_PROVISIONING_SERVER_OTP_RESPONSE)); + + if (NULL != command) free(command); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to SetDnsSuffix Host interface command +* Arguments: +* dnsSuffix AMT_ANSI_STRING structure of DNS suffix +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_SetDnsSuffix(AMT_ANSI_STRING dnsSuffix) +{ + UINT8 *readBuffer = NULL; + UINT32 msgLength = sizeof(dnsSuffix.Length) + (dnsSuffix.Length * sizeof(CHAR)); + PTHI_MESSAGE_HEADER SET_DNS_SUFFIX_HEADER = {{AMT_MAJOR_VERSION, AMT_MINOR_VERSION}, 0, {{SET_DNS_SUFFIX_REQUEST}}, msgLength}; + UINT32 command_size; + unsigned char *command; + AMT_STATUS status; + + if (dnsSuffix.Buffer == NULL) return PTSDK_STATUS_INVALID_PARAM; + command_size = sizeof(SET_DNS_SUFFIX_HEADER) + msgLength; + command = (unsigned char *)malloc(command_size); + if (command == NULL) return PTSDK_STATUS_INTERNAL_ERROR; + memcpy_s(command, command_size, (char*)&SET_DNS_SUFFIX_HEADER, sizeof(SET_DNS_SUFFIX_HEADER)); + memcpy_s(command + sizeof(SET_DNS_SUFFIX_HEADER), command_size - sizeof(SET_DNS_SUFFIX_HEADER), (char*)&(dnsSuffix.Length), sizeof(dnsSuffix.Length)); + memcpy_s(command + sizeof(SET_DNS_SUFFIX_HEADER) + sizeof(dnsSuffix.Length), command_size - sizeof(SET_DNS_SUFFIX_HEADER) - sizeof(dnsSuffix.Length), dnsSuffix.Buffer, dnsSuffix.Length); + + status = _call(command, command_size, &readBuffer, SET_DNS_SUFFIX_RESPONSE, sizeof(CFG_SET_DNS_SUFFIX_RESPONSE)); + + if (command != NULL) free(command); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Calls to EnumerateHashHandles Host interface command +* Arguments: +* hashHandles - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_EnumerateHashHandles(AMT_HASH_HANDLES *hashHandles) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(ENUMERATE_HASH_HANDLES_HEADER); + unsigned char command[sizeof(ENUMERATE_HASH_HANDLES_HEADER)]; + AMT_STATUS status; + CFG_GET_HASH_HANDLES_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(ENUMERATE_HASH_HANDLES_HEADER), sizeof(ENUMERATE_HASH_HANDLES_HEADER)); + + status = _call(command, command_size, &readBuffer, ENUMERATE_HASH_HANDLES_RESPONSE, 0); + do + { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_HASH_HANDLES_RESPONSE *)readBuffer; + status = _verifyHashHandles(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + memset(hashHandles->Handles, 0, sizeof(UINT32) * CERT_HASH_MAX_NUMBER); + hashHandles->Length = tmp_response->HashHandles.Length; + if (CERT_HASH_MAX_NUMBER < hashHandles->Length) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + + memcpy_s(hashHandles->Handles, sizeof(hashHandles->Handles), (char*)tmp_response->HashHandles.Handles, sizeof(UINT32) * hashHandles->Length); + + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* +* Confirms the correctness of the EnumerateHashHandles response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyHashHandles(const CFG_GET_HASH_HANDLES_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != sizeof(AMT_STATUS) + sizeof(response->HashHandles.Length) + (sizeof(UINT32) * response->HashHandles.Length)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + + +/* +* Calls to GetCertificateHashEntry Host interface command +* Arguments: +* passwordOTP AMT_ANSI_STRING structure of DNS suffix +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetCertificateHashEntry(UINT32 hashHandle, CERTHASH_ENTRY *hashEntry) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_CERTHASH_ENTRY_REQUEST); + unsigned char command[sizeof(CFG_GET_CERTHASH_ENTRY_REQUEST)]; + AMT_STATUS status; + CFG_GET_CERTHASH_ENTRY_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_CERTHASH_ENTRY_HEADER), sizeof(GET_CERTHASH_ENTRY_HEADER)); + memcpy_s(command + sizeof(GET_CERTHASH_ENTRY_HEADER), sizeof(command) - sizeof(GET_CERTHASH_ENTRY_HEADER), (char*)&(hashHandle), sizeof(hashHandle)); + + status = _call(command, command_size, &readBuffer, GET_CERTHASH_ENTRY_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_CERTHASH_ENTRY_RESPONSE *)readBuffer; + status = _verifyGetCertificateHashEntry(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + hashEntry->IsActive = tmp_response->Hash.IsActive; + hashEntry->IsDefault = tmp_response->Hash.IsDefault; + hashEntry->Name.Length = tmp_response->Hash.Name.Length; + hashEntry->HashAlgorithm = tmp_response->Hash.HashAlgorithm; + memcpy_s(hashEntry->CertificateHash, sizeof(hashEntry->CertificateHash), tmp_response->Hash.CertificateHash, sizeof(tmp_response->Hash.CertificateHash)); + hashEntry->Name.Buffer = (CHAR *)malloc(hashEntry->Name.Length * sizeof(CHAR)); + if (NULL == hashEntry->Name.Buffer) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + memcpy_s(hashEntry->Name.Buffer, hashEntry->Name.Length * sizeof(CHAR), &(tmp_response->Hash.Name.Buffer), hashEntry->Name.Length * sizeof(CHAR)); + + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} +/* +* Confirms the correctness of the GetCertificateHashEntry response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyGetCertificateHashEntry(const CFG_GET_CERTHASH_ENTRY_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != (sizeof(CFG_GET_CERTHASH_ENTRY_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR *) + response->Hash.Name.Length)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +/* +* Calls to GetDnsSuffix Host interface command +* Arguments: +* dnsSuffix - reference to the pre-allocated structure +* which will hold the result +* Return values: +* AMT_STATUS_SUCCESS - on success +* appropriate error value defined in StatusCodeDefinitions.h - on failure +*/ +AMT_STATUS pthi_GetDnsSuffix(AMT_ANSI_STRING *dnsSuffix) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(GET_PKI_FQDN_SUFFIX_HEADER); + unsigned char command[sizeof(GET_PKI_FQDN_SUFFIX_HEADER)]; + AMT_STATUS status; + CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *tmp_response; + + memcpy_s(command, sizeof(command), (char*)&(GET_PKI_FQDN_SUFFIX_HEADER), sizeof(GET_PKI_FQDN_SUFFIX_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_PKI_FQDN_SUFFIX_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *)readBuffer; + status = _verifyGetDnsSuffix(tmp_response); + if (status != AMT_STATUS_SUCCESS) break; + + dnsSuffix->Length = tmp_response->Suffix.Length; + dnsSuffix->Buffer = (CHAR *)malloc(dnsSuffix->Length * sizeof(CHAR)); + if (NULL == dnsSuffix->Buffer) + { + status = PTSDK_STATUS_INTERNAL_ERROR; + break; + } + memcpy_s(dnsSuffix->Buffer, dnsSuffix->Length * sizeof(CHAR), &(tmp_response->Suffix.Buffer), dnsSuffix->Length * sizeof(CHAR)); + + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} +/* +* Confirms the correctness of the GetDnsSuffix response message +* Arguments: +* response - reference to the response message +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS _verifyGetDnsSuffix(const CFG_GET_PKI_FQDN_SUFFIX_RESPONSE *response) +{ + ULONG ByteCount = response->Header.Header.Length; + if (ByteCount != sizeof(AMT_STATUS) + sizeof(response->Suffix.Length) + response->Suffix.Length * sizeof(CHAR)) return PTSDK_STATUS_INTERNAL_ERROR; + return AMT_STATUS_SUCCESS; +} + +AMT_STATUS pthi_GetLocalSystemAccount(LOCAL_SYSTEM_ACCOUNT *localAccount) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST); + unsigned char command[sizeof(CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST)]; + AMT_STATUS status; + CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_LOCAL_SYSTEM_ACCOUNT_HEADER), sizeof(GET_LOCAL_SYSTEM_ACCOUNT_HEADER)); + + status = _call(command, command_size, &readBuffer, GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE, 0); + do { + if (status != AMT_STATUS_SUCCESS) break; + tmp_response = (CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE *)readBuffer; + status = tmp_response->Status; + if( status != AMT_STATUS_SUCCESS ) break; + memcpy_s(localAccount, sizeof(LOCAL_SYSTEM_ACCOUNT), (char*)&(tmp_response->Account), sizeof(LOCAL_SYSTEM_ACCOUNT)); + } while (0); + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* + * Unprovision AMTHI command + * Arguments: + * provisionMode - indicates the provisioining mode of the device upon unprovisioining + * Return values: + * AMT_LOCAL_AGENT_STATUS_SUCCESS - on success + * AMT_STATUS_INTERNAL_ERROR - on failure + * AMT_STATUS_NOT_READY - Management controller has not progressed far enough in its + * initialization to process the command. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + * AMT_STATUS_BLOCKING_COMPONENT - One of the ME components is not ready for unprovisioning. + */ +AMT_STATUS pthi_Unprovision(CFG_PROVISIONING_MODE provisionMode) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_UNPROVISION_REQUEST); + unsigned char command[sizeof(CFG_UNPROVISION_REQUEST)]; + AMT_STATUS status; + + if ((CFG_PROVISIONING_MODE_NONE != provisionMode) && (CFG_PROVISIONING_MODE_ENTERPRISE != provisionMode)) { ILIBMESSAGE("Unprovision PTSDK_STATUS_INVALID_PARAM"); return PTSDK_STATUS_INVALID_PARAM; } + memset(command, 0, sizeof(CFG_UNPROVISION_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(UNPROVISION_HEADER), sizeof(UNPROVISION_HEADER)); + status = _call(command, command_size, &readBuffer, UNPROVISION_RESPONSE, 0); + if (readBuffer != NULL) free(readBuffer); + return status; +} + + +/* +* Sets host FQDN in AMT Calls to CFG_SET_HOST_FQDN_REQUEST command +* Arguments: +* host - host FQDN +* Return values: +* AMT_STATUS_SUCCESS - on success +* PTSDK_STATUS_INTERNAL_ERROR - on failure +*/ +AMT_STATUS pthi_SetHostFQDN(char* str) +{ + //send requeast message to FW: + AMT_STATUS status; + UINT8 *readBuffer = NULL; + CFG_SET_HOST_FQDN_REQUEST command; + int len = (int)strnlen_s(str, _MAX_PATH); + + memset(&command, 0, sizeof(CFG_SET_HOST_FQDN_REQUEST)); // Fix the valgrind warning + command.Header = SET_HOST_FQDN_HEADER; + command.Header.Length = sizeof(UINT16) + len; + command.FQDNL = len; + memcpy_s(&command.FQDN, sizeof(command.FQDN), str, len); + status = _call((UINT8 *)&command, sizeof(command), &readBuffer, SET_HOST_FQDN_RESPONSE, sizeof(CFG_SET_HOST_FQDN_RESPONSE)); + if (readBuffer != NULL) { free(readBuffer); } + return status; +} + + + +/* + * Get EHBC state AMTHI command + * This command returns EHBC enable state. Note: This command is supported from MR 8.1.20 + * Arguments: + * None + * Return values: (A status code returned in a response message that indicates whether the operation specified in the corresponding request message succeeded or failed. If the operation failed, this code indicates the specific reason for failure. Possible values described below.) + * AMT_STATUS_SUCCESS - Request succeeded. + * AMT_STATUS_INTERNAL_ERROR - An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + */ +AMT_STATUS pthi_GetStateEHBC(AMT_EHBC_STATE *state) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GETEHBPSTATE_REQUEST); + unsigned char command[sizeof(CFG_GETEHBPSTATE_REQUEST)]; + AMT_STATUS status; + CFG_GETEHBPSTATE_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GETEHBPSTATE_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_EHBC_STATE_REQUEST_HEADER), sizeof(GET_EHBC_STATE_REQUEST_HEADER)); + status = _call(command, command_size, &readBuffer, GET_EHBC_STATE_RESPONSE, sizeof(CFG_GETEHBPSTATE_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GETEHBPSTATE_RESPONSE*)readBuffer; + *state = tmp_response->EHBCState; + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + + +/* + * Get control mode AMTHI command + * gets the current control mode of AMT (client or admin control mode). The machine will be in client control mode if it was configured this way using host based configuration. + * If the machine was configured using RCFG or using host based provisioning to admin control mode in admin control mode. If the machine is not provisioned this command will return the value None. + * Arguments: + * None + * Return values: (A status code returned in a response message that indicates whether the operation specified in the corresponding request message succeeded or failed. If the operation failed, this code indicates the specific reason for failure. Possible values described below.) + * AMT_STATUS_SUCCESS - Request succeeded. + * AMT_STATUS_INTERNAL_ERROR - An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + */ +AMT_STATUS pthi_GetControlMode(int *state) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_CONTROL_MODE_REQUEST); + unsigned char command[sizeof(CFG_GET_CONTROL_MODE_REQUEST)]; + AMT_STATUS status; + CFG_GET_CONTROL_MODE_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GET_CONTROL_MODE_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_CONTROL_MODE_REQUEST_HEADER), sizeof(GET_CONTROL_MODE_REQUEST_HEADER)); + status = _call(command, command_size, &readBuffer, GET_CONTROL_MODE_RESPONSE, sizeof(CFG_GET_CONTROL_MODE_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_CONTROL_MODE_RESPONSE*)readBuffer; + *state = tmp_response->state; // 0 - None (or RPAT for 6.x) 1 - Client 2 - Admin + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +/* + * Get UID AMTHI command + * gets the UUID of AMT. + * Arguments: + * None + * Return values: (A status code returned in a response message that indicates whether the operation specified in the corresponding request message succeeded or failed. If the operation failed, this code indicates the specific reason for failure. Possible values described below.) + * AMT_STATUS_SUCCESS - Request succeeded. + * AMT_STATUS_INTERNAL_ERROR - An internal error to the AMT device has occurred. This may indicate an interface error, or a AMT application error. + * AMT_STATUS_INVALID_MESSAGE_LENGTH - Length field of header is invalid. + */ +AMT_STATUS pthi_GetUUID(AMT_UUID *uuid) +{ + UINT8 *readBuffer = NULL; + UINT32 command_size = sizeof(CFG_GET_UUID_REQUEST); + unsigned char command[sizeof(CFG_GET_UUID_REQUEST)]; + AMT_STATUS status; + CFG_GET_UUID_RESPONSE *tmp_response; + + memset(command, 0, sizeof(CFG_GET_UUID_REQUEST)); + memcpy_s(command, sizeof(command), (char*)&(GET_UUID_REQUEST_HEADER), sizeof(GET_UUID_REQUEST_HEADER)); + status = _call(command, command_size, &readBuffer, GET_UUID_RESPONSE, sizeof(CFG_GET_UUID_RESPONSE)); + if (status == AMT_STATUS_SUCCESS) + { + tmp_response = (CFG_GET_UUID_RESPONSE*)readBuffer; + memcpy_s(uuid, sizeof(AMT_UUID), (char*)&(tmp_response->UUID), sizeof(AMT_UUID)); // uuid + } + if (readBuffer != NULL) free(readBuffer); + return status; +} + +#endif diff --git a/MicroLMS/heci/PTHICommand.h b/MicroLMS/heci/PTHICommand.h new file mode 100644 index 0000000..3039093 --- /dev/null +++ b/MicroLMS/heci/PTHICommand.h @@ -0,0 +1,815 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +//---------------------------------------------------------------------------- +// +// File: PTHICommand.h +// +// Contents: header file of PTHICommand class +// +//---------------------------------------------------------------------------- +#ifndef __PTHI_COMMAND_H__ +#define __PTHI_COMMAND_H__ + +#include "StatusCodeDefinitions.h" + +#ifdef WIN32 +#include +#include "HECIwin.h" +#endif + +#ifdef _POSIX +#include "HECILinux.h" +#endif +#define CERT_HASH_MAX_LENGTH 64 +#define CERT_HASH_MAX_NUMBER 23 +#define NET_TLS_CERT_PKI_MAX_SERIAL_NUMS 3 +#define NET_TLS_CERT_PKI_MAX_SERIAL_NUM_LENGTH 16 +#define MPS_HOSTNAME_LENGTH 256 + +#define CFG_MAX_ACL_USER_LENGTH 33 +#define CFG_MAX_ACL_PWD_LENGTH 33 + +#pragma pack(1) +typedef struct _LOCAL_SYSTEM_ACCOUNT +{ + // contain null terminated string + char username[CFG_MAX_ACL_USER_LENGTH]; + // contain null terminated string + char password[CFG_MAX_ACL_PWD_LENGTH]; +}LOCAL_SYSTEM_ACCOUNT; +#pragma pack() + +/* +* Constants +*/ + +/* +static #define IDER_LOG_ENTRIES = 16; + +const UINT8 MAJOR_VERSION = 1; +const UINT8 MINOR_VERSION = 1; +const UINT8 AMT_MAJOR_VERSION = 1; +const UINT8 AMT_MINOR_VERSION = 1; +*/ + +#define IDER_LOG_ENTRIES 16 + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 1 +#define AMT_MAJOR_VERSION 1 +#define AMT_MINOR_VERSION 1 + +typedef enum _CFG_PROVISIONING_MODE +{ + CFG_PROVISIONING_MODE_NONE = 0, + CFG_PROVISIONING_MODE_ENTERPRISE, + CFG_PROVISIONING_MODE_SMALL_BUSINESS, + CFG_PROVISIONING_MODE_REMOTE_ASSISTANCE +} CFG_PROVISIONING_MODE; + +typedef enum _AMT_PROVISIONING_STATE +{ + PROVISIONING_STATE_PRE = 0, + PROVISIONING_STATE_IN = 1, + PROVISIONING_STATE_POST = 2 +} AMT_PROVISIONING_STATE; + +typedef enum _AMT_EHBC_STATE +{ + EHBC_STATE_DISABLED = 0, + EHBC_STATE_ENABLED = 1 +} AMT_EHBC_STATE; + +typedef enum _AMT_FEATURE_STATE_REQUEST +{ + REDIRECTION_SESSION = 0, + SYSTEM_DEFENSE = 1, + WEB_UI = 2 +} AMT_FEATURE_STATE_REQUEST; + +typedef enum _AMT_LAST_HOST_RESET_REASON +{ + RemoteControl = 0, + Other = 1 +} AMT_LAST_HOST_RESET_REASON; + +typedef enum _AMT_INTERFACE_INDEX +{ + WIRED = 0, + WIRELESS = 1 +} AMT_INTERFACE_INDEX; + +typedef enum _AMT_DHCP_IP_ADDRESS +{ + ACTIVE = 1, + PASSIVE = 2 +} AMT_DHCP_IP_MODE; + +//typedef UINT32 CFG_IPv4_ADDRESS + +#define CFG_IPv4_ADDRESS UINT32 + +/* +static #define BIOS_VERSION_LEN = 65; +static #define VERSIONS_NUMBER = 50; +static #define UNICODE_STRING_LEN = 20; +*/ + +#define BIOS_VERSION_LEN 65 +#define VERSIONS_NUMBER 50 +#define UNICODE_STRING_LEN 20 + +typedef enum _AMT_PROVISIONING_TLS_MODE +{ + NOT_READY = 0, + PSK = 1, + PKI = 2 +} AMT_PROVISIONING_TLS_MODE; + +typedef enum _AMT_RNG_STATUS +{ + RNG_STATUS_EXIST = 0, + RNG_STATUS_IN_PROGRESS = 1, + RNG_STATUS_NOT_EXIST = 2 +} AMT_RNG_STATUS; + +#pragma pack(1) + +typedef struct _AMT_UNICODE_STRING +{ + UINT16 Length; + UINT8 String[UNICODE_STRING_LEN]; +} AMT_UNICODE_STRING; + +typedef struct _AMT_VERSION_TYPE +{ + AMT_UNICODE_STRING Description; + AMT_UNICODE_STRING Version; +} AMT_VERSION_TYPE; + +typedef struct _PTHI_VERSION +{ + UINT8 MajorNumber; + UINT8 MinorNumber; +} PTHI_VERSION; + +typedef struct _CODE_VERSIONS +{ + UINT8 BiosVersion[BIOS_VERSION_LEN]; + UINT32 VersionsCount; + AMT_VERSION_TYPE Versions[VERSIONS_NUMBER]; +} CODE_VERSIONS; + +typedef struct _COMMAND_FMT +{ + union + { + UINT32 val; + struct + { + UINT32 Operation : 23; + UINT32 IsResponse : 1; + UINT32 Class : 8; + } fields; + } cmd; + +} COMMAND_FMT; + +typedef struct _AMT_ANSI_STRING +{ + UINT16 Length; + CHAR *Buffer; +} AMT_ANSI_STRING; + +typedef struct _PTHI_MESSAGE_HEADER +{ + PTHI_VERSION Version; + UINT16 Reserved; + COMMAND_FMT Command; + UINT32 Length; +} PTHI_MESSAGE_HEADER; + +typedef struct _PTHI_RESPONSE_MESSAGE_HEADER +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; +} PTHI_RESPONSE_MESSAGE_HEADER; + +typedef struct _CFG_GET_CODE_VERSIONS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + CODE_VERSIONS CodeVersions; +} CFG_GET_CODE_VERSIONS_RESPONSE; + +typedef struct _CFG_GET_PROVISIONING_MODE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + CFG_PROVISIONING_MODE ProvisioningMode; + AMT_BOOLEAN LegacyMode; +} CFG_GET_PROVISIONING_MODE_RESPONSE; + +typedef struct _CFG_GET_PROVISIONING_STATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_PROVISIONING_STATE ProvisioningState; +} CFG_GET_PROVISIONING_STATE_RESPONSE; + +typedef struct _CFG_GET_MAC_ADDRESSES_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT8 DedicatedMac[6]; + UINT8 HostMac[6]; +} CFG_GET_MAC_ADDRESSES_RESPONSE; + +typedef struct _CFG_GET_FEATURES_STATE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 RequestID; +} CFG_GET_FEATURES_STATE_REQUEST; + +typedef struct _GET_FEATURES_REDIRECTION_SESSION_STATUS +{ + AMT_BOOLEAN IderOpen; + AMT_BOOLEAN SolOpen; + AMT_BOOLEAN Reserved; +} GET_FEATURES_REDIRECTION_SESSION_STATUS; + +typedef struct _GET_FEATURES_SYSTEM_DEFENSE_STATE_RESPONSE +{ + AMT_BOOLEAN SystemDefenseActivated; +} GET_FEATURES_SYSTEM_DEFENSE_STATUS_RESPONSE; + +typedef struct _GET_FEATURES_WEB_UI_STATE_RESPONSE +{ + AMT_BOOLEAN WebUiEnabled; +} GET_FEATURES_WEB_UI_STATUS_RESPONSE; + +typedef union _FEATURES_STATUS_DATA +{ + GET_FEATURES_REDIRECTION_SESSION_STATUS rs; + GET_FEATURES_SYSTEM_DEFENSE_STATUS_RESPONSE sd; + GET_FEATURES_WEB_UI_STATUS_RESPONSE webUI; +} FEATURES_STATUS_DATA; + +typedef struct _CFG_GET_FEATURES_STATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT32 RequestID; + FEATURES_STATUS_DATA Data; +} CFG_GET_FEATURES_STATE_RESPONSE; + +typedef struct _CFG_GET_CURRENT_POWER_POLICY_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_CURRENT_POWER_POLICY_REQUEST; + +typedef struct _CFG_GET_CURRENT_POWER_POLICY_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_ANSI_STRING PolicyName; +} CFG_GET_CURRENT_POWER_POLICY_RESPONSE; + +typedef struct _CFG_GET_LAST_HOST_RESET_REASON_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_LAST_HOST_RESET_REASON_REQUEST; + +typedef struct _CFG_GET_LAST_HOST_RESET_REASON_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT32 Reason; + UINT32 RemoteControlTimeStamp; +} CFG_GET_LAST_HOST_RESET_REASON_RESPONSE; + +typedef struct _LAN_SETTINGS +{ + AMT_BOOLEAN Enabled; + CFG_IPv4_ADDRESS Ipv4Address; + AMT_BOOLEAN DhcpEnabled; + UINT8 DhcpIpMode; + UINT8 LinkStatus; + UINT8 MacAddress[6]; +} LAN_SETTINGS; + +typedef struct _CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 InterfaceIndex; +} CFG_GET_LAN_INTERFACE_SETTINGS_REQUEST; + +typedef struct _CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_BOOLEAN Enabled; + CFG_IPv4_ADDRESS Ipv4Address; + AMT_BOOLEAN DhcpEnabled; + UINT8 DhcpIpMode; + UINT8 LinkStatus; + UINT8 MacAddress[6]; +} CFG_GET_LAN_INTERFACE_SETTINGS_RESPONSE; + +typedef struct _CFG_GET_SECURITY_PARAMETERS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_SECURITY_PARAMETERS_REQUEST; + +typedef struct _CFG_GET_SECURITY_PARAMETERS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_BOOLEAN EnterpriseMode; + AMT_BOOLEAN TLSEnabled; + AMT_BOOLEAN HWCryptoEnabled; + AMT_PROVISIONING_STATE ProvisioningState; + AMT_BOOLEAN NetworkInterfaceEnabled; + AMT_BOOLEAN SOLEnabled; + AMT_BOOLEAN IDEREnabled; + AMT_BOOLEAN FWUpdateEnabled; + AMT_BOOLEAN LinkIsUp; + AMT_BOOLEAN Reserved[8]; +} CFG_GET_SECURITY_PARAMETERS_RESPONSE; + +typedef struct _CFG_GET_DNS_SUFFIX_LIST_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_DNS_SUFFIX_LIST_REQUEST; + +typedef struct _CFG_GET_DNS_SUFFIX_LIST_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + UINT16 DataLength; + UINT8 Data[0]; +} CFG_GET_DNS_SUFFIX_LIST_RESPONSE; + +/** + * CFG_SET_ENTERPRISE_ACCESS_REQUEST + * + * Flags Bit 0 - If this bit is set then HostIPAddress is IPv6, otherwise HostIPAddress is IPv4 address. + * Bits 1..7 - Reserved, should be zero. + * HostIPAddress IPv4 / IPv6 address + * EnterpriseAccess 1 if LMS has access to enterprise network, otherwise 0. + */ +typedef struct _CFG_SET_ENTERPRISE_ACCESS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT8 Flags; + UINT8 HostIPAddress[16]; + UINT8 EnterpriseAccess; +} CFG_SET_ENTERPRISE_ACCESS_REQUEST; + +typedef struct _CFG_SET_ENTERPRISE_ACCESS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_SET_ENTERPRISE_ACCESS_RESPONSE; + +typedef struct _CFG_OPEN_USER_INITIATED_CONNECTION_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_OPEN_USER_INITIATED_CONNECTION_REQUEST; + +typedef struct _CFG_OPEN_USER_INITIATED_CONNECTION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_OPEN_USER_INITIATED_CONNECTION_RESPONSE; + +typedef struct _CFG_CLOSE_USER_INITIATED_CONNECTION_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_CLOSE_USER_INITIATED_CONNECTION_REQUEST; + +typedef struct _CFG_CLOSE_USER_INITIATED_CONNECTION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_CLOSE_USER_INITIATED_CONNECTION_RESPONSE; + +typedef enum +{ + AMT_NETWORK_CONNECTION_DIRECT = 0, + AMT_NETWORK_CONNECTION_VPN, + AMT_NETWORK_CONNECTION_OUTSIDE_ENTERPRISE +} AMT_NETWORK_CONNECTION_STATUS; + +typedef enum +{ + REMOTE_ACCESS_CONNECTION_STATUS_NOT_CONNECTED = 0, + REMOTE_ACCESS_CONNECTION_STATUS_CONNECTING, + REMOTE_ACCESS_CONNECTION_STATUS_CONNECTED +} REMOTE_ACCESS_CONNECTION_STATUS; + +typedef enum +{ + REMOTE_ACCESS_CONNECTION_TRIGGER_USER_INITIATED = 0, + REMOTE_ACCESS_CONNECTION_TRIGGER_ALERT, + REMOTE_ACCESS_CONNECTION_TRIGGER_PROVISIONING, + REMOTE_ACCESS_CONNECTION_TRIGGER_PERIODIC +} REMOTE_ACCESS_CONNECTION_TRIGGER; + +typedef struct _CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST; + +typedef struct _CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_NETWORK_CONNECTION_STATUS AmtNetworkConnectionStatus; + REMOTE_ACCESS_CONNECTION_STATUS RemoteAccessConnectionStatus; + REMOTE_ACCESS_CONNECTION_TRIGGER RemoteAccessConnectionTrigger; + AMT_ANSI_STRING MpsHostname; +} CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE; + +typedef struct _REMOTE_ACCESS_STATUS +{ + AMT_NETWORK_CONNECTION_STATUS AmtNetworkConnectionStatus; + REMOTE_ACCESS_CONNECTION_STATUS RemoteAccessConnectionStatus; + REMOTE_ACCESS_CONNECTION_TRIGGER RemoteAccessConnectionTrigger; + AMT_ANSI_STRING MpsHostname; +} REMOTE_ACCESS_STATUS; + + +typedef UINT8 AMT_UUID[16]; + +//const AMT_UUID AMT_UUID_LINK_STATE; + +typedef struct _STATE_DATA +{ + UINT8 LinkStatus; // (0 - down; 1 - up) + UINT8 HardSKU; + UINT8 CryptoFuse; // (0 - disabled; 1 - enabled) + UINT8 FlashProtaction; // (0 - disabled; 1 - enabled) + UINT8 LastMEResetReason; +} STATE_DATA; + +typedef struct _STATE_GET_AMT_STATE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + AMT_UUID StateVariableIdentifier; +} STATE_GET_AMT_STATE_REQUEST; + +typedef struct _STATE_GET_AMT_STATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_UUID StateDataIdentifier; + UINT32 ByteCount; + STATE_DATA StateData; +} STATE_GET_AMT_STATE_RESPONSE; + + +typedef struct _AMT_HASH_HANDLES +{ + UINT32 Length; + UINT32 Handles[CERT_HASH_MAX_NUMBER]; +} AMT_HASH_HANDLES; + +typedef struct _CERTHASH_ENTRY +{ + AMT_BOOLEAN IsDefault; + AMT_BOOLEAN IsActive; + UINT8 CertificateHash[CERT_HASH_MAX_LENGTH]; + UINT8 HashAlgorithm; + AMT_ANSI_STRING Name; +} CERTHASH_ENTRY; + +typedef enum +{ + CERT_HASH_ALGORITHM_MD5 = 0, // 16 bytes + CERT_HASH_ALGORITHM_SHA1, // 20 bytes + CERT_HASH_ALGORITHM_SHA256, // 32 bytes + CERT_HASH_ALGORITHM_SHA512, // 64 bytes +} CERT_HASH_ALGORITHM; + +typedef struct +{ + UINT16 Year; + UINT16 Month; + UINT16 DayOfWeek; + UINT16 Day; + UINT16 Hour; + UINT16 Minute; + UINT16 Second; +} TIME_DATE; + +typedef struct _AMT_PROV_AUDIT_RECORD +{ + UINT8 ProvisioningTLSMode; + AMT_BOOLEAN SecureDNS; + AMT_BOOLEAN HostInitiated; + CERT_HASH_ALGORITHM SelectedHashType; + UINT8 SelectedHashData[CERT_HASH_MAX_LENGTH]; + UINT8 CaCertificateSerials[NET_TLS_CERT_PKI_MAX_SERIAL_NUMS*NET_TLS_CERT_PKI_MAX_SERIAL_NUM_LENGTH]; + AMT_BOOLEAN AdditionalCaSerialNums; + AMT_BOOLEAN IsOemDefault; + AMT_BOOLEAN IsTimeValid; + UINT32 ProvServerIP; + TIME_DATE TlsStartTime; + AMT_ANSI_STRING ProvServerFQDN; +} AMT_PROV_AUDIT_RECORD; + +typedef struct _CFG_GENERATE_RNG_SEED_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_GENERATE_RNG_SEED_RESPONSE; + +typedef struct _CFG_GET_RNG_SEED_STATUS_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_RNG_STATUS RngStatus; +} CFG_GET_RNG_SEED_STATUS_RESPONSE; + +typedef struct _CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_BOOLEAN ZeroTouchEnabled; +} CFG_GET_ZERO_TOUCH_ENABLED_RESPONSE; + +typedef struct _CFG_GET_PROVISIONING_TLS_MODE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_PROVISIONING_TLS_MODE ProvisioningTlsMode; +} CFG_GET_PROVISIONING_TLS_MODE_RESPONSE; + +typedef struct _CFG_START_CONFIGURATION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_START_CONFIGURATION_RESPONSE; + +typedef struct _CFG_STOP_CONFIGURATION_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_STOP_CONFIGURATION_RESPONSE; + +typedef struct _CFG_SET_PROVISIONING_SERVER_OTP_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_SET_PROVISIONING_SERVER_OTP_RESPONSE; + +typedef struct _CFG_SET_DNS_SUFFIX_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; +} CFG_SET_DNS_SUFFIX_RESPONSE; + +typedef struct _CFG_GET_HASH_HANDLES_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_HASH_HANDLES HashHandles; +} CFG_GET_HASH_HANDLES_RESPONSE; + +typedef struct _CFG_GET_CERTHASH_ENTRY_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 HashHandle; +} CFG_GET_CERTHASH_ENTRY_REQUEST; + +typedef struct _CFG_GET_CERTHASH_ENTRY_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + CERTHASH_ENTRY Hash; +} CFG_GET_CERTHASH_ENTRY_RESPONSE; + +typedef struct _CFG_GET_PKI_FQDN_SUFFIX_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_ANSI_STRING Suffix; +} CFG_GET_PKI_FQDN_SUFFIX_RESPONSE; + +typedef struct CFG_SET_HOST_FQDN_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT16 FQDNL; + char FQDN[256]; +}CFG_SET_HOST_FQDN_REQUEST; + +typedef struct _CFG_SET_HOST_FQDN_RESPONSE +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; +} CFG_SET_HOST_FQDN_RESPONSE; + +typedef struct _CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; + LOCAL_SYSTEM_ACCOUNT Account; + } CFG_GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE; + +typedef struct _CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT8 Reserved[40]; +} CFG_GET_LOCAL_SYSTEM_ACCOUNT_REQUEST; + +typedef struct _CFG_UNPROVISION_RESPONSE +{ + PTHI_MESSAGE_HEADER Header; + AMT_STATUS Status; + } CFG_UNPROVISION_RESPONSE; + +typedef struct _CFG_UNPROVISION_REQUEST +{ + PTHI_MESSAGE_HEADER Header; + UINT32 Mode; +} CFG_UNPROVISION_REQUEST; + +typedef struct _CFG_GETEHBPSTATE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GETEHBPSTATE_REQUEST; + +typedef struct _CFG_GETEHBPSTATE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_EHBC_STATE EHBCState; +} CFG_GETEHBPSTATE_RESPONSE; + +typedef struct _CFG_GET_CONTROL_MODE_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_CONTROL_MODE_REQUEST; + +typedef struct _CFG_GET_CONTROL_MODE_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + int state; +} CFG_GET_CONTROL_MODE_RESPONSE; + +typedef struct _CFG_GET_UUID_REQUEST +{ + PTHI_MESSAGE_HEADER Header; +} CFG_GET_UUID_REQUEST; + +typedef struct _CFG_GET_UUID_RESPONSE +{ + PTHI_RESPONSE_MESSAGE_HEADER Header; + AMT_UUID UUID; +} CFG_GET_UUID_RESPONSE; + +#pragma pack() + + +AMT_STATUS pthi_GetCodeVersions(CODE_VERSIONS *codeVersions); +AMT_STATUS pthi_GetProvisioningMode(CFG_PROVISIONING_MODE *provisioningMode, AMT_BOOLEAN *legacy); +AMT_STATUS pthi_GetProvisioningState(AMT_PROVISIONING_STATE *state); +AMT_STATUS pthi_GetMacAddresses(UINT8 DedicatedMac[6], UINT8 HostMac[6]); +AMT_STATUS pthi_GetFeaturesState(UINT32 requestID, AMT_BOOLEAN (*requestStatus)[2]); +AMT_STATUS pthi_GetLastHostResetReason(UINT32 *Reason, UINT32 *RemoteControlTimeStamp); +AMT_STATUS pthi_GetCurrentPowerPolicy(AMT_ANSI_STRING *policyName); +AMT_STATUS pthi_GetLanInterfaceSettings(UINT32 interfaceSettings, LAN_SETTINGS *lanSettings); +AMT_STATUS pthi_GetHeciVersion(HECI_VERSION *hecVersion); +AMT_STATUS pthi_GetTLSEnabled(AMT_BOOLEAN *tlsEnabled); +//AMT_STATUS pthi_GetDNSSuffixList(std::list *dnsSuffixList); +AMT_STATUS pthi_SetEnterpriseAccess(UINT8 Flags, UINT8 HostIPAddress[16], UINT8 EnterpriseAccess); +AMT_STATUS pthi_GetFWResetReason(UINT8 *MEResetReason); +AMT_STATUS pthi_OpenUserInitiatedConnection(); +AMT_STATUS pthi_CloseUserInitiatedConnection(); +AMT_STATUS pthi_GetRemoteAccessConnectionStatus(REMOTE_ACCESS_STATUS *remoteAccessStatus); +AMT_STATUS pthi_GenerateRngKey(); +AMT_STATUS pthi_GetRngSeedStatus(AMT_RNG_STATUS *rngStatus); +AMT_STATUS pthi_GetZeroTouchEnabled(AMT_BOOLEAN *zeroTouchEnabled); +AMT_STATUS pthi_GetProvisioningTlsMode(AMT_PROVISIONING_TLS_MODE *provisioningTlsMode); +AMT_STATUS pthi_StartConfiguration(); +AMT_STATUS pthi_StopConfiguration(); +AMT_STATUS pthi_SetProvisioningServerOTP(AMT_ANSI_STRING passwordOTP); +AMT_STATUS pthi_SetDnsSuffix(AMT_ANSI_STRING dnsSuffix); +AMT_STATUS pthi_EnumerateHashHandles(AMT_HASH_HANDLES *hashHandles); +AMT_STATUS pthi_GetCertificateHashEntry(UINT32 hashHandle, CERTHASH_ENTRY *hashEntry); +AMT_STATUS pthi_GetDnsSuffix(AMT_ANSI_STRING *dnsSuffix); +AMT_STATUS pthi_SetHostFQDN(char* str); +AMT_STATUS pthi_GetLocalSystemAccount(LOCAL_SYSTEM_ACCOUNT *localAccount); +AMT_STATUS pthi_Unprovision(CFG_PROVISIONING_MODE provisionMode); +AMT_STATUS pthi_GetStateEHBC(AMT_EHBC_STATE *state); +AMT_STATUS pthi_GetControlMode(int *state); +AMT_STATUS pthi_GetUUID(AMT_UUID *uuid); + +#define PROVISIONING_MODE_REQUEST 0x04000008 +#define PROVISIONING_MODE_RESPONSE 0x04800008 +const PTHI_MESSAGE_HEADER GET_PROVISIONING_MODE_HEADER; + +#define UNPROVISION_REQUEST 0x04000010 +#define UNPROVISION_RESPONSE 0x04800010 +const PTHI_MESSAGE_HEADER UNPROVISION_HEADER; + +#define PROVISIONING_STATE_REQUEST 0x04000011 +#define PROVISIONING_STATE_RESPONSE 0x04800011 +const PTHI_MESSAGE_HEADER GET_PROVISIONING_STATE_HEADER; + +#define CODE_VERSIONS_REQUEST 0x0400001A +#define CODE_VERSIONS_RESPONSE 0x0480001A +const PTHI_MESSAGE_HEADER GET_CODE_VERSION_HEADER; + +#define GET_SECURITY_PARAMETERS_REQUEST 0x0400001B +#define GET_SECURITY_PARAMETERS_RESPONSE 0x0480001B +//const PTHI_MESSAGE_HEADER GET_SECURITY_PARAMETERS_HEADER; + +#define GET_MAC_ADDRESSES_REQUEST 0x04000025 +#define GET_MAC_ADDRESSES_RESPONSE 0x04800025 +const PTHI_MESSAGE_HEADER GET_MAC_ADDRESSES_HEADER; + +#define GENERATE_RNG_SEED_REQUEST 0x04000028 +#define GENERATE_RNG_SEED_RESPONSE 0x04800028 +//const PTHI_MESSAGE_HEADER GENERATE_RNG_SEED_HEADER; + +#define SET_PROVISIONING_SERVER_OTP_REQUEST 0x0400002A +#define SET_PROVISIONING_SERVER_OTP_RESPONSE 0x0480002A + +#define SET_DNS_SUFFIX_REQUEST 0x0400002F +#define SET_DNS_SUFFIX_RESPONSE 0x0480002F + +#define ENUMERATE_HASH_HANDLES_REQUEST 0x0400002C +#define ENUMERATE_HASH_HANDLES_RESPONSE 0x0480002C +//const PTHI_MESSAGE_HEADER ENUMERATE_HASH_HANDLES_HEADER; + +#define GET_RNG_SEED_STATUS_REQUEST 0x0400002E +#define GET_RNG_SEED_STATUS_RESPONSE 0x0480002E +//const PTHI_MESSAGE_HEADER GET_RNG_SEED_STATUS_HEADER; + +#define GET_DNS_SUFFIX_LIST_REQUEST 0x0400003E +#define GET_DNS_SUFFIX_LIST_RESPONSE 0x0480003E +//const PTHI_MESSAGE_HEADER GET_DNS_SUFFIX_LIST_HEADER; + +#define SET_ENTERPRISE_ACCESS_REQUEST 0x0400003F +#define SET_ENTERPRISE_ACCESS_RESPONSE 0x0480003F +//const PTHI_MESSAGE_HEADER SET_ENTERPRISE_ACCESS_HEADER; + +#define OPEN_USER_INITIATED_CONNECTION_REQUEST 0x04000044 +#define OPEN_USER_INITIATED_CONNECTION_RESPONSE 0x04800044 +//const PTHI_MESSAGE_HEADER OPEN_USER_INITIATED_CONNECTION_HEADER; + +#define CLOSE_USER_INITIATED_CONNECTION_REQUEST 0x04000045 +#define CLOSE_USER_INITIATED_CONNECTION_RESPONSE 0x04800045 +//const PTHI_MESSAGE_HEADER CLOSE_USER_INITIATED_CONNECTION_HEADER; + +#define GET_REMOTE_ACCESS_CONNECTION_STATUS_REQUEST 0x04000046 +#define GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE 0x04800046 +//const PTHI_MESSAGE_HEADER GET_REMOTE_ACCESS_CONNECTION_STATUS_HEADER; + +#define GET_CURRENT_POWER_POLICY_REQUEST 0x04000047 +#define GET_CURRENT_POWER_POLICY_RESPONSE 0x04800047 +const PTHI_MESSAGE_HEADER GET_CURRENT_POWER_POLICY_HEADER; + +#define GET_LAN_INTERFACE_SETTINGS_REQUEST 0x04000048 +#define GET_LAN_INTERFACE_SETTINGS_RESPONSE 0x04800048 +//const PTHI_MESSAGE_HEADER GET_LAN_INTERFACE_SETTINGS_HEADER; + +#define GET_FEATURES_STATE_REQUEST 0x04000049 +#define GET_FEATURES_STATE_RESPONSE 0x04800049 +const PTHI_MESSAGE_HEADER GET_FEATURES_STATE_HEADER; + +#define GET_LAST_HOST_RESET_REASON_REQUEST 0x0400004A +#define GET_LAST_HOST_RESET_REASON_RESPONSE 0x0480004A +//const PTHI_MESSAGE_HEADER GET_LAST_HOST_RESET_REASON_HEADER; + +#define GET_AMT_STATE_REQUEST 0x01000001 +#define GET_AMT_STATE_RESPONSE 0x01800001 +//const PTHI_MESSAGE_HEADER GET_AMT_STATE_HEADER; + +#define GET_ZERO_TOUCH_ENABLED_REQUEST 0x04000030 +#define GET_ZERO_TOUCH_ENABLED_RESPONSE 0x04800030 +//const PTHI_MESSAGE_HEADER GET_ZERO_TOUCH_ENABLED_HEADER; + +#define GET_PROVISIONING_TLS_MODE_REQUEST 0x0400002B +#define GET_PROVISIONING_TLS_MODE_RESPONSE 0x0480002B +//const PTHI_MESSAGE_HEADER GET_PROVISIONING_TLS_MODE_HEADER; + +#define START_CONFIGURATION_REQUEST 0x04000029 +#define START_CONFIGURATION_RESPONSE 0x04800029 +//const PTHI_MESSAGE_HEADER START_CONFIGURATION_HEADER; + +#define GET_CERTHASH_ENTRY_REQUEST 0x0400002D +#define GET_CERTHASH_ENTRY_RESPONSE 0x0480002D +//const PTHI_MESSAGE_HEADER GET_CERTHASH_ENTRY_HEADER; + +#define GET_PKI_FQDN_SUFFIX_REQUEST 0x04000036 +#define GET_PKI_FQDN_SUFFIX_RESPONSE 0x04800036 +// + +#define SET_HOST_FQDN_REQUEST 0x0400005b +#define SET_HOST_FQDN_RESPONSE 0x0480005b +//const PTHI_MESSAGE_HEADER GET_PKI_FQDN_SUFFIX_HEADER; + +#define GET_LOCAL_SYSTEM_ACCOUNT_REQUEST 0x04000067 +#define GET_LOCAL_SYSTEM_ACCOUNT_RESPONSE 0x04800067 +//const PTHI_MESSAGE_HEADER GET_LOCAL_SYSTEM_ACCOUNT_HEADER; + +#define GET_EHBC_STATE_REQUEST 0x4000084 +#define GET_EHBC_STATE_RESPONSE 0x4800084 + +const PTHI_MESSAGE_HEADER GET_EHBC_STATE_HEADER; + +#define GET_CONTROL_MODE_REQUEST 0x400006b +#define GET_CONTROL_MODE_RESPONSE 0x480006b +const PTHI_MESSAGE_HEADER GET_CONTROL_MODE_HEADER; + +#define STOP_CONFIGURATION_REQUEST 0x400005e +#define STOP_CONFIGURATION_RESPONSE 0x480005e + +#define GET_UUID_REQUEST 0x400005c +#define GET_UUID_RESPONSE 0x480005c + +#endif + +#endif diff --git a/MicroLMS/heci/StatusCodeDefinitions.h b/MicroLMS/heci/StatusCodeDefinitions.h new file mode 100644 index 0000000..c903f97 --- /dev/null +++ b/MicroLMS/heci/StatusCodeDefinitions.h @@ -0,0 +1,403 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef _MINCORE + +//---------------------------------------------------------------------------- +// +// File: StatusCodeDefinitions.h +// +// Notes: This file contains the definitions of the status codes +// as defined in the Intel® AMT Network Design Guide. +// +//---------------------------------------------------------------------------- + +#ifndef STATUS_CODE_DEFINITIONS_H +#define STATUS_CODE_DEFINITIONS_H + +typedef unsigned int PT_STATUS; +//typedef unsigned int AMT_STATUS; + +//Request succeeded +#define PT_STATUS_SUCCESS 0x0 +#define AMT_STATUS_SUCCESS 0x0 + +//An internal error in the Intel® AMT device has occurred +#define PT_STATUS_INTERNAL_ERROR 0x1 +#define AMT_STATUS_INTERNAL_ERROR 0x1 + +//Intel® AMT device has not progressed far enough in its +//initialization to process the command. +#define PT_STATUS_NOT_READY 0x2 +#define AMT_STATUS_NOT_READY 0x2 + +//Command is not permitted in current operating mode. +#define PT_STATUS_INVALID_PT_MODE 0x3 +#define AMT_STATUS_INVALID_PT_MODE 0x3 + +//Length field of header is invalid. +#define PT_STATUS_INVALID_MESSAGE_LENGTH 0x4 +#define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4 + +//The requested hardware asset inventory table +//checksum is not available. +#define PT_STATUS_TABLE_FINGERPRINT_NOT_AVAILABLE 0x5 +#define AMT_STATUS_TABLE_FINGERPRINT_NOT_AVAILABLE 0x5 + +//The Integrity Check Value field of the request +//message sent by Intel® AMT enabled device is invalid. +#define PT_STATUS_INTEGRITY_CHECK_FAILED 0x6 +#define AMT_STATUS_INTEGRITY_CHECK_FAILED 0x6 + +//The specified ISV version is not supported +#define PT_STATUS_UNSUPPORTED_ISVS_VERSION 0x7 +#define AMT_STATUS_UNSUPPORTED_ISVS_VERSION 0x7 + +//The specified queried application is not registered. +#define PT_STATUS_APPLICATION_NOT_REGISTERED 0x8 +#define AMT_STATUS_APPLICATION_NOT_REGISTERED 0x8 + +//Either an invalid name or a not previously registered +//“Enterprise” name was specified +#define PT_STATUS_INVALID_REGISTRATION_DATA 0x9 +#define AMT_STATUS_INVALID_REGISTRATION_DATA 0x9 + +//The application handle provided in the request +//message has never been allocated. +#define PT_STATUS_APPLICATION_DOES_NOT_EXIST 0xA +#define AMT_STATUS_APPLICATION_DOES_NOT_EXIST 0xA + +//The requested number of bytes cannot be allocated in ISV storage. +#define PT_STATUS_NOT_ENOUGH_STORAGE 0xB +#define AMT_STATUS_NOT_ENOUGH_STORAGE 0xB + +//The specified name is invalid. +#define PT_STATUS_INVALID_NAME 0xC +#define AMT_STATUS_INVALID_NAME 0xC + +//The specified block does not exist. +#define PT_STATUS_BLOCK_DOES_NOT_EXIST 0xD +#define AMT_STATUS_BLOCK_DOES_NOT_EXIST 0xD + +//The specified byte offset is invalid. +#define PT_STATUS_INVALID_BYTE_OFFSET 0xE +#define AMT_STATUS_INVALID_BYTE_OFFSET 0xE + +//The specified byte count is invalid. +#define PT_STATUS_INVALID_BYTE_COUNT 0xF +#define AMT_STATUS_INVALID_BYTE_COUNT 0xF + +//The requesting application is not +//permitted to request execution of the specified operation. +#define PT_STATUS_NOT_PERMITTED 0x10 +#define AMT_STATUS_NOT_PERMITTED 0x10 + +//The requesting application is not the owner of the block +//as required for the requested operation. +#define PT_STATUS_NOT_OWNER 0x11 +#define AMT_STATUS_NOT_OWNER 0x11 + +//The specified block is locked by another application. +#define PT_STATUS_BLOCK_LOCKED_BY_OTHER 0x12 +#define AMT_STATUS_BLOCK_LOCKED_BY_OTHER 0x12 + +//The specified block is not locked. +#define PT_STATUS_BLOCK_NOT_LOCKED 0x13 +#define AMT_STATUS_BLOCK_NOT_LOCKED 0x13 + +//The specified group permission bits are invalid. +#define PT_STATUS_INVALID_GROUP_PERMISSIONS 0x14 +#define AMT_STATUS_INVALID_GROUP_PERMISSIONS 0x14 + +//The specified group does not exist. +#define PT_STATUS_GROUP_DOES_NOT_EXIST 0x15 +#define AMT_STATUS_GROUP_DOES_NOT_EXIST 0x15 + +//The specified member count is invalid. +#define PT_STATUS_INVALID_MEMBER_COUNT 0x16 +#define AMT_STATUS_INVALID_MEMBER_COUNT 0x16 + +//The request cannot be satisfied because a maximum +//limit associated with the request has been reached. +#define PT_STATUS_MAX_LIMIT_REACHED 0x17 +#define AMT_STATUS_MAX_LIMIT_REACHED 0x17 + +//specified key algorithm is invalid. +#define PT_STATUS_INVALID_AUTH_TYPE 0x18 +#define AMT_STATUS_INVALID_AUTH_TYPE 0x18 + +//Not Used +#define PT_STATUS_AUTHENTICATION_FAILED 0x19 +#define AMT_STATUS_AUTHENTICATION_FAILED 0x19 + +//The specified DHCP mode is invalid. +#define PT_STATUS_INVALID_DHCP_MODE 0x1A +#define AMT_STATUS_INVALID_DHCP_MODE 0x1A + +//The specified IP address is not a valid IP unicast address. +#define PT_STATUS_INVALID_IP_ADDRESS 0x1B +#define AMT_STATUS_INVALID_IP_ADDRESS 0x1B + +//The specified domain name is not a valid domain name. +#define PT_STATUS_INVALID_DOMAIN_NAME 0x1C +#define AMT_STATUS_INVALID_DOMAIN_NAME 0x1C + +//Not Used +#define PT_STATUS_UNSUPPORTED_VERSION 0x1D +#define AMT_STATUS_UNSUPPORTED_VERSION 0x1D + +//The requested operation cannot be performed because a +//prerequisite request message has not been received. +#define PT_STATUS_REQUEST_UNEXPECTED 0x1E +#define AMT_STATUS_REQUEST_UNEXPECTED 0x1E + +//Not Used +#define PT_STATUS_INVALID_TABLE_TYPE 0x1F +#define AMT_STATUS_INVALID_TABLE_TYPE 0x1F + +//The specified provisioning mode code is undefined. +#define PT_STATUS_INVALID_PROVISIONING_STATE 0x20 +#define AMT_STATUS_INVALID_PROVISIONING_STATE 0x20 + +//Not Used +#define PT_STATUS_UNSUPPORTED_OBJECT 0x21 +#define AMT_STATUS_UNSUPPORTED_OBJECT 0x21 + +//The specified time was not accepted by the Intel® AMT device +//since it is earlier than the baseline time set for the device. +#define PT_STATUS_INVALID_TIME 0x22 +#define AMT_STATUS_INVALID_TIME 0x22 + +//StartingIndex is invalid. +#define PT_STATUS_INVALID_INDEX 0x23 +#define AMT_STATUS_INVALID_INDEX 0x23 + +//A parameter is invalid. +#define PT_STATUS_INVALID_PARAMETER 0x24 +#define AMT_STATUS_INVALID_PARAMETER 0x24 + +//An invalid netmask was supplied +//(a valid netmask is an IP address in which all ‘1’s are before +//the ‘0’ – e.g. FFFC0000h is valid, FF0C0000h is invalid). +#define PT_STATUS_INVALID_NETMASK 0x25 +#define AMT_STATUS_INVALID_NETMASK 0x25 + +//The operation failed because the Flash wear-out +//protection mechanism prevented a write to an NVRAM sector. +#define PT_STATUS_FLASH_WRITE_LIMIT_EXCEEDED 0x26 +#define AMT_STATUS_FLASH_WRITE_LIMIT_EXCEEDED 0x26 + +//ME FW did not receive the entire image file. +#define PT_STATUS_INVALID_IMAGE_LENGTH 0x27 +#define AMT_STATUS_INVALID_IMAGE_LENGTH 0x27 + +//ME FW received an image file with an invalid signature. +#define PT_STATUS_INVALID_IMAGE_SIGNATURE 0x28 +#define AMT_STATUS_INVALID_IMAGE_SIGNATURE 0x28 + +//LME can not support the requested version. +#define PT_STATUS_PROPOSE_ANOTHER_VERSION 0x29 +#define AMT_STATUS_PROPOSE_ANOTHER_VERSION 0x29 + +//The PID must be a 64 bit quantity made up of ASCII codes +//of some combination of 8 characters – +//capital alphabets (A–Z), and numbers (0–9). +#define PT_STATUS_INVALID_PID_FORMAT 0x2A +#define AMT_STATUS_INVALID_PID_FORMAT 0x2A + +//The PPS must be a 256 bit quantity made up of ASCII codes +//of some combination of 32 characters – +//capital alphabets (A–Z), and numbers (0–9). +#define PT_STATUS_INVALID_PPS_FORMAT 0x2B +#define AMT_STATUS_INVALID_PPS_FORMAT 0x2B + +//Full BIST test has been blocked +#define PT_STATUS_BIST_COMMAND_BLOCKED 0x2C +#define AMT_STATUS_BIST_COMMAND_BLOCKED 0x2C + +//A TCP/IP connection could not be opened on with the selected port. +#define PT_STATUS_CONNECTION_FAILED 0x2D +#define AMT_STATUS_CONNECTION_FAILED 0x2D + +//Max number of connection reached. +//LME can not open the requested connection. +#define PT_STATUS_CONNECTION_TOO_MANY 0x2E +#define AMT_STATUS_CONNECTION_TOO_MANY 0x2E + +//Random key generation is in progress. +#define PT_STATUS_RNG_GENERATION_IN_PROGRESS 0x2F +#define AMT_STATUS_RNG_GENERATION_IN_PROGRESS 0x2F + +//A randomly generated key does not exist. +#define PT_STATUS_RNG_NOT_READY 0x30 +#define AMT_STATUS_RNG_NOT_READY 0x30 + +//Self-generated AMT certificate does not exist. +#define PT_STATUS_CERTIFICATE_NOT_READY 0x31 +#define AMT_STATUS_CERTIFICATE_NOT_READY 0x31 + +//This code establishes a dividing line between +//status codes which are common to host interface and +//network interface and status codes which are used by +//network interface only. +#define PT_STATUS_NETWORK_IF_ERROR_BASE 0x800 +#define AMT_STATUS_NETWORK_IF_ERROR_BASE 0x800 + +//The OEM number specified in the remote control +//command is not supported by the Intel® AMT device +#define PT_STATUS_UNSUPPORTED_OEM_NUMBER 0x801 +#define AMT_STATUS_UNSUPPORTED_OEM_NUMBER 0x801 + +//The boot option specified in the remote control command +//is not supported by the Intel® AMT device +#define PT_STATUS_UNSUPPORTED_BOOT_OPTION 0x802 +#define AMT_STATUS_UNSUPPORTED_BOOT_OPTION 0x802 + +//The command specified in the remote control command +//is not supported by the Intel® AMT device +#define PT_STATUS_INVALID_COMMAND 0x803 +#define AMT_STATUS_INVALID_COMMAND 0x803 + +//The special command specified in the remote control command +//is not supported by the Intel® AMT device +#define PT_STATUS_INVALID_SPECIAL_COMMAND 0x804 +#define AMT_STATUS_INVALID_SPECIAL_COMMAND 0x804 + +//The handle specified in the command is invalid +#define PT_STATUS_INVALID_HANDLE 0x805 +#define AMT_STATUS_INVALID_HANDLE 0x805 + +//The password specified in the User ACL is invalid +#define PT_STATUS_INVALID_PASSWORD 0x806 +#define AMT_STATUS_INVALID_PASSWORD 0x806 + +//The realm specified in the User ACL is invalid +#define PT_STATUS_INVALID_REALM 0x807 +#define AMT_STATUS_INVALID_REALM 0x807 + +//The FPACL or EACL entry is used by an active +//registration and cannot be removed or modified. +#define PT_STATUS_STORAGE_ACL_ENTRY_IN_USE 0x808 +#define AMT_STATUS_STORAGE_ACL_ENTRY_IN_USE 0x808 + +//Essential data is missing on CommitChanges command. +#define PT_STATUS_DATA_MISSING 0x809 +#define AMT_STATUS_DATA_MISSING 0x809 + +//The parameter specified is a duplicate of an existing value. +//Returned for a case where duplicate entries are added to FPACL +//(Factory Partner Allocation Control List) or EACL +//(Enterprise Access Control List) lists. +#define PT_STATUS_DUPLICATE 0x80A +#define AMT_STATUS_DUPLICATE 0x80A + +//Event Log operation failed due to the current freeze status of the log. +#define PT_STATUS_EVENTLOG_FROZEN 0x80B +#define AMT_STATUS_EVENTLOG_FROZEN 0x80B + +//The device is missing private key material. +#define PT_STATUS_PKI_MISSING_KEYS 0x80C +#define AMT_STATUS_PKI_MISSING_KEYS 0x80C + +//The device is currently generating a keypair. +//Caller may try repeating this operation at a later time. +#define PT_STATUS_PKI_GENERATING_KEYS 0x80D +#define AMT_STATUS_PKI_GENERATING_KEYS 0x80D + +//An invalid Key was entered. +#define PT_STATUS_INVALID_KEY 0x80E +#define AMT_STATUS_INVALID_KEY 0x80E + +//An invalid X.509 certificate was entered. +#define PT_STATUS_INVALID_CERT 0x80F +#define AMT_STATUS_INVALID_CERT 0x80F + +//Certificate Chain and Private Key do not match. +#define PT_STATUS_CERT_KEY_NOT_MATCH 0x810 +#define AMT_STATUS_CERT_KEY_NOT_MATCH 0x810 + +//The request cannot be satisfied because the maximum +//number of allowed Kerberos domains has been reached. +//(The domain is determined by the first 24 Bytes of the SID.) +#define PT_STATUS_MAX_KERB_DOMAIN_REACHED 0x811 +#define AMT_STATUS_MAX_KERB_DOMAIN_REACHED 0x811 + +// The requested configuration is unsupported +#define PT_STATUS_UNSUPPORTED 0x812 +#define AMT_STATUS_UNSUPPORTED 0x812 + +// A profile with the requested priority already exists +#define PT_STATUS_INVALID_PRIORITY 0x813 +#define AMT_STATUS_INVALID_PRIORITY 0x813 + +// Unable to find specified element +#define PT_STATUS_NOT_FOUND 0x814 +#define AMT_STATUS_NOT_FOUND 0x814 + +// Invalid User credentials +#define PT_STATUS_INVALID_CREDENTIALS 0x815 +#define AMT_STATUS_INVALID_CREDENTIALS 0x815 + +// Passphrase is invalid +#define PT_STATUS_INVALID_PASSPHRASE 0x816 +#define AMT_STATUS_INVALID_PASSPHRASE 0x816 + +// A certificate handle must be chosen before the +// operation can be completed. +#define PT_STATUS_NO_ASSOCIATION 0x818 +#define AMT_STATUS_NO_ASSOCIATION 0x818 + +// The command is defined as Audit Log event and can not be +// logged. +#define PT_STATUS_AUDIT_FAIL 0x81B +#define AMT_STATUS_AUDIT_FAIL 0x81B + +// One of the ME components is not ready for unprovisioning. +#define PT_STATUS_BLOCKING_COMPONENT 0x81C +#define AMT_STATUS_BLOCKING_COMPONENT 0x81C + +//The application has identified an internal error +#define PTSDK_STATUS_INTERNAL_ERROR 0x1000 + +//An ISV operation was called while the library is not +//initialized +#define PTSDK_STATUS_NOT_INITIALIZED 0x1001 + +//The requested library I/F is not supported by the current library +//implementation. +#define PTSDK_STATUS_LIB_VERSION_UNSUPPORTED 0x1002 + +//One of the parameters is invalid (usually indicates a +//NULL pointer or an invalid session handle is specified) +#define PTSDK_STATUS_INVALID_PARAM 0x1003 + +//The SDK could not allocate sufficient resources to complete the operation. +#define PTSDK_STATUS_RESOURCES 0x1004 + +//The Library has identified a HW Internal error. +#define PTSDK_STATUS_HARDWARE_ACCESS_ERROR 0x1005 + +//The application that sent the request message is not registered. +//Usually indicates the registration timeout has elapsed. +//The caller should reregister with the Intel AMT enabled device. +#define PTSDK_STATUS_REQUESTOR_NOT_REGISTERED 0x1006 + +//A network error has occurred while processing the call. +#define PTSDK_STATUS_NETWORK_ERROR 0x1007 + +//Specified container can not hold the requested string +#define PTSDK_STATUS_PARAM_BUFFER_TOO_SHORT 0x1008 + +//For Windows only. +//ISVS_InitializeCOMinThread was not called by the current thread. +#define PTSDK_STATUS_COM_NOT_INITIALIZED_IN_THREAD 0x1009 + +//The URL parameter was not optional in current configuration. +#define PTSDK_STATUS_URL_REQUIRED 0x100A + +#endif + +#endif diff --git a/MicroLMS/heci/mei.h b/MicroLMS/heci/mei.h new file mode 100644 index 0000000..356d7cf --- /dev/null +++ b/MicroLMS/heci/mei.h @@ -0,0 +1,57 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef _LINUX_MEI_H +#define _LINUX_MEI_H +// #include +// #include +typedef struct { + unsigned char b[16]; +} uuid_le; +#define IOCTL_MEI_CONNECT_CLIENT _IOWR('H' , 0x01, struct mei_connect_client_data) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct mei_client { + unsigned int max_msg_length; + unsigned char protocol_version; + unsigned char reserved[3]; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +struct mei_connect_client_data { + union { + uuid_le in_client_uuid; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct mei_client out_client_properties; + }; +}; +#define IOCTL_MEI_SETUP_DMA_BUF _IOWR('H' , 0x02, struct mei_client_dma_data) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define IOCTL_MEI_UNSET_DMA_BUF _IOW('H' , 0x03, struct mei_client_dma_handle) +struct mei_client_dma_data { + union { + unsigned long userptr; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + }; + unsigned int length; + unsigned int handle; +}; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +struct mei_client_dma_handle { + unsigned int handle; +}; +#endif +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/MicroLMS/microstack/ILibAsyncServerSocket.c b/MicroLMS/microstack/ILibAsyncServerSocket.c new file mode 100644 index 0000000..91520d8 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncServerSocket.c @@ -0,0 +1,614 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#include +#include +#define SOCKET_ERROR (-1) +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#if defined(WINSOCK2) +#include +#include +#elif defined(WINSOCK1) +#include +#include +#endif + +#include "ILibParsers.h" +#include "ILibAsyncServerSocket.h" +#include "ILibAsyncSocket.h" + +#define DEBUGSTATEMENT(x) + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) + +typedef struct ILibAsyncServerSocketModule +{ + ILibChain_Link ChainLink; + + int MaxConnection; + void **AsyncSockets; + ILibServerScope scope; + + SOCKET ListenSocket; + unsigned short portNumber, initialPortNumber; + int listening; + int loopbackFlag; + + ILibAsyncServerSocket_OnReceive OnReceive; + ILibAsyncServerSocket_OnConnect OnConnect; + ILibAsyncServerSocket_OnDisconnect OnDisconnect; + ILibAsyncServerSocket_OnInterrupt OnInterrupt; + ILibAsyncServerSocket_OnSendOK OnSendOK; + + void *Tag; + int Tag2; + +}ILibAsyncServerSocketModule; +struct ILibAsyncServerSocket_Data +{ + struct ILibAsyncServerSocketModule *module; + ILibAsyncServerSocket_BufferReAllocated Callback; + void *user; +}; + +const int ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE = (const int)sizeof(ILibAsyncServerSocketModule); + + +/*! \fn ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +\brief Returns the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to query +\returns The user Tag +*/ +void *ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +{ + return (((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag); +} +/*! \fn ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag) +\brief Sets the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to save the tag to +\param tag The value to save +*/ +void ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag) +{ + ((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag = tag; +} + +/*! \fn ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +\brief Returns the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to query +\returns The user Tag +*/ +int ILibAsyncServerSocket_GetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule) +{ + return(((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag2); +} +/*! \fn ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *tag) +\brief Sets the user Tag associated with the AsyncServer +\param ILibAsyncSocketModule The ILibAsyncServerSocket to save the tag to +\param tag The value to save +*/ +void ILibAsyncServerSocket_SetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, int tag) +{ + ((struct ILibAsyncServerSocketModule*)ILibAsyncSocketModule)->Tag2 = tag; +} + +// +// Internal method called by ILibAsyncSocket, to signal an interrupt condition +// +// The ILibAsyncServerSocket that was interrupted +// The associated user tag +void ILibAsyncServerSocket_OnInterruptSink(ILibAsyncSocket_SocketModule socketModule, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + if (data == NULL) return; + if (data->module->OnInterrupt != NULL) data->module->OnInterrupt(data->module, socketModule, data->user); + free(user); +} +// +// Chain PreSelect handler +// +// +// +// +// +// +void ILibAsyncServerSocket_PreSelect(void* socketModule, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + struct ILibAsyncServerSocketModule *module = (struct ILibAsyncServerSocketModule*)socketModule; + int flags,i; + + UNREFERENCED_PARAMETER( writeset ); + UNREFERENCED_PARAMETER( errorset ); + UNREFERENCED_PARAMETER( blocktime ); + + // + // The socket isn't put in listening mode, until the chain is started. + // If this variable == 0, that means we need to do that. + // + if (module->listening == 0) + { + // + // Set the socket to non-block mode, so we can play nice and share the thread + // +#ifdef WIN32 + flags = 1; + if(ioctlsocket(module->ListenSocket, FIONBIO, (u_long *)(&flags)) == SOCKET_ERROR) ILIBCRITICALEXIT2(246, WSAGetLastError()); +#elif _POSIX + flags = fcntl(module->ListenSocket, F_GETFL,0); + fcntl(module->ListenSocket, F_SETFL, O_NONBLOCK | flags); +#endif + + // + // Put the socket in Listen, and add it to the fdset for the Select loop + // + module->listening = 1; + listen(module->ListenSocket, 4); + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->ListenSocket, readset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + else if (module->ListenSocket != ~0) + { + // Only put the ListenSocket in the readset, if we are able to handle a new socket + for(i = 0; i < module->MaxConnection; ++i) + { + if (ILibAsyncSocket_IsFree(module->AsyncSockets[i]) != 0) + { + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->ListenSocket, readset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + break; + } + } + } +} +/*! \fn ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback) +\brief Set the callback handler for when the internal data buffer has been resized +\param AsyncServerSocketToken The ILibAsyncServerSocket to query +\param ConnectionToken The specific connection to set the callback with +\param Callback The callback handler to set +*/ +void ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)ILibAsyncSocket_GetUser(ConnectionToken); + UNREFERENCED_PARAMETER( AsyncServerSocketToken ); + if (data != NULL) data->Callback = Callback; +} + +// +// Chain PostSelect handler +// +// +// +// +// +// +void ILibAsyncServerSocket_PostSelect(void* socketModule, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset) +{ + struct ILibAsyncServerSocket_Data *data; + struct sockaddr_in6 addr; +#ifdef _POSIX + socklen_t addrlen; +#else + int addrlen; +#endif + + struct ILibAsyncServerSocketModule *module = (struct ILibAsyncServerSocketModule*)socketModule; + int i,flags; +#ifdef WIN32 + SOCKET NewSocket; +#elif defined( _POSIX) + int NewSocket; +#endif + + UNREFERENCED_PARAMETER( slct ); + UNREFERENCED_PARAMETER( writeset ); + UNREFERENCED_PARAMETER( errorset ); + + if (FD_ISSET(module->ListenSocket, readset) != 0) + { + // + // There are pending TCP connection requests + // + for(i = 0; i < module->MaxConnection; ++i) + { + // + // Check to see if we have available resources to handle this connection request + // + if (ILibAsyncSocket_IsFree(module->AsyncSockets[i]) != 0) + { + addrlen = sizeof(addr); + NewSocket = accept(module->ListenSocket, (struct sockaddr*)&addr, &addrlen); // Klocwork claims we could lose the resource acquired fom the declaration, but that is not possible in this case + + if (NewSocket != ~0) + { + //printf("Accepting new connection, socket = %d\r\n", NewSocket); + // + // Set this new socket to non-blocking mode, so we can play nice and share thread + // +#ifdef WIN32 + flags = 1; + if (ioctlsocket(NewSocket, FIONBIO, (u_long *)(&flags)) == SOCKET_ERROR) ILIBCRITICALEXIT2(246, WSAGetLastError()); +#elif _POSIX + flags = fcntl(NewSocket, F_GETFL,0); + fcntl(NewSocket, F_SETFL, O_NONBLOCK|flags); +#endif + // + // Instantiate a module to contain all the data about this connection + // + if ((data = (struct ILibAsyncServerSocket_Data*)malloc(sizeof(struct ILibAsyncServerSocket_Data))) == NULL) ILIBCRITICALEXIT(254); + memset(data, 0, sizeof(struct ILibAsyncServerSocket_Data)); + data->module = (struct ILibAsyncServerSocketModule*)socketModule; + + ILibAsyncSocket_UseThisSocket(module->AsyncSockets[i], NewSocket, &ILibAsyncServerSocket_OnInterruptSink, data); + ILibAsyncSocket_SetRemoteAddress(module->AsyncSockets[i], (struct sockaddr*)&addr); + + if (module->OnConnect != NULL) + { + // Notify the user about this new connection + module->OnConnect(module, module->AsyncSockets[i], &(data->user)); + } + } + else {break;} + } + } + } +} // Klocwork claims that we could lose the resource acquired in the declaration, but that is not possible in this case +// +// Chain Destroy handler +// +// +void ILibAsyncServerSocket_Destroy(void *socketModule) +{ + struct ILibAsyncServerSocketModule *module =(struct ILibAsyncServerSocketModule*)socketModule; + + free(module->AsyncSockets); + if (module->ListenSocket != (SOCKET)~0) + { +#ifdef WIN32 + closesocket(module->ListenSocket); +#elif _POSIX + close(module->ListenSocket); +#endif + module->ListenSocket = (SOCKET)~0; + } +} + +// +// Internal method dispatched by the OnData event of the underlying ILibAsyncSocket +// +// +// +// +// +// +// +// +void ILibAsyncServerSocket_OnData(ILibAsyncSocket_SocketModule socketModule,char* buffer,int *p_beginPointer, int endPointer,void (**OnInterrupt)(void *AsyncSocketMoudle, void *user),void **user, int *PAUSE) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)(*user); + int bpointer = *p_beginPointer; + + UNREFERENCED_PARAMETER( OnInterrupt ); + + // Pass the received data up + if (data != NULL && data->module->OnReceive != NULL) + { + data->module->OnReceive(data->module, socketModule, buffer, &bpointer, endPointer, &(data->module->OnInterrupt), &(data->user), PAUSE); + if (ILibAsyncSocket_IsFree(socketModule)) + { + *p_beginPointer = endPointer; + } + else + { + *p_beginPointer = bpointer; + } + } +} +// +// Internal method dispatched by the OnConnect event of the underlying ILibAsyncSocket. In this case, it will only occur when TLS connection is established. +// +// +// +void ILibAsyncServerSocket_OnConnectSink(ILibAsyncSocket_SocketModule socketModule, int Connected, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + + if (data == NULL) return; + if (Connected == 0) + { + // Connection Failed, clean up + free(data); + return; + } + + if (data->module->OnConnect != NULL) data->module->OnConnect(data->module, socketModule, &(data->user)); +} +// +// Internal method dispatched by the OnDisconnect event of the underlying ILibAsyncSocket +// +// +// +void ILibAsyncServerSocket_OnDisconnectSink(ILibAsyncSocket_SocketModule socketModule, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + + // Pass this Disconnect event up + if (data == NULL) return; + if (data->module->OnDisconnect != NULL) data->module->OnDisconnect(data->module, socketModule, data->user); + + // If the chain is shutting down, we need to free some resources + if (ILibIsChainBeingDestroyed(data->module->ChainLink.ParentChain) == 0) free(data); +} +// +// Internal method dispatched by the OnSendOK event of the underlying ILibAsyncSocket +// +// +// +void ILibAsyncServerSocket_OnSendOKSink(ILibAsyncSocket_SocketModule socketModule, void *user) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + + // Pass the OnSendOK event up + if (data != NULL && data->module->OnSendOK != NULL) data->module->OnSendOK(data->module, socketModule, data->user); +} +// +// Internal method dispatched by ILibAsyncSocket, to signal that the buffers have been reallocated +// +// The ILibAsyncSocket sender +// The ILibAsyncServerSocket_Data object +// The offset to the new buffer location +void ILibAsyncServerSocket_OnBufferReAllocated(ILibAsyncSocket_SocketModule ConnectionToken, void *user, ptrdiff_t offSet) +{ + struct ILibAsyncServerSocket_Data *data = (struct ILibAsyncServerSocket_Data*)user; + if (data!=NULL && data->Callback!=NULL) + { + // + // If someone above us, has registered for this callback, we need to fire it, + // with the correct user object + // + data->Callback(data->module,ConnectionToken,data->user,offSet); + } +} + +void ILibAsyncServerSocket_ResumeListeningSink(void *chain, void* user) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)user; + + int ra = 1; + int off = 0; + int receivingAddressLength = sizeof(struct sockaddr_in6); + struct sockaddr_in6 localif; + struct sockaddr_in6 localAddress; + + UNREFERENCED_PARAMETER(chain); + + memset(&localif, 0, sizeof(struct sockaddr_in6)); + if (m->loopbackFlag != 2 && ILibDetectIPv6Support()) + { + // Setup the IPv6 any or loopback address, this socket will also work for IPv4 traffic on IPv6 stack + localif.sin6_family = AF_INET6; + localif.sin6_addr = (m->loopbackFlag != 0 ? in6addr_loopback : in6addr_any); + localif.sin6_port = htons(m->initialPortNumber); + } + else + { + // IPv4-only detected + localif.sin6_family = AF_INET; +#ifdef WINSOCK2 + ((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((m->loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY)); +#else + ((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((m->loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY)); +#endif + ((struct sockaddr_in*)&localif)->sin_port = htons(m->initialPortNumber); + } + + // Get our listening socket + if ((m->ListenSocket = socket(localif.sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { m->ListenSocket = (SOCKET)~0; return; } + + // Setup the IPv6 & IPv4 support on same socket + if (localif.sin6_family == AF_INET6) if (setsockopt(m->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); + +#ifdef SO_NOSIGPIPE + setsockopt(m->ListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&ra, sizeof(int)); // Turn off SIGPIPE if writing to disconnected socket +#endif + +#if defined(WIN32) + // On Windows. Lets make sure no one else can bind to this addr/port. This stops socket hijacking (not a problem on Linux). + if (setsockopt(m->ListenSocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#else + // On Linux. Setting the re-use on a TCP socket allows reuse of the socket even in timeout state. Allows for fast stop/start (Not a problem on Windows). + if (setsockopt(m->ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#endif + + // Bind the socket +#if defined(WIN32) + if (bind(m->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { closesocket(m->ListenSocket); m->ListenSocket = (SOCKET)~0; return; } +#else + if (bind(m->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { close(m->ListenSocket); m->ListenSocket = (SOCKET)~0; return; } +#endif + + // Fetch the local port number +#if defined(WINSOCK2) + getsockname(m->ListenSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength); +#else + getsockname(m->ListenSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength); +#endif + if (localAddress.sin6_family == AF_INET6) m->portNumber = ntohs(localAddress.sin6_port); else m->portNumber = ntohs(((struct sockaddr_in*)&localAddress)->sin_port); + m->listening = 0; +} +void ILibAsyncServerSocket_StopListeningSink(void *chain, void* user) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)user; + UNREFERENCED_PARAMETER(chain); + + if (m->ListenSocket != (SOCKET)~0) + { +#ifdef WIN32 + closesocket(m->ListenSocket); +#else + close(m->ListenSocket); +#endif + m->ListenSocket = (SOCKET)~0; + } +} +//! Take the server socket out of listening mode, rejecting new incoming connection requests +/*! + \param module ILibAsyncServerSocket_ServerModule Server Listening Module +*/ +void ILibAsyncServerSocket_StopListening(ILibAsyncServerSocket_ServerModule module) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)module; + ILibChain_RunOnMicrostackThread(m->ChainLink.ParentChain, ILibAsyncServerSocket_StopListeningSink, m); +} +//! Put the server socket back in listening mode, to allow new incoming connection requests +/*! + \param module ILibAsyncServerSocket_ServerModule Server Listening Module +*/ +void ILibAsyncServerSocket_ResumeListening(ILibAsyncServerSocket_ServerModule module) +{ + ILibAsyncServerSocketModule *m = (ILibAsyncServerSocketModule*)module; + ILibChain_RunOnMicrostackThread(m->ChainLink.ParentChain, ILibAsyncServerSocket_ResumeListeningSink, m); +} + + +/*! \fn ILibCreateAsyncServerSocketModule(void *Chain, int MaxConnections, int PortNumber, int initialBufferSize, ILibAsyncServerSocket_OnConnect OnConnect,ILibAsyncServerSocket_OnDisconnect OnDisconnect,ILibAsyncServerSocket_OnReceive OnReceive,ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK) +\brief Instantiates a new ILibAsyncServerSocket +\param Chain The chain to add this module to. (Chain must not be running) +\param MaxConnections The max number of simultaneous connections that will be allowed +\param PortNumber The port number to bind to. 0 will select a random port +\param initialBufferSize The initial size of the receive buffer +\param loopbackFlag 0 to bind to ANY, 1 to bind to IPv6 loopback first, 2 to bind to IPv4 loopback first. +\param OnConnect Function Pointer that triggers when a connection is established +\param OnDisconnect Function Pointer that triggers when a connection is closed +\param OnReceive Function Pointer that triggers when data is received +\param OnInterrupt Function Pointer that triggers when connection interrupted +\param OnSendOK Function Pointer that triggers when pending sends are complete +\param ServerAutoFreeMemorySize Size of AutoFreeMemory on Server to co-allocate +\param SessionAutoFreeMemorySize Size of AutoFreeMemory on Session to co-allocate +\returns An ILibAsyncServerSocket module +*/ +ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(void *Chain, int MaxConnections, unsigned short PortNumber, int initialBufferSize, int loopbackFlag, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize) +{ + int i; + int ra = 1; + int off = 0; + int receivingAddressLength = sizeof(struct sockaddr_in6); + struct sockaddr_in6 localif; + struct sockaddr_in6 localAddress; + struct ILibAsyncServerSocketModule *RetVal; + + memset(&localif, 0, sizeof(struct sockaddr_in6)); + if (loopbackFlag != 2 && ILibDetectIPv6Support()) + { + // Setup the IPv6 any or loopback address, this socket will also work for IPv4 traffic on IPv6 stack + localif.sin6_family = AF_INET6; + localif.sin6_addr = (loopbackFlag != 0?in6addr_loopback:in6addr_any); + localif.sin6_port = htons(PortNumber); + } + else + { + // IPv4-only detected + localif.sin6_family = AF_INET; +#ifdef WINSOCK2 + ((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY)); +#else + ((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY)); +#endif + ((struct sockaddr_in*)&localif)->sin_port = htons(PortNumber); + } + + // Instantiate a new AsyncServer module + RetVal = (struct ILibAsyncServerSocketModule*)ILibChain_Link_Allocate(sizeof(struct ILibAsyncServerSocketModule), ServerUserMappedMemorySize); + + RetVal->ChainLink.PreSelectHandler = &ILibAsyncServerSocket_PreSelect; + RetVal->ChainLink.PostSelectHandler = &ILibAsyncServerSocket_PostSelect; + RetVal->ChainLink.DestroyHandler = &ILibAsyncServerSocket_Destroy; + RetVal->ChainLink.ParentChain = Chain; + RetVal->OnConnect = OnConnect; + RetVal->OnDisconnect = OnDisconnect; + RetVal->OnInterrupt = OnInterrupt; + RetVal->OnSendOK = OnSendOK; + RetVal->OnReceive = OnReceive; + RetVal->MaxConnection = MaxConnections; + RetVal->AsyncSockets = (void**)malloc(MaxConnections * sizeof(void*)); + if (RetVal->AsyncSockets == NULL) { free(RetVal); ILIBMARKPOSITION(253); return NULL; } + RetVal->portNumber = (unsigned short)PortNumber; + RetVal->loopbackFlag = loopbackFlag; + RetVal->initialPortNumber = PortNumber; + + // Get our listening socket + if ((RetVal->ListenSocket = socket(localif.sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { free(RetVal->AsyncSockets); free(RetVal); return 0; } + + // Setup the IPv6 & IPv4 support on same socket + if (localif.sin6_family == AF_INET6) if (setsockopt(RetVal->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); + +#ifdef SO_NOSIGPIPE + setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&ra, sizeof(int)); // Turn off SIGPIPE if writing to disconnected socket +#endif + +#if defined(WIN32) + // On Windows. Lets make sure no one else can bind to this addr/port. This stops socket hijacking (not a problem on Linux). + if (setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#else + // On Linux. Setting the re-use on a TCP socket allows reuse of the socket even in timeout state. Allows for fast stop/start (Not a problem on Windows). + if (setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&ra, sizeof(int)) != 0) ILIBCRITICALERREXIT(253); +#endif + + // Bind the socket +#if defined(WIN32) + if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { closesocket(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } +#else + if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { close(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } +#endif + + // Fetch the local port number +#if defined(WINSOCK2) + getsockname(RetVal->ListenSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength); +#else + getsockname(RetVal->ListenSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength); +#endif + if (localAddress.sin6_family == AF_INET6) RetVal->portNumber = ntohs(localAddress.sin6_port); else RetVal->portNumber = ntohs(((struct sockaddr_in*)&localAddress)->sin_port); + + // Create our socket pool + for(i = 0; i < MaxConnections; ++i) + { + RetVal->AsyncSockets[i] = ILibCreateAsyncSocketModuleWithMemory(Chain, initialBufferSize, &ILibAsyncServerSocket_OnData, &ILibAsyncServerSocket_OnConnectSink, &ILibAsyncServerSocket_OnDisconnectSink, &ILibAsyncServerSocket_OnSendOKSink, SessionUserMappedMemorySize); + // + // We want to know about any buffer reallocations, because anything above us may want to know + // + ILibAsyncSocket_SetReAllocateNotificationCallback(RetVal->AsyncSockets[i], &ILibAsyncServerSocket_OnBufferReAllocated); + } + ILibAddToChain(Chain, RetVal); + + return(RetVal); +} + +/*! \fn ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule) +\brief Returns the port number the server is bound to +\param ServerSocketModule The ILibAsyncServer to query +\returns The listening port number +*/ +unsigned short ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule) +{ + return(((struct ILibAsyncServerSocketModule*)ServerSocketModule)->portNumber); +} diff --git a/MicroLMS/microstack/ILibAsyncServerSocket.h b/MicroLMS/microstack/ILibAsyncServerSocket.h new file mode 100644 index 0000000..60c5aa9 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncServerSocket.h @@ -0,0 +1,155 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef ___ILibAsyncServerSocket___ +#define ___ILibAsyncServerSocket___ + +/*! \file ILibAsyncServerSocket.h + \brief MicroStack APIs for TCP Server Functionality +*/ + +/*! \defgroup ILibAsyncServerSocket ILibAsyncServerSocket Module + \{ +*/ + +#if defined(WIN32) +#include +#elif defined(_POSIX) +#include +#endif + +#include "ILibAsyncSocket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \typedef ILibAsyncServerSocket_ServerModule + \brief The handle for an ILibAsyncServerSocket module +*/ +typedef void* ILibAsyncServerSocket_ServerModule; + +/*! \typedef ILibAsyncServerSocket_ConnectionToken + \brief Connection state, for a connected session +*/ +typedef ILibAsyncSocket_SocketModule ILibAsyncServerSocket_ConnectionToken; + +/*! \typedef ILibAsyncServerSocket_BufferReAllocated + \brief BufferReAllocation Handler + \param AsyncServerSocketToken The ILibAsyncServerSocket token + \param ConnectionToken The ILibAsyncServerSocket_Connection token + \param user The User object + \param newOffset The buffer has shifted by this offset +*/ +typedef void (*ILibAsyncServerSocket_BufferReAllocated)(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user, ptrdiff_t newOffset); +void ILibAsyncServerSocket_SetReAllocateNotificationCallback(ILibAsyncServerSocket_ServerModule AsyncServerSocketToken, ILibAsyncServerSocket_ConnectionToken ConnectionToken, ILibAsyncServerSocket_BufferReAllocated Callback); + +/*! \typedef ILibAsyncServerSocket_OnInterrupt + \brief Handler for when a session was interrupted by a call to ILibStopChain + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param user +*/ +typedef void (*ILibAsyncServerSocket_OnInterrupt)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user); +/*! \typedef ILibAsyncServerSocket_OnReceive + \brief Handler for when data is received + \par + Note on memory handling: + When you process the received buffer, you must advance \a p_beginPointer the number of bytes that you + have processed. If \a p_beginPointer does not equal \a endPointer when this method completes, + the system will continue to reclaim any memory that has already been processed, and call this method again + until no more memory has been processed. If no memory has been processed, and more data has been received + on the network, the buffer will be automatically grown (according to a specific alogrythm), to accomodate any new data. + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param buffer The data that was received + \param[in,out] p_beginPointer The start index of the data that was received + \param endPointer The end index of the data that was received + \param[in,out] OnInterrupt Set this pointer to receive notification if this session is interrupted + \param[in,out] user Set a custom user object + \param[out] PAUSE Flag to indicate if the system should continue reading data off the network +*/ +typedef void (*ILibAsyncServerSocket_OnReceive)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncServerSocket_OnInterrupt *OnInterrupt, void **user, int *PAUSE); +/*! \typedef ILibAsyncServerSocket_OnConnect + \brief Handler for when a connection is made + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param[in,out] user Set a user object to associate with this connection +*/ +typedef void (*ILibAsyncServerSocket_OnConnect)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void **user); +/*! \typedef ILibAsyncServerSocket_OnDisconnect + \brief Handler for when a connection is terminated normally + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param user User object that was associated with this connection +*/ +typedef void (*ILibAsyncServerSocket_OnDisconnect)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user); +/*! \typedef ILibAsyncServerSocket_OnSendOK + \brief Handler for when pending send operations have completed + \par + This handler will only be called if a call to \a ILibAsyncServerSocket_Send returned a value greater + than 0, which indicates that not all of the data could be sent. + \param AsyncServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param user User object that was associated with this connection +*/ +typedef void (*ILibAsyncServerSocket_OnSendOK)(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user); + +extern const int ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE; + +#define ILibCreateAsyncServerSocketModule(Chain, MaxConnections, PortNumber, initialBufferSize, loopbackFlag, OnConnect, OnDisconnect, OnReceive, OnInterrupt, OnSendOK) ILibCreateAsyncServerSocketModuleWithMemory(Chain, MaxConnections, PortNumber, initialBufferSize, loopbackFlag, OnConnect, OnDisconnect, OnReceive, OnInterrupt, OnSendOK, 0, 0) +ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(void *Chain, int MaxConnections, unsigned short PortNumber, int initialBufferSize, int loopbackFlag, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize); + +void *ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule); +void ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *user); +int ILibAsyncServerSocket_GetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule); +void ILibAsyncServerSocket_SetTag2(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, int user); + +void ILibAsyncServerSocket_StopListening(ILibAsyncServerSocket_ServerModule module); +void ILibAsyncServerSocket_ResumeListening(ILibAsyncServerSocket_ServerModule module); + +unsigned short ILibAsyncServerSocket_GetPortNumber(ILibAsyncServerSocket_ServerModule ServerSocketModule); + +/*! \def ILibAsyncServerSocket_Send + \brief Sends data onto the TCP stream + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session + \param buffer The data to be sent + \param bufferLength The length of \a buffer + \param UserFreeBuffer The \a ILibAsyncSocket_MemoryOwnership enumeration, that identifies how the memory pointer to by \a buffer is to be handled + \returns \a ILibAsyncSocket_SendStatus indicating the send status +*/ +#define ILibAsyncServerSocket_Send(ServerSocketModule, ConnectionToken, buffer, bufferLength, UserFreeBuffer) ILibAsyncSocket_Send(ConnectionToken, buffer, bufferLength, UserFreeBuffer) + +/*! \def ILibAsyncServerSocket_Disconnect + \brief Disconnects a TCP stream + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session +*/ +#define ILibAsyncServerSocket_Disconnect(ServerSocketModule, ConnectionToken) ILibAsyncSocket_Disconnect(ConnectionToken) +/*! \def ILibAsyncServerSocket_GetPendingBytesToSend + \brief Gets the outstanding number of bytes to be sent +*/ +#define ILibAsyncServerSocket_GetPendingBytesToSend(ServerSocketModule, ConnectionToken) ILibAsyncSocket_GetPendingBytesToSend(ConnectionToken) +/*! \def ILibAsyncServerSocket_GetTotalBytesSent + \brief Gets the total number of bytes that have been sent + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session +*/ +#define ILibAsyncServerSocket_GetTotalBytesSent(ServerSocketModule, ConnectionToken) ILibAsyncSocket_GetTotalBytesSent(ConnectionToken) +/*! \def ILibAsyncServerSocket_ResetTotalBytesSent + \brief Resets the total bytes sent counter + \param ServerSocketModule The parent ILibAsyncServerSocket_ServerModule + \param ConnectionToken The connection state for this session +*/ +#define ILibAsyncServerSocket_ResetTotalBytesSent(ServerSocketModule, ConnectionToken) ILibAsyncSocket_ResetTotalBytesSent(ConnectionToken) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/MicroLMS/microstack/ILibAsyncSocket.c b/MicroLMS/microstack/ILibAsyncSocket.c new file mode 100644 index 0000000..8a4e340 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncSocket.c @@ -0,0 +1,1677 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifdef MEMORY_CHECK +#include +#define MEMCHECK(x) x +#else +#define MEMCHECK(x) +#endif + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#include +#include +#define SOCKET_ERROR (-1) +#define UNREFERENCED_PARAMETER(P) (P) +#endif + +#if defined(WINSOCK2) +#include +#include +#define MSG_NOSIGNAL 0 +#elif defined(WINSOCK1) +#include +#include +#endif + +#include "ILibParsers.h" +#include "ILibAsyncSocket.h" +#include "ILibRemoteLogging.h" + +#include + +//#ifndef WINSOCK2 +//#define SOCKET unsigned int +//#endif + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) + +#ifdef SEMAPHORE_TRACKING +#define SEM_TRACK(x) x +void AsyncSocket_TrackLock(const char* MethodName, int Occurance, void *data) +{ + char v[100]; + wchar_t wv[100]; + size_t l; + + sprintf_s(v, 100, " LOCK[%s, %d] (%x)\r\n",MethodName,Occurance,data); +#ifdef WIN32 + mbstowcs_s(&l, wv, 100, v, 100); + OutputDebugString(wv); +#else + printf(v); +#endif +} +void AsyncSocket_TrackUnLock(const char* MethodName, int Occurance, void *data) +{ + char v[100]; + wchar_t wv[100]; + size_t l; + + sprintf_s(v, 100, "UNLOCK[%s, %d] (%x)\r\n",MethodName,Occurance,data); +#ifdef WIN32 + mbstowcs_s(&l, wv, 100, v, 100); + OutputDebugString(wv); +#else + printf(v); +#endif +} +#else +#define SEM_TRACK(x) +#endif + +struct ILibAsyncSocket_SendData +{ + char* buffer; + int bufferSize; + int bytesSent; + + struct sockaddr_in6 remoteAddress; + + int UserFree; + struct ILibAsyncSocket_SendData *Next; +}; + +typedef struct ILibAsyncSocketModule +{ + ILibTransport Transport; + // DO NOT MODIFY THE ABOVE FIELDS (ILibTransport) + + unsigned int PendingBytesToSend; + unsigned int TotalBytesSent; + +#if defined(WIN32) + SOCKET internalSocket; +#elif defined(_POSIX) + int internalSocket; +#endif + + // The IPv4/IPv6 compliant address of the remote endpoint. We are not going to be using IPv6 all the time, + // but we use the IPv6 structure to allocate the meximum space we need. + struct sockaddr_in6 RemoteAddress; + + // Local interface of a given socket. This module will bind to any interface, but the actual interface used + // is stored here. + struct sockaddr_in6 LocalAddress; + + // Source address. Here is stored the actual source of a packet, usualy used with UDP where the source + // of the traffic changes. + struct sockaddr_in6 SourceAddress; + +#ifdef MICROSTACK_PROXY + // The address and port of a HTTPS proxy + struct sockaddr_in6 ProxyAddress; + char ProxiedHost[255]; + int ProxyState; + char* ProxyUser; + char* ProxyPass; +#endif + + ILibAsyncSocket_OnData OnData; + ILibAsyncSocket_OnConnect OnConnect; + ILibAsyncSocket_OnDisconnect OnDisconnect; + ILibAsyncSocket_OnSendOK OnSendOK; + ILibAsyncSocket_OnInterrupt OnInterrupt; + + ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceeded; + ILibAsyncSocket_OnBufferReAllocated OnBufferReAllocated; + + void *LifeTime; + void *user; + void *user2; + int user3; + int PAUSE; + int FinConnect; + int BeginPointer; + int EndPointer; + char* buffer; + int MallocSize; + int InitialSize; + + struct ILibAsyncSocket_SendData *PendingSend_Head; + struct ILibAsyncSocket_SendData *PendingSend_Tail; + sem_t SendLock; + + int MaxBufferSize; + int MaxBufferSizeExceeded; + void *MaxBufferSizeUserObject; + + // Added for TLS support + long long timeout_lastActivity; + int timeout_milliSeconds; + ILibAsyncSocket_TimeoutHandler timeout_handler; +}ILibAsyncSocketModule; + +void ILibAsyncSocket_PostSelect(void* object,int slct, fd_set *readset, fd_set *writeset, fd_set *errorset); +void ILibAsyncSocket_PreSelect(void* object,fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime); +const int ILibMemory_ASYNCSOCKET_CONTAINERSIZE = (const int)sizeof(ILibAsyncSocketModule); + +typedef enum ILibAsyncSocket_TLSPlainText_ContentType +{ + ILibAsyncSocket_TLSPlainText_ContentType_ChangeCipherSpec = 20, + ILibAsyncSocket_TLSPlainText_ContentType_Alert = 21, + ILibAsyncSocket_TLSPlainText_ContentType_Handshake = 22, + ILibAsyncSocket_TLSPlainText_ContentType_ApplicationData = 23 +}ILibAsyncSocket_TLSPlainText_ContentType; +typedef enum ILibAsyncSocket_TLSHandshakeType +{ + ILibAsyncSocket_TLSHandshakeType_hello = 0, + ILibAsyncSocket_TLSHandshakeType_clienthello = 1, + ILibAsyncSocket_TLSHandshakeType_serverhello = 2, + ILibAsyncSocket_TLSHandshakeType_certificate = 11, + ILibAsyncSocket_TLSHandshakeType_serverkeyexchange = 12, + ILibAsyncSocket_TLSHandshakeType_certificaterequest = 13, + ILibAsyncSocket_TLSHandshakeType_serverhellodone = 14, + ILibAsyncSocket_TLSHandshakeType_certificateverify = 15, + ILibAsyncSocket_TLSHandshakeType_clientkeyexchange = 16, + ILibAsyncSocket_TLSHandshakeType_finished = 20 +}ILibAsyncSocket_TLSHandshakeType; + +// +// An internal method called by Chain as Destroy, to cleanup AsyncSocket +// +// The AsyncSocketModule +void ILibAsyncSocket_Destroy(void *socketModule) +{ + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + struct ILibAsyncSocket_SendData *temp, *current; + + // Call the interrupt event if necessary + if (!ILibAsyncSocket_IsFree(module)) + { + if (module->OnInterrupt != NULL) module->OnInterrupt(module, module->user); + } + + // Close socket if necessary + if (module->internalSocket != ~0) + { +#if defined(WIN32) +#if defined(WINSOCK2) + shutdown(module->internalSocket, SD_BOTH); +#endif + closesocket(module->internalSocket); +#elif defined(_POSIX) + shutdown(module->internalSocket, SHUT_RDWR); + close(module->internalSocket); +#endif + module->internalSocket = (SOCKET)~0; + } + + // Free the buffer if necessary + if (module->buffer != NULL) + { + if (module->buffer != ILibScratchPad2) free(module->buffer); + module->buffer = NULL; + module->MallocSize = 0; + } + + // Clear all the data that is pending to be sent + temp = current = module->PendingSend_Head; + while (current != NULL) + { + temp = current->Next; + if (current->UserFree == 0) free(current->buffer); + free(current); + current = temp; + } + + module->FinConnect = 0; + module->user = NULL; + sem_destroy(&(module->SendLock)); +} +/*! \fn ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback) +\brief Set the callback handler for when the internal data buffer has been resized +\param AsyncSocketToken The specific connection to set the callback with +\param Callback The callback handler to set +*/ +void ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback) +{ + if (AsyncSocketToken != NULL) { ((struct ILibAsyncSocketModule*)AsyncSocketToken)->OnBufferReAllocated = Callback; } +} + +ILibTransport_DoneState ILibAsyncSocket_TransportSend(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done) +{ + UNREFERENCED_PARAMETER(done); + return((ILibTransport_DoneState)ILibAsyncSocket_Send(transport, buffer, bufferLength, (enum ILibAsyncSocket_MemoryOwnership)ownership)); +} + + +/*! \fn ILibCreateAsyncSocketModule(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect,ILibAsyncSocket_OnSendOK OnSendOK) +\brief Creates a new AsyncSocketModule +\param Chain The chain to add this module to. (Chain must not be running) +\param initialBufferSize The initial size of the receive buffer +\param OnData Function Pointer that triggers when Data is received +\param OnConnect Function Pointer that triggers upon successfull connection establishment +\param OnDisconnect Function Pointer that triggers upon disconnect +\param OnSendOK Function Pointer that triggers when pending sends are complete +\param AutoFreeMemorySize Amount of memory to create along side the object, that will be freed when the parent object is freed +\returns An ILibAsyncSocket token +*/ +ILibAsyncSocket_SocketModule ILibCreateAsyncSocketModuleWithMemory(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect, ILibAsyncSocket_OnSendOK OnSendOK, int UserMappedMemorySize) +{ + struct ILibAsyncSocketModule *RetVal = (struct ILibAsyncSocketModule*)ILibChain_Link_Allocate(sizeof(struct ILibAsyncSocketModule), UserMappedMemorySize); + + RetVal->Transport.IdentifierFlags = ILibTransports_AsyncSocket; + RetVal->Transport.SendPtr = &ILibAsyncSocket_TransportSend; + RetVal->Transport.ClosePtr = &ILibAsyncSocket_Disconnect; + RetVal->Transport.PendingBytesPtr = &ILibAsyncSocket_GetPendingBytesToSend; + RetVal->Transport.ChainLink.ParentChain = Chain; + + if (initialBufferSize != 0) + { + // Use a new buffer + if ((RetVal->buffer = (char*)malloc(initialBufferSize)) == NULL) ILIBCRITICALEXIT(254); + } + else + { + // Use a static buffer, often used for UDP. + initialBufferSize = sizeof(ILibScratchPad2); + RetVal->buffer = ILibScratchPad2; + } + RetVal->Transport.ChainLink.PreSelectHandler = &ILibAsyncSocket_PreSelect; + RetVal->Transport.ChainLink.PostSelectHandler = &ILibAsyncSocket_PostSelect; + RetVal->Transport.ChainLink.DestroyHandler = &ILibAsyncSocket_Destroy; + RetVal->internalSocket = (SOCKET)~0; + RetVal->OnData = OnData; + RetVal->OnConnect = OnConnect; + RetVal->OnDisconnect = OnDisconnect; + RetVal->OnSendOK = OnSendOK; + RetVal->InitialSize = initialBufferSize; + RetVal->MallocSize = initialBufferSize; + RetVal->LifeTime = ILibGetBaseTimer(Chain); //ILibCreateLifeTime(Chain); + + sem_init(&(RetVal->SendLock), 0, 1); + + ILibAddToChain(Chain, RetVal); + + return((void*)RetVal); +} + +/*! \fn ILibAsyncSocket_ClearPendingSend(ILibAsyncSocket_SocketModule socketModule) +\brief Clears all the pending data to be sent for an AsyncSocket +\param socketModule The ILibAsyncSocket to clear +*/ +void ILibAsyncSocket_ClearPendingSend(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + struct ILibAsyncSocket_SendData *data, *temp; + + data = module->PendingSend_Head; + module->PendingSend_Tail = NULL; + module->PendingSend_Head = NULL; + module->PendingBytesToSend = 0; + while (data != NULL) + { + temp = data->Next; + // We only need to free this if we have ownership of this memory + if (data->UserFree == 0) free(data->buffer); + free(data); + data = temp; + } +} + +/*! \fn ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, int remoteAddress, unsigned short remotePort, enum ILibAsyncSocket_MemoryOwnership UserFree) +\brief Sends data on an AsyncSocket module to a specific destination. (Valid only for UDP) +\param socketModule The ILibAsyncSocket module to send data on +\param buffer The buffer to send +\param length The length of the buffer to send +\param remoteAddress The IPAddress of the destination +\param remotePort The Port number of the destination +\param UserFree Flag indicating memory ownership. +\returns \a ILibAsyncSocket_SendStatus indicating the send status +*/ +enum ILibAsyncSocket_SendStatus ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, struct sockaddr *remoteAddress, enum ILibAsyncSocket_MemoryOwnership UserFree) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + struct ILibAsyncSocket_SendData *data; + int bytesSent = 0; + enum ILibAsyncSocket_SendStatus retVal = ILibAsyncSocket_ALL_DATA_SENT; + + // If the socket is empty, return now. + if (socketModule == NULL) return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR; + + // Setup a new send data structure + if ((data = (struct ILibAsyncSocket_SendData*)malloc(sizeof(struct ILibAsyncSocket_SendData))) == NULL) ILIBCRITICALEXIT(254); + memset(data, 0, sizeof(struct ILibAsyncSocket_SendData)); + data->buffer = buffer; + data->bufferSize = length; + data->bytesSent = 0; + data->UserFree = UserFree; + data->Next = NULL; + + // Copy the address to the send data structure + memset(&(data->remoteAddress), 0, sizeof(struct sockaddr_in6)); + if (remoteAddress != NULL) memcpy_s(&(data->remoteAddress), sizeof(struct sockaddr_in6), remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family)); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_Send", 1, module);) + sem_wait(&(module->SendLock)); + + if (module->internalSocket == ~0) + { +#ifdef WIN32 + ILIBMESSAGE2("internalSocket ERROR", WSAGetLastError()); +#endif + // Too Bad, the socket closed + if (UserFree == 0) free(buffer); + free(data); + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 2, module);) + sem_post(&(module->SendLock)); + return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR; + } + + module->PendingBytesToSend += length; + if (module->PendingSend_Tail != NULL || module->FinConnect == 0) + { + // There are still bytes that are pending to be sent, or pending connection, so we need to queue this up + if (module->PendingSend_Tail == NULL) + { + module->PendingSend_Tail = data; + module->PendingSend_Head = data; + } + else + { + module->PendingSend_Tail->Next = data; + module->PendingSend_Tail = data; + } + + retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET; + + if (UserFree == ILibAsyncSocket_MemoryOwnership_USER) + { + // If we don't own this memory, we need to copy the buffer, + // because the user may free this memory before we have a chance to send it + if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(data->buffer, data->bufferSize, buffer, length); + MEMCHECK(assert(length <= data->bufferSize);) + data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN; + } + } + else + { + // There is no data pending to be sent, so lets go ahead and try to send this data + module->PendingSend_Tail = data; + module->PendingSend_Head = data; + + if (remoteAddress == NULL) + { + // Set MSG_NOSIGNAL since we don't want to get Broken Pipe signals in Linux, ignored if Windows. + bytesSent = send(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize-module->PendingSend_Head->bytesSent, MSG_NOSIGNAL); + } + else + { + bytesSent = sendto(module->internalSocket, module->PendingSend_Head->buffer+module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize-module->PendingSend_Head->bytesSent, MSG_NOSIGNAL, (struct sockaddr*)remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family)); + } + + module->timeout_lastActivity = ILibGetUptime(); + + if (bytesSent > 0) + { + // We were able to send something, so lets increment the counters + module->PendingSend_Head->bytesSent += bytesSent; + module->PendingBytesToSend -= bytesSent; + module->TotalBytesSent += bytesSent; + } + + if (bytesSent == -1) + { + // Send returned an error, so lets figure out what it was, as it could be normal +#if defined(WIN32) + bytesSent = WSAGetLastError(); + if (bytesSent != WSAEWOULDBLOCK) +#elif defined(_POSIX) + if (errno != EWOULDBLOCK) +#endif + { + // printf("ILibAsyncSocket_Send ERROR %d\r\n", errno); + +#ifdef WIN32 + ILIBMESSAGE2("ILibAsyncSocket_Send ERROR", WSAGetLastError()); +#endif // WIN32 + + // Most likely the socket closed while we tried to send + module->PAUSE = 1; + ILibAsyncSocket_ClearPendingSend(module); // This causes a segfault + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 3, module);) + sem_post(&(module->SendLock)); + + // Ensure Calling On_Disconnect with MicroStackThread + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + + return ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR; + } + } + if (module->PendingSend_Head->bytesSent == module->PendingSend_Head->bufferSize) + { + // All of the data has been sent + if (UserFree == 0) free(module->PendingSend_Head->buffer); + module->PendingSend_Tail = NULL; + free(module->PendingSend_Head); + module->PendingSend_Head = NULL; + } + else + { + // All of the data wasn't sent, so we need to copy the buffer + // if we don't own the memory, because the user may free the + // memory, before we have a chance to complete sending it. + if (UserFree == ILibAsyncSocket_MemoryOwnership_USER) + { + if ((data->buffer = (char*)malloc(data->bufferSize)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(data->buffer, data->bufferSize, buffer, length); + MEMCHECK(assert(length <= data->bufferSize);) + data->UserFree = ILibAsyncSocket_MemoryOwnership_CHAIN; + } + retVal = ILibAsyncSocket_NOT_ALL_DATA_SENT_YET; + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_3, "AsyncSocket[%p] NOT_ALL_DATA_SENT", (void*)module); + } + + } + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Send", 4, module);) + sem_post(&(module->SendLock)); + if (retVal != ILibAsyncSocket_ALL_DATA_SENT) ILibForceUnBlockChain(module->Transport.ChainLink.ParentChain); + return (retVal); +} + +/*! \fn ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule) +\brief Disconnects an ILibAsyncSocket +\param socketModule The ILibAsyncSocket to disconnect +*/ +void ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule) +{ +#if defined(WIN32) + SOCKET s; +#else + int s; +#endif + + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_1, "AsyncSocket[%p] << DISCONNECT", (void*)module); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_Disconnect", 1, module);) + sem_wait(&(module->SendLock)); + module->timeout_handler = NULL; + module->timeout_milliSeconds = 0; + + + if (module->internalSocket != ~0) + { + // There is an associated socket that is still valid, so we need to close it + module->PAUSE = 1; + s = module->internalSocket; + module->internalSocket = (SOCKET)~0; + if (s != -1) + { +#if defined(WIN32) +#if defined(WINSOCK2) + shutdown(s, SD_BOTH); +#endif + closesocket(s); +#elif defined(_POSIX) + shutdown(s, SHUT_RDWR); + close(s); +#endif + } + + // Since the socket is closing, we need to clear the data that is pending to be sent + ILibAsyncSocket_ClearPendingSend(socketModule); + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Disconnect", 2, module);) + sem_post(&(module->SendLock)); + + // This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently + if (module->FinConnect <= 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed + if (module->FinConnect > 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected + + module->FinConnect = 0; + module->user = NULL; + } + else + { + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_Disconnect", 3, module);) + sem_post(&(module->SendLock)); + } +} + +void ILibProcessAsyncSocket(struct ILibAsyncSocketModule *Reader, int pendingRead); +void ILibAsyncSocket_Callback(ILibAsyncSocket_SocketModule socketModule, int connectDisconnectReadWrite) +{ + if (socketModule != NULL) + { + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + if (connectDisconnectReadWrite == 0) // Connected + { + memset(&(module->LocalAddress), 0, sizeof(struct sockaddr_in6)); + + module->FinConnect = 1; + module->PAUSE = 0; + + // No SSL, tell application we are connected. + module->OnConnect(module, -1, module->user); + } + else if (connectDisconnectReadWrite == 1) // Disconnected + ILibAsyncSocket_Disconnect(module); + else if (connectDisconnectReadWrite == 2) // Data read + ILibProcessAsyncSocket(module, 1); + } +} + + +/*! \fn ILibAsyncSocket_ConnectTo(ILibAsyncSocket_SocketModule socketModule, int localInterface, int remoteInterface, int remotePortNumber, ILibAsyncSocket_OnInterrupt InterruptPtr,void *user) +\brief Attempts to establish a TCP connection +\param socketModule The ILibAsyncSocket to initiate the connection +\param localInterface The interface to use to establish the connection +\param remoteInterface The remote interface to connect to +\param InterruptPtr Function Pointer that triggers if connection attempt is interrupted +\param user User object that will be passed to the \a OnConnect method +*/ +void ILibAsyncSocket_ConnectTo(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteInterface, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +{ + int flags = 1; + char *tmp; + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + struct sockaddr_in6 any; + + // If there is something going on and we try to connect using this socket, fail! This is not supposed to happen. + if (module->internalSocket != -1) + { + ILIBCRITICALEXIT2(253, (int)(module->internalSocket)); + } + + // Clean up + memset(&(module->RemoteAddress), 0, sizeof(struct sockaddr_in6)); + memset(&(module->LocalAddress) , 0, sizeof(struct sockaddr_in6)); + memset(&(module->SourceAddress), 0, sizeof(struct sockaddr_in6)); + + // Setup + memcpy_s(&(module->RemoteAddress), sizeof(struct sockaddr_in6), remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sa_family)); + module->PendingBytesToSend = 0; + module->TotalBytesSent = 0; + module->PAUSE = 0; + module->user = user; + module->OnInterrupt = InterruptPtr; + if ((tmp = (char*)realloc(module->buffer, module->InitialSize)) == NULL) ILIBCRITICALEXIT(254); + module->buffer = tmp; + module->MallocSize = module->InitialSize; + + // If localInterface is NULL, we will assume INADDRANY - IPv4/IPv6 based on remote address + if (localInterface == NULL) + { + memset(&any, 0, sizeof(struct sockaddr_in6)); + #ifdef MICROSTACK_PROXY + if (module->ProxyAddress.sin6_family == 0) any.sin6_family = remoteInterface->sa_family; else any.sin6_family = module->ProxyAddress.sin6_family; + #else + any.sin6_family = remoteInterface->sa_family; + #endif + localInterface = (struct sockaddr*)&any; + } + + // The local port should always be zero +#ifdef _DEBUG + if (localInterface->sa_family == AF_INET && ((struct sockaddr_in*)localInterface)->sin_port != 0) { ILIBCRITICALEXIT(253); } + if (localInterface->sa_family == AF_INET6 && ((struct sockaddr_in6*)localInterface)->sin6_port != 0) { ILIBCRITICALEXIT(253); } +#endif + + // Allocate a new socket + if ((module->internalSocket = ILibGetSocket(localInterface, SOCK_STREAM, IPPROTO_TCP)) == 0) { ILIBCRITICALEXIT(253); return; } + + // Initialise the buffer pointers, since no data is in them yet. + module->FinConnect = 0; + module->BeginPointer = 0; + module->EndPointer = 0; + + // Turn on keep-alives for the socket + if (setsockopt(module->internalSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flags, sizeof(flags)) != 0) ILIBCRITICALERREXIT(253); + + // Set the socket to non-blocking mode, because we need to play nice and share the MicroStack thread +#if defined(WIN32) + ioctlsocket(module->internalSocket, FIONBIO, (u_long *)(&flags)); +#elif defined(_POSIX) + flags = fcntl(module->internalSocket, F_GETFL,0); + fcntl(module->internalSocket, F_SETFL, O_NONBLOCK | flags); +#endif + + // Connect the socket, and force the chain to unblock, since the select statement doesn't have us in the fdset yet. +#ifdef MICROSTACK_PROXY + if (module->ProxyAddress.sin6_family != 0) + { + if (connect(module->internalSocket, (struct sockaddr*)&(module->ProxyAddress), INET_SOCKADDR_LENGTH(module->ProxyAddress.sin6_family)) != -1) + { + // Connect failed. Set a short time and call disconnect. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + } + else +#endif + if (connect(module->internalSocket, (struct sockaddr*)remoteInterface, INET_SOCKADDR_LENGTH(remoteInterface->sa_family)) != -1) + { + // Connect failed. Set a short time and call disconnect. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + +#ifdef _DEBUG + #ifdef _POSIX + if (errno != EINPROGRESS) // The result of the connect should always be "WOULD BLOCK" on Linux. But sometimes this fails. + { + // This happens when the interface is no longer available. Disconnect socket. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + #endif + #ifdef WIN32 + { + if (GetLastError() != WSAEWOULDBLOCK) // The result of the connect should always be "WOULD BLOCK" on Windows. + { + // This happens when the interface is no longer available. Disconnect socket. + module->FinConnect = -1; + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + return; + } + } + #endif +#endif + + ILibForceUnBlockChain(module->Transport.ChainLink.ParentChain); +} + +#ifdef MICROSTACK_PROXY +//! Connect using an HTTPS proxy. If "proxyAddress" is set to NULL, this call acts just to a normal connect call without a proxy. +/*! + \param socketModule ILibAsyncSocket Client to initiate the connection + \param localInterface Local endpoint to originate the connection request from + \param remoteAddress Destination endpoint to connect to + \param proxyAddress Proxy Server to relay the connection thru. + \param proxyUser Proxy Server username (Username is stored by reference, so this memory must remain valid for duration of connection) + \param proxyPass Proxy Server password (Password is stored by reference, so this memory must remain valid for duration of connection) + \param InterruptPtr Event handler triggered if connection request is interrupted + \param user Custom user state data +*/ +void ILibAsyncSocket_ConnectToProxy(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteAddress, struct sockaddr *proxyAddress, char* proxyUser, char* proxyPass, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + memset(&(module->ProxyAddress), 0, sizeof(struct sockaddr_in6)); + module->ProxyState = 0; + module->ProxyUser = proxyUser; // Proxy user & password are kept by reference!!! + module->ProxyPass = proxyPass; + + if (proxyAddress != NULL) memcpy_s(&(module->ProxyAddress), sizeof(struct sockaddr_in6), proxyAddress, INET_SOCKADDR_LENGTH(proxyAddress->sa_family)); + ILibAsyncSocket_ConnectTo(socketModule, localInterface, remoteAddress, InterruptPtr, user); +} +#endif + +// +// Internal method called when data is ready to be processed on an ILibAsyncSocket +// +// The ILibAsyncSocket with pending data +void ILibProcessAsyncSocket(struct ILibAsyncSocketModule *Reader, int pendingRead) +{ + int bytesReceived = 0; + int len; + char *temp; + + if (Reader->PAUSE > 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(Reader->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] is PAUSED", (void*)Reader); + return; + } + + + // + // Try to read from the socket + // + if (pendingRead != 0) + { + { + // Read data off the non-SSL, generic socket. + // Set the receive address buffer size and read from the socket. + len = sizeof(struct sockaddr_in6); +#if defined(WINSOCK2) + bytesReceived = recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), (int*)&len); +#else + bytesReceived = (int)recvfrom(Reader->internalSocket, Reader->buffer + Reader->EndPointer, Reader->MallocSize - Reader->EndPointer, 0, (struct sockaddr*)&(Reader->SourceAddress), (socklen_t*)&len); +#endif +#ifdef WIN32 +#ifdef _DEBUG + if (bytesReceived == SOCKET_ERROR) ILIBMESSAGE2("Failed socket recvfrom function", WSAGetLastError()); +#endif +#endif + ILib6to4((struct sockaddr*)&(Reader->SourceAddress)); + ILibRemoteLogging_printf(ILibChainGetLogger(Reader->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] recv (NON-TLS) returned %d", (void*)Reader, bytesReceived); + } + if (bytesReceived > 0) + { + // + // Data was read, so increment our counters + // + Reader->EndPointer += bytesReceived; + } + } + + // + // Event OnData up the stack, to process any data that is available + // + while (Reader->internalSocket != ~0 && Reader->PAUSE <= 0 && Reader->BeginPointer != Reader->EndPointer && Reader->EndPointer != 0) + { + int iPointer = 0; + + if (Reader->OnData != NULL) + { + Reader->OnData(Reader, Reader->buffer + Reader->BeginPointer, &(iPointer), Reader->EndPointer - Reader->BeginPointer, &(Reader->OnInterrupt), &(Reader->user), &(Reader->PAUSE)); + if (iPointer == 0) { break; } + Reader->BeginPointer += iPointer; + } + } + if (Reader->BeginPointer == Reader->EndPointer) { Reader->BeginPointer = Reader->EndPointer = 0; } + + + if (bytesReceived <= 0 && pendingRead != 0) + { + // If a UDP packet is larger than the buffer, drop it. +#if defined(WINSOCK2) + if (bytesReceived == SOCKET_ERROR && WSAGetLastError() == 10040) { ILIBMESSAGE2("Failed bytesReceived", WSAGetLastError()); return; } +#else + // TODO: Linux errno + //if (bytesReceived == -1 && errno != 0) printf("ERROR: errno = %d, %s\r\n", errno, strerror(errno)); +#endif + + // + // This means the socket was gracefully closed by the remote endpoint + // + SEM_TRACK(AsyncSocket_TrackLock("ILibProcessAsyncSocket", 1, Reader);) + ILibAsyncSocket_ClearPendingSend(Reader); + SEM_TRACK(AsyncSocket_TrackUnLock("ILibProcessAsyncSocket", 2, Reader);) + +#if defined(WIN32) +#if defined(WINSOCK2) + shutdown(Reader->internalSocket, SD_BOTH); +#endif + closesocket(Reader->internalSocket); +#elif defined(_POSIX) + shutdown(Reader->internalSocket, SHUT_RDWR); + close(Reader->internalSocket); +#endif + Reader->internalSocket = (SOCKET)~0; + + ILibAsyncSocket_ClearPendingSend(Reader); + + // + // Inform the user the socket has closed + // + Reader->timeout_handler = NULL; + Reader->timeout_milliSeconds = 0; + // This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently + if (Reader->FinConnect <= 0 && Reader->OnConnect != NULL) { Reader->OnConnect(Reader, 0, Reader->user); } // Connection Failed + if (Reader->FinConnect > 0 && Reader->OnDisconnect != NULL) { Reader->OnDisconnect(Reader, Reader->user); } // Socket Disconnected + Reader->FinConnect = 0; + + // + // If we need to free the buffer, do so + // + if (Reader->buffer != NULL) + { + if (Reader->buffer != ILibScratchPad2) free(Reader->buffer); + Reader->buffer = NULL; + Reader->MallocSize = 0; + } + } + else + { + // + // Only do these checks if the socket was not closed, otherwise we're wasting time + // + + // + // Check to see if we need to move any data, to maximize buffer space + // + if (Reader->BeginPointer > 0) + { + // + // We can save some cycles by moving the data back to the top + // of the buffer, instead of just allocating more memory. + // + temp = Reader->buffer + Reader->BeginPointer; + + memmove_s(Reader->buffer, Reader->MallocSize, temp, Reader->EndPointer - Reader->BeginPointer); + Reader->EndPointer -= Reader->BeginPointer; + Reader->BeginPointer = 0; + + // + // Even though we didn't allocate new memory, we still moved data in the buffer, + // so we need to inform people of that, because it might be important + // + if (Reader->OnBufferReAllocated != NULL) Reader->OnBufferReAllocated(Reader, Reader->user, temp - Reader->buffer); + } + + // + // Check to see if we should grow the buffer + // + if (Reader->MallocSize - Reader->EndPointer < 1024 && (Reader->MaxBufferSize == 0 || Reader->MallocSize < Reader->MaxBufferSize)) + { + Reader->MallocSize = (Reader->MallocSize + MEMORYCHUNKSIZE < Reader->MaxBufferSize) ? (Reader->MallocSize + MEMORYCHUNKSIZE) : (Reader->MaxBufferSize == 0 ? (Reader->MallocSize + MEMORYCHUNKSIZE) : Reader->MaxBufferSize); + + temp = Reader->buffer; + if ((Reader->buffer = (char*)realloc(Reader->buffer, Reader->MallocSize)) == NULL) ILIBCRITICALEXIT(254); + // + // If this realloc moved the buffer somewhere, we need to inform people of it + // + if (Reader->buffer != temp && Reader->OnBufferReAllocated != NULL) Reader->OnBufferReAllocated(Reader, Reader->user, Reader->buffer - temp); + } + } +} + +/*! \fn ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the user object +\param socketModule The ILibAsyncSocket token to fetch the user object from +\returns The user object +*/ +void *ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule) +{ + return(socketModule == NULL?NULL:((struct ILibAsyncSocketModule*)socketModule)->user); +} + +void ILibAsyncSocket_SetUser(ILibAsyncSocket_SocketModule socketModule, void* user) +{ + if (socketModule == NULL) return; + ((struct ILibAsyncSocketModule*)socketModule)->user = user; +} + +void *ILibAsyncSocket_GetUser2(ILibAsyncSocket_SocketModule socketModule) +{ + return(socketModule == NULL?NULL:((struct ILibAsyncSocketModule*)socketModule)->user2); +} + +void ILibAsyncSocket_SetUser2(ILibAsyncSocket_SocketModule socketModule, void* user2) +{ + if (socketModule == NULL) return; + ((struct ILibAsyncSocketModule*)socketModule)->user2 = user2; +} + +int ILibAsyncSocket_GetUser3(ILibAsyncSocket_SocketModule socketModule) +{ + return(socketModule == NULL?-1:((struct ILibAsyncSocketModule*)socketModule)->user3); +} + +void ILibAsyncSocket_SetUser3(ILibAsyncSocket_SocketModule socketModule, int user3) +{ + if (socketModule == NULL) return; + ((struct ILibAsyncSocketModule*)socketModule)->user3 = user3; +} + +void ILibAsyncSocket_SetTimeout(ILibAsyncSocket_SocketModule socketModule, int timeoutSeconds, ILibAsyncSocket_TimeoutHandler timeoutHandler) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + module->timeout_milliSeconds = timeoutSeconds * 1000; + module->timeout_handler = timeoutHandler; +} + +// +// Chained PreSelect handler for ILibAsyncSocket +// +// +// +// +// +void ILibAsyncSocket_PreSelect(void* socketModule,fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + if (module->internalSocket == -1) return; // If there is not internal socket, just return now. + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "AsyncSocket[%p] entered PreSelect", (void*)module); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PreSelect", 1, module);) + sem_wait(&(module->SendLock)); + + if (module->internalSocket != -1) + { + if (module->timeout_milliSeconds != 0) + { + // User has set idle timeout, so we need to start checking + if (module->timeout_lastActivity == 0) + { + // No activity yet on socket, so set the idle timeout for the full duration + *blocktime = module->timeout_milliSeconds; + } + else + { + long long activity = ILibGetUptime() - module->timeout_lastActivity; // number of milliseconds since last activity + if (activity >= module->timeout_milliSeconds) + { + // Idle Timeout Occured + ILibAsyncSocket_TimeoutHandler h = module->timeout_handler; + module->timeout_milliSeconds = 0; + module->timeout_handler = NULL; + if (h != NULL) + { + sem_post(&(module->SendLock)); + h(module, module->user); + sem_wait(&(module->SendLock)); + if (module->timeout_milliSeconds != 0) { *blocktime = module->timeout_milliSeconds; } + } + } + else + { + // Idle Timeout did not occur yet, so set the blocktime for the rest of the timeout + *blocktime = (int)(module->timeout_milliSeconds - activity); + } + } + } + + if (module->PAUSE < 0) *blocktime = 0; + if (module->FinConnect == 0) + { + // Not Connected Yet + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->internalSocket, writeset); + FD_SET(module->internalSocket, errorset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + else + { + if (module->PAUSE == 0) // Only if this is zero. <0 is resume, so we want to process first + { + // Already Connected, just needs reading + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->internalSocket, readset); + FD_SET(module->internalSocket, errorset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + } + + if (module->PendingSend_Head != NULL) + { + // If there is pending data to be sent, then we need to check when the socket is writable + #if defined(WIN32) + #pragma warning( push, 3 ) // warning C4127: conditional expression is constant + #endif + FD_SET(module->internalSocket, writeset); + #if defined(WIN32) + #pragma warning( pop ) + #endif + } + } + + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PreSelect", 2, module);) + sem_post(&(module->SendLock)); + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "...AsyncSocket[%p] exited PreSelect", (void*)module); +} + +void ILibAsyncSocket_PrivateShutdown(void* socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + + // If this is an SSL socket, close down the SSL state + + // Now shutdown the socket and set it to zero + #if defined(WIN32) + #if defined(WINSOCK2) + shutdown(module->internalSocket, SD_BOTH); + #endif + closesocket(module->internalSocket); + #elif defined(_POSIX) + shutdown(module->internalSocket, SHUT_RDWR); + close(module->internalSocket); + #endif + module->internalSocket = (SOCKET)~0; + module->timeout_handler = NULL; + module->timeout_milliSeconds = 0; + + // This was a normal socket, fire the event notifying the user. Depending on connection state, we event differently + if (module->FinConnect <= 0 && module->OnConnect != NULL) { module->OnConnect(module, 0, module->user); } // Connection Failed + if (module->FinConnect > 0 && module->OnDisconnect != NULL) { module->OnDisconnect(module, module->user); } // Socket Disconnected + module->FinConnect = 0; +} + +// +// Chained PostSelect handler for ILibAsyncSocket +// +// +// +// +// +// +void ILibAsyncSocket_PostSelect(void* socketModule, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset) +{ + int TriggerSendOK = 0; + struct ILibAsyncSocket_SendData *temp; + int bytesSent = 0; + int flags, len; + int TRY_TO_SEND = 1; + int triggerReadSet = 0; + int triggerResume = 0; + int triggerWriteSet = 0; + int serr = 0, serrlen = sizeof(serr); + int fd_error, fd_read, fd_write; + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + + // If there is no internal socket or no events, just return now. + if (module->internalSocket == -1 || module->FinConnect == -1) return; + fd_error = FD_ISSET(module->internalSocket, errorset); + fd_read = FD_ISSET(module->internalSocket, readset); + fd_write = FD_ISSET(module->internalSocket, writeset); + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "AsyncSocket[%p] entered PostSelect", (void*)module); + + + + UNREFERENCED_PARAMETER( slct ); + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PostSelect", 1, module);) + sem_wait(&(module->SendLock)); // Lock! + + //if (fd_error != 0) printf("ILibAsyncSocket_PostSelect-ERROR\r\n"); + //if (fd_read != 0) printf("ILibAsyncSocket_PostSelect-READ\r\n"); + //if (fd_write != 0) printf("ILibAsyncSocket_PostSelect-WRITE\r\n"); + + // + // Error Handling. If the ERROR flag is set we have a problem. If not, we must check the socket status for an error. + // Yes, this is odd, but it's possible for a socket to report a read set and still have an error, in this past this + // was not handled and caused a lot of problems. + // + if (fd_error != 0) + { + serr = 1; + } + else + { + // Fetch the socket error code +#if defined(WINSOCK2) + getsockopt(module->internalSocket, SOL_SOCKET, SO_ERROR, (char*)&serr, (int*)&serrlen); +#else + getsockopt(module->internalSocket, SOL_SOCKET, SO_ERROR, (char*)&serr, (socklen_t*)&serrlen); +#endif + } + + #ifdef MICROSTACK_PROXY + // Handle proxy, we need to read the proxy response, all of it and not a byte more. + if (module->FinConnect == 1 && module->ProxyState == 1 && serr == 0 && fd_read != 0) + { + char *ptr1, *ptr2; + int len2, len3; + serr = 555; // Fake proxy error + len2 = recv(module->internalSocket, ILibScratchPad2, 1024, MSG_PEEK | MSG_NOSIGNAL); + if (len2 > 0 && len2 < 1024) + { + ILibScratchPad2[len2] = 0; + ptr1 = strstr(ILibScratchPad2, "\r\n\r\n"); + ptr2 = strstr(ILibScratchPad2, " 200 "); + if (ptr1 != NULL && ptr2 != NULL && ptr2 < ptr1) + { + len3 = (int)((ptr1 + 4) - ILibScratchPad2); + recv(module->internalSocket, ILibScratchPad2, len3, MSG_NOSIGNAL); + module->FinConnect = 0; // Let pretend we never connected, this will trigger all the connection stuff. + module->ProxyState = 2; // Move the proxy connection state forward. + serr = 0; // Proxy connected collectly. + } + } + } + #endif + + // If there are any errors, shutdown this socket + if (serr != 0) + { + // Unlock before fireing the event + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + ILibAsyncSocket_PrivateShutdown(module); + } + else + { + // There are no errors, lets keep processing the socket normally + if (module->FinConnect == 0) + { + // Check to see if the socket is connected +#ifdef MICROSTACK_PROXY + if (fd_write != 0 || module->ProxyState == 2) +#else + if (fd_write != 0) +#endif + { + // Connected + len = sizeof(struct sockaddr_in6); +#if defined(WINSOCK2) + getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), (int*)&len); +#else + getsockname(module->internalSocket, (struct sockaddr*)(&module->LocalAddress), (socklen_t*)&len); +#endif + module->FinConnect = 1; + module->PAUSE = 0; + + // Set the socket to non-blocking mode, so we can play nice and share the thread + #if defined(WIN32) + flags = 1; + ioctlsocket(module->internalSocket, FIONBIO, (u_long *)(&flags)); + #elif defined(_POSIX) + flags = fcntl(module->internalSocket, F_GETFL,0); + fcntl(module->internalSocket, F_SETFL, O_NONBLOCK|flags); + #endif + + // If this is a proxy connection, send the proxy connect header now. +#ifdef MICROSTACK_PROXY + if (module->ProxyAddress.sin6_family != 0 && module->ProxyState == 0) + { + int len2; + ILibInet_ntop((int)(module->RemoteAddress.sin6_family), (void*)&(((struct sockaddr_in*)&(module->RemoteAddress))->sin_addr), ILibScratchPad, 4096); + if (module->ProxyUser == NULL || module->ProxyPass == NULL) + { + len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s:%u HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\n\r\n", ILibScratchPad, ntohs(module->RemoteAddress.sin6_port), ILibScratchPad); + } else { + char* ProxyAuth = NULL; + len2 = sprintf_s(ILibScratchPad2, 4096, "%s:%s", module->ProxyUser, module->ProxyPass); + len2 = ILibBase64Encode((unsigned char*)ILibScratchPad2, len2, (unsigned char**)&ProxyAuth); + len2 = sprintf_s(ILibScratchPad2, 4096, "CONNECT %s:%u HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\nProxy-authorization: basic %s\r\n\r\n", ILibScratchPad, ntohs(module->RemoteAddress.sin6_port), ILibScratchPad, ProxyAuth); + if (ProxyAuth != NULL) free(ProxyAuth); + } + module->timeout_lastActivity = ILibGetUptime(); + send(module->internalSocket, ILibScratchPad2, len2, MSG_NOSIGNAL); + module->ProxyState = 1; + // TODO: Set timeout. If the proxy does not respond, we need to close this connection. + // On the other hand... This is not generally a problem, proxies will disconnect after a timeout anyway. + + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + return; + } + if (module->ProxyState == 2) module->ProxyState = 3; +#endif + + // Connection Complete + triggerWriteSet = 1; + } + + // Unlock before fireing the event + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + + // If we did connect, we got more things to do + if (triggerWriteSet != 0) + { + module->timeout_lastActivity = ILibGetUptime(); + { + // If this is a normal socket, event the connection now. + if (module->OnConnect != NULL) module->OnConnect(module, -1, module->user); + } + } + } + else + { + // Connected socket, we need to read data + if (fd_read != 0) + { + module->timeout_lastActivity = ILibGetUptime(); + triggerReadSet = 1; // Data Available + } + else if (module->PAUSE < 0) + { + // Someone resumed a paused connection, but the FD_SET was not triggered because there is no new data on the socket. + module->timeout_lastActivity = ILibGetUptime(); + triggerResume = 1; + ++module->PAUSE; + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] was RESUMED with no new data on socket", (void*)module); + } + + // Unlock before fireing the event + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + sem_post(&(module->SendLock)); + + if (triggerReadSet != 0 || triggerResume != 0) ILibProcessAsyncSocket(module, triggerReadSet); + } + } + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 4, module);) + + + SEM_TRACK(AsyncSocket_TrackLock("ILibAsyncSocket_PostSelect", 1, module);) + sem_wait(&(module->SendLock)); + // Write Handling + if (module->FinConnect > 0 && module->internalSocket != ~0 && fd_write != 0 && module->PendingSend_Head != NULL) + { + // + // Keep trying to send data, until we are told we can't + // + module->timeout_lastActivity = ILibGetUptime(); + while (TRY_TO_SEND != 0) + { + if (module->PendingSend_Head == NULL) break; + if (module->PendingSend_Head->remoteAddress.sin6_family == 0) + { + bytesSent = (int)send(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent, MSG_NOSIGNAL); // Klocwork reports that this could block while holding a lock... This socket has been set to O_NONBLOCK, so that will never happen + } + else + { + bytesSent = (int)sendto(module->internalSocket, module->PendingSend_Head->buffer + module->PendingSend_Head->bytesSent, module->PendingSend_Head->bufferSize - module->PendingSend_Head->bytesSent, MSG_NOSIGNAL, (struct sockaddr*)&module->PendingSend_Head->remoteAddress, INET_SOCKADDR_LENGTH(module->PendingSend_Head->remoteAddress.sin6_family)); // Klocwork reports that this could block while holding a lock... This socket has been set to O_NONBLOCK, so that will never happen + } + + if (bytesSent == 0) { TRY_TO_SEND = 0; } //To avoid get stuck in an infinite loop when bytesSent == 0 + + if (bytesSent > 0) + { + module->PendingBytesToSend -= bytesSent; + module->TotalBytesSent += bytesSent; + module->PendingSend_Head->bytesSent += bytesSent; + if (module->PendingSend_Head->bytesSent == module->PendingSend_Head->bufferSize) + { + // Finished Sending this block + if (module->PendingSend_Head == module->PendingSend_Tail) + { + module->PendingSend_Tail = NULL; + } + if (module->PendingSend_Head->UserFree == 0) + { + free(module->PendingSend_Head->buffer); + } + temp = module->PendingSend_Head->Next; + free(module->PendingSend_Head); + module->PendingSend_Head = temp; + if (module->PendingSend_Head == NULL) { TRY_TO_SEND = 0; } + } + else + { + // We sent data, but not everything that needs to get sent was sent, try again + TRY_TO_SEND = 1; + } + } + if (bytesSent == -1) + { + // Error, clean up everything + TRY_TO_SEND = 0; +#if defined(WIN32) + if (WSAGetLastError() != WSAEWOULDBLOCK) +#elif defined(_POSIX) + if (errno != EWOULDBLOCK) +#endif + { + // There was an error sending + ILibAsyncSocket_ClearPendingSend(socketModule); + ILibLifeTime_Add(module->LifeTime, socketModule, 0, &ILibAsyncSocket_Disconnect, NULL); + } + } + } + // This triggers OnSendOK, if all the pending data has been sent. + if (module->PendingSend_Head == NULL && bytesSent != -1) { TriggerSendOK = 1; } + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 2, module);) + sem_post(&(module->SendLock)); + if (TriggerSendOK != 0) + { + module->OnSendOK(module, module->user); + if (module->Transport.SendOkPtr != NULL) { module->Transport.SendOkPtr(module); } + } + + if (bytesSent == 0) { ILibAsyncSocket_ClearPendingSend(socketModule); } //If bytesSent == 0 then clear pending data + } + else + { + SEM_TRACK(AsyncSocket_TrackUnLock("ILibAsyncSocket_PostSelect", 2, module);) + sem_post(&(module->SendLock)); + } + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_5, "...AsyncSocket[%p] exited PostSelect", (void*)module); +} + +/*! \fn ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule) +\brief Determines if an ILibAsyncSocket is in use +\param socketModule The ILibAsyncSocket to query +\returns 0 if in use, nonzero otherwise +*/ +int ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return((module == NULL || module->internalSocket==~0)?1:0); +} + +int ILibAsyncSocket_IsConnected(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return module->FinConnect; +} + +/*! \fn ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the number of bytes that are pending to be sent +\param socketModule The ILibAsyncSocket to query +\returns Number of pending bytes +*/ +unsigned int ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return(module->PendingBytesToSend); +} + +/*! \fn ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the total number of bytes that have been sent, since the last reset +\param socketModule The ILibAsyncSocket to query +\returns Number of bytes sent +*/ +unsigned int ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return(module->TotalBytesSent); +} + +/*! \fn ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +\brief Resets the total bytes sent counter +\param socketModule The ILibAsyncSocket to reset +*/ +void ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + module->TotalBytesSent = 0; +} + +/*! \fn ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer) +\brief Returns the buffer associated with an ILibAsyncSocket +\param socketModule The ILibAsyncSocket to obtain the buffer from +\param[out] buffer The buffer +\param[out] BeginPointer Stating offset of the buffer +\param[out] EndPointer Length of buffer +*/ +void ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer) +{ + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + + *buffer = module->buffer; + *BeginPointer = module->BeginPointer; + *EndPointer = module->EndPointer; +} + +void ILibAsyncSocket_ModuleOnConnect(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + if (module != NULL && module->OnConnect != NULL) module->OnConnect(module, -1, module->user); +} + +//! Set the SSL client context used by all connections done by this socket module. The SSL context must +//! be set before using this module. If left to NULL, all connections are in the clear using TCP. +//! +//! This is utilized by the ILibAsyncServerSocket module +/*! + \param socketModule The ILibAsyncSocket to modify + \param ssl_ctx The ssl_ctx structure + \param server ILibAsyncSocket_TLS_Mode Configuration setting to set +*/ + +//! Sets the remote address field. +//! This is utilized by the ILibAsyncServerSocket module +/*! + \param socketModule ILibAsyncSocket_SocketModule to modify + \param remoteAddress The remote endpoint to set +*/ +void ILibAsyncSocket_SetRemoteAddress(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress) +{ + if (socketModule != NULL) + { + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + memcpy_s(&(module->RemoteAddress), sizeof(struct sockaddr_in6), remoteAddress, INET_SOCKADDR_LENGTH(remoteAddress->sa_family)); + } +} + +/*! \fn ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule,void* UseThisSocket,ILibAsyncSocket_OnInterrupt InterruptPtr,void *user) +\brief Associates an actual socket with ILibAsyncSocket +\par +Instead of calling \a ConnectTo, you can call this method to associate with an already +connected socket. +\param socketModule The ILibAsyncSocket to associate +\param UseThisSocket The socket to associate +\param InterruptPtr Function Pointer that triggers when the TCP connection is interrupted +\param user User object to associate with this session +*/ +#if defined(WIN32) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, SOCKET UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +#elif defined(_POSIX) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, int UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user) +#endif +{ + int flags; + char *tmp; + struct ILibAsyncSocketModule* module = (struct ILibAsyncSocketModule*)socketModule; + + module->PendingBytesToSend = 0; + module->TotalBytesSent = 0; + module->internalSocket = UseThisSocket; + module->OnInterrupt = InterruptPtr; + module->user = user; + module->FinConnect = 1; + module->PAUSE = 0; + + ILibRemoteLogging_printf(ILibChainGetLogger(module->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_1, "AsyncSocket[%p] Initialized", (void*)module); + + // + // If the buffer is too small/big, we need to realloc it to the minimum specified size + // + if (module->buffer != ILibScratchPad2) + { + if ((tmp = (char*)realloc(module->buffer, module->InitialSize)) == NULL) ILIBCRITICALEXIT(254); + module->buffer = tmp; + module->MallocSize = module->InitialSize; + } + module->BeginPointer = 0; + module->EndPointer = 0; + + // + // Make sure the socket is non-blocking, so we can play nice and share the thread + // +#if defined(WIN32) + flags = 1; + ioctlsocket(module->internalSocket, FIONBIO,(u_long *)(&flags)); +#elif defined(_POSIX) + flags = fcntl(module->internalSocket,F_GETFL,0); + fcntl(module->internalSocket,F_SETFL,O_NONBLOCK|flags); +#endif +} + +/*! \fn ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the Remote Interface of a connected session +\param socketModule The ILibAsyncSocket to query +\param[in,out] remoteAddress The remote interface +\returns Number of bytes written into remoteAddress +*/ +int ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + if (module->RemoteAddress.sin6_family != 0) + { + memcpy_s(remoteAddress, sizeof(struct sockaddr_in6), &(module->RemoteAddress), INET_SOCKADDR_LENGTH(module->RemoteAddress.sin6_family)); + return INET_SOCKADDR_LENGTH(module->RemoteAddress.sin6_family); + } + memcpy_s(remoteAddress, sizeof(struct sockaddr_in6), &(module->SourceAddress), INET_SOCKADDR_LENGTH(module->SourceAddress.sin6_family)); + return INET_SOCKADDR_LENGTH(module->SourceAddress.sin6_family); +} + +/*! \fn ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule) +\brief Returns the Local Interface of a connected session, in network order +\param socketModule The ILibAsyncSocket to query +\param[in,out] localAddress The local interface +\returns The number of bytes written to localAddress +*/ +int ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *localAddress) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + int receivingAddressLength = sizeof(struct sockaddr_in6); + + if (module->LocalAddress.sin6_family !=0) + { + memcpy_s(localAddress, INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family), &(module->LocalAddress), INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family)); + return INET_SOCKADDR_LENGTH(module->LocalAddress.sin6_family); + } + else + { +#if defined(WINSOCK2) + getsockname(module->internalSocket, localAddress, (int*)&receivingAddressLength); +#else + getsockname(module->internalSocket, localAddress, (socklen_t*)&receivingAddressLength); +#endif + return receivingAddressLength; + } +} +//! Get's the locally bound port +/*! + \param socketModule ILibAsyncSocket_SocketModule object to query + \return The locallly bound port +*/ +unsigned short ILibAsyncSocket_GetLocalPort(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + int receivingAddressLength = sizeof(struct sockaddr_in6); + struct sockaddr_in6 localAddress; + + if (module->LocalAddress.sin6_family == AF_INET6) return ntohs(module->LocalAddress.sin6_port); + if (module->LocalAddress.sin6_family == AF_INET) return ntohs((((struct sockaddr_in*)(&(module->LocalAddress)))->sin_port)); +#if defined(WINSOCK2) + getsockname(module->internalSocket, (struct sockaddr*)&localAddress, (int*)&receivingAddressLength); +#else + getsockname(module->internalSocket, (struct sockaddr*)&localAddress, (socklen_t*)&receivingAddressLength); +#endif + if (localAddress.sin6_family == AF_INET6) return ntohs(localAddress.sin6_port); + if (localAddress.sin6_family == AF_INET) return ntohs((((struct sockaddr_in*)(&localAddress))->sin_port)); + return 0; +} +/*! \fn ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule) +\brief Pauses a session +\par +Sessions can be paused, such that further data is not read from the socket until resumed. NOTE: MUST be called from Microstack thread +\param socketModule The ILibAsyncSocket to pause. +*/ +void ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule; + if (socketModule == NULL) { return; } + + sm->PAUSE = 1; +} +/*! \fn ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule) +\brief Resumes a paused session +\par +Sessions can be paused, such that further data is not read from the socket until resumed +\param socketModule The ILibAsyncSocket to resume +*/ +void ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule; + if (sm == NULL) { return; } + ILibRemoteLogging_printf(ILibChainGetLogger(sm->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "AsyncSocket[%p] was RESUMED", (void*)socketModule); + if (sm->PAUSE > 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(sm->Transport.ChainLink.ParentChain), ILibRemoteLogging_Modules_Microstack_AsyncSocket, ILibRemoteLogging_Flags_VerbosityLevel_2, "...Unblocking Chain"); + sm->PAUSE = -1; + ILibForceUnBlockChain(sm->Transport.ChainLink.ParentChain); + } +} + +/*! \fn ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module) +\brief Obtain the underlying raw socket +\param module The ILibAsyncSocket to query +\returns The pointer to raw socket +*/ +void* ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + return(&(sm->internalSocket)); +} + +void ILibAsyncSocket_Shutdown(ILibAsyncSocket_SocketModule module) +{ + ILibAsyncSocket_PrivateShutdown(module); +} + +//! Sets the local endpoint associated with this ILibAsyncSocket +/*! + \param module ILibAsyncSocket_SocketModule to modify + \param LocalAddress Local endpoint to set +*/ +void ILibAsyncSocket_SetLocalInterface(ILibAsyncSocket_SocketModule module, struct sockaddr *LocalAddress) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + memcpy_s(&(sm->LocalAddress), sizeof(struct sockaddr_in6), LocalAddress, INET_SOCKADDR_LENGTH(LocalAddress->sa_family)); +} +//! Sets the maximum size that the internal buffer can be grown +/*! + \param module ILibAsyncSocket_SocketModule to configure + \param maxSize Maximum size in bytes + \param OnBufferSizeExceededCallback ILibAsyncSocket_OnBufferSizeExceeded handler to be dispatched if the max size is exceeded + \param user Custom user state data +*/ +void ILibAsyncSocket_SetMaximumBufferSize(ILibAsyncSocket_SocketModule module, int maxSize, ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceededCallback, void *user) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + sm->MaxBufferSize = maxSize; + sm->OnBufferSizeExceeded = OnBufferSizeExceededCallback; + sm->MaxBufferSizeUserObject = user; +} +//! Sets the SendOK event handler +/*! + \param module ILibAsyncSocket_SocketModule to configure + \param OnSendOK ILibAsyncSocket_OnSendOK handler to dispatch on an OnSendOK event +*/ +void ILibAsyncSocket_SetSendOK(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnSendOK OnSendOK) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)module; + sm->OnSendOK = OnSendOK; +} + +//! Determines if the specified IPv6 address is a link local address +/*! + \param LocalAddress IPv6 Address + \return 1 = Link Local, 0 = Not Link Local +*/ +int ILibAsyncSocket_IsIPv6LinkLocal(struct sockaddr *LocalAddress) +{ + struct sockaddr_in6 *x = (struct sockaddr_in6*)LocalAddress; +#if defined(WIN32) + if (LocalAddress->sa_family == AF_INET6 && x->sin6_addr.u.Byte[0] == 0xFE && x->sin6_addr.u.Byte[1] == 0x80) return 1; +#else + if (LocalAddress->sa_family == AF_INET6 && x->sin6_addr.s6_addr[0] == 0xFE && x->sin6_addr.s6_addr[1] == 0x80) return 1; +#endif + return 0; +} +//! Determines if the internal socket is a link local socket +/*! + \param socketModule ILibAsyncSocket_SocketModule to query + \return 0 = Not Link Local, 1 = Link Local +*/ +int ILibAsyncSocket_IsModuleIPv6LinkLocal(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *module = (struct ILibAsyncSocketModule*)socketModule; + return ILibAsyncSocket_IsIPv6LinkLocal((struct sockaddr*)&(module->LocalAddress)); +} +//! Returns 1 if the ILibAsyncSocket was disconnected because the buffer size was exceeded +/*! + \param socketModule ILibAsyncSocket_SocketModule to query + \return 1 = BufferSizeExceeded +*/ +int ILibAsyncSocket_WasClosedBecauseBufferSizeExceeded(ILibAsyncSocket_SocketModule socketModule) +{ + struct ILibAsyncSocketModule *sm = (struct ILibAsyncSocketModule*)socketModule; + return(sm->MaxBufferSizeExceeded); +} + +void ILibAsyncSocket_UpdateOnData(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnData OnData) +{ + ((ILibAsyncSocketModule*)module)->OnData = OnData; +} diff --git a/MicroLMS/microstack/ILibAsyncSocket.h b/MicroLMS/microstack/ILibAsyncSocket.h new file mode 100644 index 0000000..a4776a7 --- /dev/null +++ b/MicroLMS/microstack/ILibAsyncSocket.h @@ -0,0 +1,195 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef ___ILibAsyncSocket___ +#define ___ILibAsyncSocket___ + +/*! \file ILibAsyncSocket.h +\brief MicroStack APIs for TCP Client Functionality +*/ + +/*! \defgroup ILibAsyncSocket ILibAsyncSocket Module +\{ +*/ + +#if defined(WIN32) +#include +#elif defined(_POSIX) +#include +#endif + +//#include "ssl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \def MEMORYCHUNKSIZE +\brief Incrementally grow the buffer by this amount of bytes +*/ +#define MEMORYCHUNKSIZE 4096 + +#define ILibTransports_AsyncSocket 0x40 + +typedef enum ILibAsyncSocket_SendStatus +{ + ILibAsyncSocket_ALL_DATA_SENT = 1, /*!< All of the data has already been sent */ + ILibAsyncSocket_NOT_ALL_DATA_SENT_YET = 0, /*!< Not all of the data could be sent, but is queued to be sent as soon as possible */ + ILibAsyncSocket_SEND_ON_CLOSED_SOCKET_ERROR = -4 /*!< A send operation was attmepted on a closed socket */ +}ILibAsyncSocket_SendStatus; + +/*! \enum ILibAsyncSocket_MemoryOwnership +\brief Enumeration values for Memory Ownership of variables +*/ +typedef enum ILibAsyncSocket_MemoryOwnership +{ + ILibAsyncSocket_MemoryOwnership_CHAIN = 0, /*!< The Microstack will own this memory, and free it when it is done with it */ + ILibAsyncSocket_MemoryOwnership_STATIC = 1, /*!< This memory is static, so the Microstack will not free it, and assume it will not go away, so it won't copy it either */ + ILibAsyncSocket_MemoryOwnership_USER = 2 /*!< The Microstack doesn't own this memory, so if necessary the memory will be copied */ +}ILibAsyncSocket_MemoryOwnership; + +/*! \typedef ILibAsyncSocket_SocketModule +\brief The handle for an ILibAsyncSocket module +*/ +typedef void* ILibAsyncSocket_SocketModule; +/*! \typedef ILibAsyncSocket_OnInterrupt +\brief Handler for when a session was interrupted by a call to ILibStopChain +\param socketModule The \a ILibAsyncSocket_SocketModule that was interrupted +\param user The user object that was associated with this connection +*/ + +//typedef void(*ILibAsyncSocket_OnReplaceSocket)(ILibAsyncSocket_SocketModule socketModule, void *user); +typedef void(*ILibAsyncSocket_OnBufferSizeExceeded)(ILibAsyncSocket_SocketModule socketModule, void *user); + +typedef void(*ILibAsyncSocket_OnInterrupt)(ILibAsyncSocket_SocketModule socketModule, void *user); +/*! \typedef ILibAsyncSocket_OnData +\brief Handler for when data is received +\par +Note on memory handling: +When you process the received buffer, you must advance \a p_beginPointer the number of bytes that you +have processed. If \a p_beginPointer does not equal \a endPointer when this method completes, +the system will continue to reclaim any memory that has already been processed, and call this method again +until no more memory has been processed. If no memory has been processed, and more data has been received +on the network, the buffer will be automatically grown (according to a specific alogrythm), to accomodate any new data. +\param socketModule The \a ILibAsyncSocket_SocketModule that received data +\param buffer The data that was received +\param[in,out] p_beginPointer The start index of the data that was received +\param endPointer The end index of the data that was received +\param[in,out] OnInterrupt Set this pointer to receive notification if this session is interrupted +\param[in,out] user Set a custom user object +\param[out] PAUSE Flag to indicate if the system should continue reading data off the network +*/ +typedef void(*ILibAsyncSocket_OnData)(ILibAsyncSocket_SocketModule socketModule, char* buffer, int *p_beginPointer, int endPointer,ILibAsyncSocket_OnInterrupt* OnInterrupt, void **user, int *PAUSE); +/*! \typedef ILibAsyncSocket_OnConnect +\brief Handler for when a connection is made +\param socketModule The \a ILibAsyncSocket_SocketModule that was connected +\param user The user object that was associated with this object +*/ +typedef void(*ILibAsyncSocket_OnConnect)(ILibAsyncSocket_SocketModule socketModule, int Connected, void *user); +/*! \typedef ILibAsyncSocket_OnDisconnect +\brief Handler for when a connection is terminated normally +\param socketModule The \a ILibAsyncSocket_SocketModule that was disconnected +\param user User object that was associated with this connection +*/ +typedef void(*ILibAsyncSocket_OnDisconnect)(ILibAsyncSocket_SocketModule socketModule, void *user); +/*! \typedef ILibAsyncSocket_OnSendOK +\brief Handler for when pending send operations have completed +\par +This handler will only be called if a call to \a ILibAsyncSocket_Send returned a value greater +than 0, which indicates that not all of the data could be sent. +\param socketModule The \a ILibAsyncSocket_SocketModule whos pending sends have completed +\param user User object that was associated with this connection +*/ +typedef void(*ILibAsyncSocket_OnSendOK)(ILibAsyncSocket_SocketModule socketModule, void *user); +/*! \typedef ILibAsyncSocket_OnBufferReAllocated +\brief Handler for when the internal data buffer has been resized. +\par +Note: This is only useful if you are storing pointer values into the buffer supplied in \a ILibAsyncSocket_OnData. +\param AsyncSocketToken The \a ILibAsyncSocket_SocketModule whos buffer was resized +\param user The user object that was associated with this connection +\param newOffset The new offset differential. Simply add this value to your existing pointers, to obtain the correct pointer into the resized buffer. +*/ +typedef void(*ILibAsyncSocket_OnBufferReAllocated)(ILibAsyncSocket_SocketModule AsyncSocketToken, void *user, ptrdiff_t newOffset); + +/*! \defgroup TLSGroup TLS Related Methods +* @{ +*/ +/*! @} */ + +extern const int ILibMemory_ASYNCSOCKET_CONTAINERSIZE; + +void ILibAsyncSocket_SetReAllocateNotificationCallback(ILibAsyncSocket_SocketModule AsyncSocketToken, ILibAsyncSocket_OnBufferReAllocated Callback); +void *ILibAsyncSocket_GetUser(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetUser(ILibAsyncSocket_SocketModule socketModule, void* user); +void *ILibAsyncSocket_GetUser2(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetUser2(ILibAsyncSocket_SocketModule socketModule, void* user); +int ILibAsyncSocket_GetUser3(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetUser3(ILibAsyncSocket_SocketModule socketModule, int user); +void ILibAsyncSocket_UpdateOnData(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnData OnData); + +#define ILibCreateAsyncSocketModule(Chain, initialBufferSize, OnData, OnConnect, OnDisconnect, OnSendOK) ILibCreateAsyncSocketModuleWithMemory(Chain, initialBufferSize, OnData, OnConnect, OnDisconnect, OnSendOK, 0) +ILibAsyncSocket_SocketModule ILibCreateAsyncSocketModuleWithMemory(void *Chain, int initialBufferSize, ILibAsyncSocket_OnData OnData, ILibAsyncSocket_OnConnect OnConnect, ILibAsyncSocket_OnDisconnect OnDisconnect, ILibAsyncSocket_OnSendOK OnSendOK, int UserMappedMemorySize); + +void *ILibAsyncSocket_GetSocket(ILibAsyncSocket_SocketModule module); +void ILibAsyncSocket_Shutdown(ILibAsyncSocket_SocketModule module); + +unsigned int ILibAsyncSocket_GetPendingBytesToSend(ILibAsyncSocket_SocketModule socketModule); +unsigned int ILibAsyncSocket_GetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_ResetTotalBytesSent(ILibAsyncSocket_SocketModule socketModule); + +void ILibAsyncSocket_ConnectTo(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteAddress, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); + +#ifdef MICROSTACK_PROXY +void ILibAsyncSocket_ConnectToProxy(void* socketModule, struct sockaddr *localInterface, struct sockaddr *remoteAddress, struct sockaddr *proxyAddress, char* proxyUser, char* proxyPass, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); +#endif + +enum ILibAsyncSocket_SendStatus ILibAsyncSocket_SendTo(ILibAsyncSocket_SocketModule socketModule, char* buffer, int length, struct sockaddr *remoteAddress, enum ILibAsyncSocket_MemoryOwnership UserFree); + +/*! \def ILibAsyncSocket_Send +\brief Sends data onto the TCP stream +\param socketModule The \a ILibAsyncSocket_SocketModule to send data on +\param buffer The data to be sent +\param length The length of \a buffer +\param UserFree The \a ILibAsyncSocket_MemoryOwnership enumeration, that identifies how the memory pointer to by \a buffer is to be handled +\returns \a ILibAsyncSocket_SendStatus indicating the send status +*/ +#define ILibAsyncSocket_Send(socketModule, buffer, length, UserFree) ILibAsyncSocket_SendTo(socketModule, buffer, length, NULL, UserFree) +void ILibAsyncSocket_Disconnect(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_GetBuffer(ILibAsyncSocket_SocketModule socketModule, char **buffer, int *BeginPointer, int *EndPointer); + +#if defined(WIN32) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, SOCKET UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); +#elif defined(_POSIX) +void ILibAsyncSocket_UseThisSocket(ILibAsyncSocket_SocketModule socketModule, int UseThisSocket, ILibAsyncSocket_OnInterrupt InterruptPtr, void *user); +#endif + + +void ILibAsyncSocket_SetRemoteAddress(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress); +void ILibAsyncSocket_SetLocalInterface(ILibAsyncSocket_SocketModule module, struct sockaddr *localAddress); + +int ILibAsyncSocket_IsFree(ILibAsyncSocket_SocketModule socketModule); +int ILibAsyncSocket_IsConnected(ILibAsyncSocket_SocketModule socketModule); +int ILibAsyncSocket_GetLocalInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *localAddress); +int ILibAsyncSocket_GetRemoteInterface(ILibAsyncSocket_SocketModule socketModule, struct sockaddr *remoteAddress); +unsigned short ILibAsyncSocket_GetLocalPort(ILibAsyncSocket_SocketModule socketModule); + +void ILibAsyncSocket_Resume(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_Pause(ILibAsyncSocket_SocketModule socketModule); +int ILibAsyncSocket_WasClosedBecauseBufferSizeExceeded(ILibAsyncSocket_SocketModule socketModule); +void ILibAsyncSocket_SetMaximumBufferSize(ILibAsyncSocket_SocketModule module, int maxSize, ILibAsyncSocket_OnBufferSizeExceeded OnBufferSizeExceededCallback, void *user); +void ILibAsyncSocket_SetSendOK(ILibAsyncSocket_SocketModule module, ILibAsyncSocket_OnSendOK OnSendOK); +int ILibAsyncSocket_IsIPv6LinkLocal(struct sockaddr *LocalAddress); +int ILibAsyncSocket_IsModuleIPv6LinkLocal(ILibAsyncSocket_SocketModule module); + +typedef void(*ILibAsyncSocket_TimeoutHandler)(ILibAsyncSocket_SocketModule module, void *user); +void ILibAsyncSocket_SetTimeout(ILibAsyncSocket_SocketModule module, int timeoutSeconds, ILibAsyncSocket_TimeoutHandler timeoutHandler); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/MicroLMS/microstack/ILibLMS.c b/MicroLMS/microstack/ILibLMS.c new file mode 100644 index 0000000..f82d9e7 --- /dev/null +++ b/MicroLMS/microstack/ILibLMS.c @@ -0,0 +1,834 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +/* +Real LMS code can be found here: http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers +*/ + +#if !defined(_NOHECI) + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif + +#if defined(WINSOCK2) + #include + #include +#elif defined(WINSOCK1) + #include + #include +#endif + +#include "ILibParsers.h" +#include "ILibAsyncSocket.h" +#include "ILibAsyncServerSocket.h" + +#ifdef WIN32 + #include "../heci/heciwin.h" + #include "WinBase.h" +#endif +#ifdef _POSIX + #include "../heci/HECILinux.h" + #define UNREFERENCED_PARAMETER(P) (P) +#endif +#include "../heci/PTHICommand.h" +#include "../heci/LMEConnection.h" + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) +#define LMS_MAX_CONNECTIONS 16 // Maximum is 32 +#define LMS_MAX_SESSIONS 32 +// #define _DEBUGLMS +#ifdef _DEBUGLMS +#define LMSDEBUG(...) printf(__VA_ARGS__); +#else +#define LMSDEBUG(...) +#endif + +char * g_SelfLMSDir=NULL; +int LastId = 2; + +struct ILibLMS_StateModule* IlibExternLMS = NULL; // Glocal pointer to the LMS module. Since we can only have one of these modules running, a global pointer is sometimes useful. +void ILibLMS_SetupConnection(struct ILibLMS_StateModule* module, int i); +void ILibLMS_LaunchHoldingSessions(struct ILibLMS_StateModule* module); + +// Each LMS session can be in one of these states. +enum LME_CHANNEL_STATUS { + LME_CS_FREE = 0, // The session slot is free, can be used at any time. + LME_CS_PENDING_CONNECT = 1, // A connection as been made to LMS and a notice has been send to Intel AMT asking for a CHANNEL OPEN. + LME_CS_CONNECTED = 2, // Intel AMT confirmed the connection and data can flow in both directions. + LME_CS_PENDING_LMS_DISCONNECT = 3, // The connection to LMS was disconnected, Intel AMT has been notified and we are waitting for Intel AMT confirmation. + LME_CS_PENDING_AMT_DISCONNECT = 4, // Intel AMT decided to close the connection. We are disconnecting the LMS TCP connection. + LME_CS_HOLDING = 5, // We got too much data from the LMS TCP connection, more than Intel AMT can handle. We are holding the connection until AMT can handle more. + LME_CS_CONNECTION_WAIT = 6 // A TCP connection to LMS was made, but there all LMS connections are currently busy. Waitting for one to free up. +}; + +// This is the structure for a session +struct LMEChannel +{ + int ourid; // The iddentifier of this channel on our side, this is the same as the index in the "Channel Sessions[LMS_MAX_SESSIONS];" array below. + int amtid; // The Intel AMT identifier for this channel. + enum LME_CHANNEL_STATUS status; // Current status of the channel. + int sockettype; // Type of connected socket: 0 = TCP, 1 = WebSocket + void* socketmodule; // The microstack associated with the LMS connection. + int txwindow; // Transmit window. + int rxwindow; // Receive window. + unsigned short localport; // The Intel AMT connection port. + int errorcount; // Open channel error count. + char* pending; // Buffer pointing to pending data that needs to be sent to Intel AMT, used for websocket only. + int pendingcount; // Buffer pointing to pending data size that needs to be sent to Intel AMT, used for websocket only. + int pendingptr; // Pointer to start of the pending buffer. +}; + +enum LME_VERSION_HANDSHAKING { + LME_NOT_INITIATED, + LME_INITIATED, + LME_AGREED +}; + +enum LME_SERVICE_STATUS { + LME_NOT_STARTED, + LME_STARTED +}; + +// This is the Intel AMT LMS chain module structure +struct ILibLMS_StateModule +{ + ILibChain_Link ChainLink; + void *Server1; // Microstack TCP server for port 16992 + void *Server2; // Microstack TCP server for port 16993 + void *ControlSocket; // Pointer to the LMS control web socket, NULL if not connected. + + struct LMEConnection MeConnection; // The MEI connection structure + struct LMEChannel Sessions[LMS_MAX_SESSIONS]; // List of sessions + //ILibWebServer_ServerToken WebServer; // LMS Web Server + enum LME_VERSION_HANDSHAKING handshakingStatus; + enum LME_SERVICE_STATUS pfwdService; + unsigned int AmtProtVersionMajor; // Intel AMT MEI major version + unsigned int AmtProtVersionMinor; // Intel AMT MEI minor version + sem_t Lock; // Global lock, this is needed because MEI listener is a different thread from the microstack thread +}; + +/* +// Convert a block of data to HEX +// The "out" must have (len*2)+1 free space. +char utils_HexTable[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; +char* __fastcall util_tohex(char* data, int len, char* out) +{ + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for (i = 0; i < len; i++) + { + *(p++) = utils_HexTable[((unsigned char)data[i]) >> 4]; + *(p++) = utils_HexTable[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} +*/ + +int GetFreeSlot(struct ILibLMS_StateModule* module){ + int i; + int cur = LastId; + // Look for an empty session + for (i = 0; i < LMS_MAX_SESSIONS-3; i++) { + cur++; + if (cur >= LMS_MAX_SESSIONS){ + cur = 3; + } + if (module->Sessions[cur].status == LME_CS_FREE) { + LastId = cur; + return cur; + } + } + return -1; +} + +//int GetFreeSlot(struct ILibLMS_StateModule* module){ +// int i; +// for (i = 0; i < LMS_MAX_SESSIONS - 3; i++) { +// if (cur >= LMS_MAX_SESSIONS){ +// cur = 3; +// } +// if (module->Sessions[cur].status == LME_CS_FREE) { +// LastId = cur; +// return cur; +// } +// } +// return -1; +//} + +// This function is called each time a MEI message is received from Intel AMT +void ILibLMS_MEICallback(struct LMEConnection* lmemodule, void *param, void *rxBuffer, unsigned int len) +{ + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)param; + int disconnects = 0; // This is a counter of how many sessions are freed up. We use this at the end to place HOLDING sessions into free slots. + + //ILibAsyncServerSocket_ServerModule newModule; + //ILibLinkedList list; + //void* node; + //ILibTransport* curModule; + + // Happens when the chain is being destroyed, don't call anything chain related. + if (rxBuffer == NULL) return; + + sem_wait(&(module->Lock)); + LMSDEBUG("ILibLMS_MEICallback %d\r\n", ((unsigned char*)rxBuffer)[0]); + + switch (((unsigned char*)rxBuffer)[0]) + { + case APF_GLOBAL_REQUEST: // 80 + { + + int request = 0; + unsigned char *pCurrent; + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + pHeader->StringLength = ntohl(pHeader->StringLength); + + if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST)) == 0) { request = 1; } + else if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST)) == 0) { request = 2; } + else if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_UDP_SEND_TO) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_UDP_SEND_TO, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_UDP_SEND_TO)) == 0) { request = 3; } + + + if (request == 1 || request == 2) + { + int port = 0; + unsigned int len2; + unsigned int bytesRead = len; + unsigned int hsize=0; + if (request==1) + hsize = sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST) + sizeof(UINT8); + else if (request==2) + hsize = sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST) + sizeof(UINT8); + + pCurrent = (unsigned char*)rxBuffer + hsize; + bytesRead -= hsize; + if (bytesRead < sizeof(unsigned int)) { + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + + len2 = ntohl(*((unsigned int *)pCurrent)); + pCurrent += sizeof(unsigned int); + if (bytesRead < (sizeof(unsigned int) + len2 + sizeof(unsigned int))) { + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + + // addr = (char*)pCurrent; + pCurrent += len2; + port = ntohl(*((unsigned int *)pCurrent)); + + if (request == 1) + { + //newModule = ILibCreateAsyncServerSocketModule(module->Chain, LMS_MAX_CONNECTIONS, port, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); + //if (newModule){ + // ILibAsyncServerSocket_SetTag(newModule, module); + // LME_TcpForwardReplySuccess(lmemodule, port); + //} + //else{ + // LME_TcpForwardReplyFailure(lmemodule); + //} + + // New forwarding request + if (port == 16992 || port == 16993) + LME_TcpForwardReplySuccess(lmemodule, port); + else + LME_TcpForwardReplyFailure(lmemodule); + } + else + { + // Cancel a forwarding request + + + + //list = ILibGetModuleList(module->Chain); + //node = ILibLinkedList_GetNode_Head(list); + //while (node != NULL){ + // curModule = (ILibTransport*)ILibLinkedList_GetDataFromNode(node); + // if (curModule->IdentifierFlags == ILibTransports_ILibAsyncServerSocket){ + // if (ILibAsyncServerSocket_GetPortNumber(curModule)){ + // ILibAsyncServerSocket_Close(curModule); + // } + // } + // node = ILibLinkedList_GetNextNode(node); + //} + + + LME_TcpForwardCancelReplySuccess(lmemodule); + } + } + else if (request == 3) + { + // Send a UDP packet + // TODO: Send UDP + } + else{ + // do nothing + } + } + break; + case APF_CHANNEL_OPEN: // (90) Sent by Intel AMT when a channel needs to be open from Intel AMT. This is not common, but WSMAN events are a good example of channel coming from AMT. + { + APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; + + if (pHeader->StringLength == APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT) && memcmp((char*)pHeader->String, APF_OPEN_CHANNEL_REQUEST_DIRECT, APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT)) == 0) + { + unsigned int len2; + unsigned char *pCurrent; + struct LMEChannelOpenRequestMessage channelOpenRequest; + + if (len < sizeof(APF_GENERIC_HEADER) + ntohl(pHeader->StringLength) + 7 + (5 * sizeof(UINT32))) { LME_Deinit(lmemodule); sem_post(&(module->Lock)); return; } + pCurrent = (unsigned char*)rxBuffer + sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT); + + channelOpenRequest.ChannelType = APF_CHANNEL_DIRECT; + channelOpenRequest.SenderChannel = ntohl(*((UINT32 *)pCurrent)); + pCurrent += sizeof(UINT32); + channelOpenRequest.InitialWindow = ntohl(*((UINT32 *)pCurrent)); + pCurrent += 2 * sizeof(UINT32); + len2 = ntohl(*((UINT32 *)pCurrent)); + pCurrent += sizeof(UINT32); + channelOpenRequest.Address = (char*)pCurrent; + pCurrent += len2; + channelOpenRequest.Port = ntohl(*((UINT32 *)pCurrent)); + pCurrent += sizeof(UINT32); + + LME_ChannelOpenReplyFailure(lmemodule, channelOpenRequest.SenderChannel, OPEN_FAILURE_REASON_CONNECT_FAILED); + } + } + break; + case APF_DISCONNECT: // (1) Intel AMT wants to completely disconnect. Not sure when this happens. + { + // First, we decode the message. + struct LMEDisconnectMessage disconnectMessage; + disconnectMessage.MessageType = APF_DISCONNECT; + disconnectMessage.ReasonCode = ((APF_DISCONNECT_REASON_CODE)ntohl(((APF_DISCONNECT_MESSAGE *)rxBuffer)->ReasonCode)); + LMSDEBUG("LME requested to disconnect with reason code 0x%08x\r\n", disconnectMessage.ReasonCode); + LME_Deinit(lmemodule); + } + break; + case APF_SERVICE_REQUEST: // (5) + { + + int service = 0; + APF_SERVICE_REQUEST_MESSAGE *pMessage = (APF_SERVICE_REQUEST_MESSAGE *)rxBuffer; + pMessage->ServiceNameLength = ntohl(pMessage->ServiceNameLength); + if (pMessage->ServiceNameLength == 18) { + if (memcmp(pMessage->ServiceName, "pfwd@amt.intel.com", 18) == 0) service = 1; + else if (memcmp(pMessage->ServiceName, "auth@amt.intel.com", 18) == 0) service = 2; + } + + if (service > 0) + { + if (service == 1) + { + LME_ServiceAccept(lmemodule, "pfwd@amt.intel.com"); + module->pfwdService = LME_STARTED; + } + else if (service == 2) + { + LME_ServiceAccept(lmemodule, "auth@amt.intel.com"); + } + } else { + LMSDEBUG("APF_SERVICE_REQUEST - APF_DISCONNECT_SERVICE_NOT_AVAILABLE\r\n"); + LME_Disconnect(lmemodule, APF_DISCONNECT_SERVICE_NOT_AVAILABLE); + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + } + break; + case APF_CHANNEL_OPEN_CONFIRMATION: // (91) Intel AMT confirmation to an APF_CHANNEL_OPEN request. + { + + // First, we decode the message. + struct LMEChannel* channel; + APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE *pMessage = (APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE *)rxBuffer; + + + struct LMEChannelOpenReplySuccessMessage channelOpenReply; + channelOpenReply.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. + channelOpenReply.SenderChannel = ntohl(pMessage->SenderChannel); // This is the identifier on the Intel AMT side. + channelOpenReply.InitialWindow = ntohl(pMessage->InitialWindowSize); // This is the starting window size for flow control. + channel = &(module->Sessions[channelOpenReply.RecipientChannel]); // Get the current session for this message. + + if (channel == NULL) + break; // This should never happen. + + LMSDEBUG("MEI OPEN OK OUR:%d AMT:%d\r\n", channelOpenReply.RecipientChannel, channelOpenReply.SenderChannel); + + if (channel->status == LME_CS_PENDING_CONNECT) // If the channel is in PENDING_CONNECT mode, move the session to connected state. + { + channel->amtid = channelOpenReply.SenderChannel; // We have to set the Intel AMT identifier for this session. + channel->txwindow = channelOpenReply.InitialWindow; // Set the session txwindow. + channel->status = LME_CS_CONNECTED; // Now set the session as CONNECTED. + LMSDEBUG("Channel %d now CONNECTED by AMT %d\r\n", channel->ourid, channel->socketmodule); + if (channel->sockettype == 0) // If the current socket is PAUSED, lets resume it so data can start flowing again. + { + ILibAsyncSocket_Resume(channel->socketmodule); // TCP socket resume + } + } + else if (channel->status == LME_CS_PENDING_LMS_DISCONNECT) // If the channel is in PENDING_DISCONNECT, we have to disconnect the session now. Happens when we disconnect while connection is pending. We don't want to stop a channel during connection, that is bad. + { + channel->amtid = channelOpenReply.SenderChannel; // We have to set the Intel AMT identifier for this session. + LME_ChannelClose(&(module->MeConnection), channel->amtid, channel->ourid); // Send the Intel AMT close. We keep the channel in LME_CS_PENDING_LMS_DISCONNECT state until the close is confirmed. + LMSDEBUG("Channel %d now CONNECTED by AMT %d, but CLOSING it now\r\n", channel->ourid, channel->socketmodule); + } + else + { + // Here, we get an APF_CHANNEL_OPEN in an unexpected state, this should never happen. + LMSDEBUG("Channel %d, unexpected CONNECTED by AMT %d\r\n", channel->ourid, channel->socketmodule); + } + } + break; + case APF_CHANNEL_OPEN_FAILURE: // (92) Intel AMT rejected our connection attempt. + { + + // First, we decode the message. + struct LMEChannel* channel; + APF_CHANNEL_OPEN_FAILURE_MESSAGE *pMessage = (APF_CHANNEL_OPEN_FAILURE_MESSAGE *)rxBuffer; + struct LMEChannelOpenReplyFailureMessage channelOpenReply; + channelOpenReply.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. + + channelOpenReply.ReasonCode = (OPEN_FAILURE_REASON)(ntohl(pMessage->ReasonCode)); // Get the error reason code. + channel = &(module->Sessions[channelOpenReply.RecipientChannel]); // Get the current session for this message. + if (channel == NULL) break; // This should never happen. + + LMSDEBUG("**OPEN FAIL OUR:%d ERR:%d\r\n", channelOpenReply.RecipientChannel, channelOpenReply.ReasonCode); + + if (channel->errorcount++ >= 0 || channel->status == LME_CS_PENDING_LMS_DISCONNECT) + { + // Fail connection + channel->status = LME_CS_FREE; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + sem_post(&(module->Lock)); + ILibAsyncSocket_Disconnect(channel->socketmodule); + //ILibLMS_LaunchHoldingSessions(module); + return; + } + else + { + // Try again + //ILibLMS_SetupConnection(module, channel->ourid); + } + } + break; + case APF_CHANNEL_CLOSE: // (97) Intel AMT is closing this channel, we need to disconnect the LMS TCP connection + { + + // First, we decode the message. + struct LMEChannel* channel; + APF_CHANNEL_CLOSE_MESSAGE *pMessage = (APF_CHANNEL_CLOSE_MESSAGE *)rxBuffer; + struct LMEChannelCloseMessage channelClose; + channelClose.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. + channel = &(module->Sessions[channelClose.RecipientChannel]); // Get the current session for this message. + if (channel == NULL) break; // This should never happen. + + //LMSDEBUG("CLOSE OUR:%d\r\n", channelClose.RecipientChannel); + + if (channel->status == LME_CS_CONNECTED) + { + if (ILibAsyncSocket_IsConnected(channel->socketmodule)) + { + channel->status = LME_CS_PENDING_AMT_DISCONNECT; + LMSDEBUG("Channel %d now PENDING_AMT_DISCONNECT by AMT, calling microstack disconnect %d\r\n", channel->ourid, channel->socketmodule); + LME_ChannelClose(lmemodule, channel->amtid, channel->ourid); + sem_post(&(module->Lock)); + if (channel->sockettype == 0) // If the current socket is PAUSED, lets resume it so data can start flowing again. + { + ILibAsyncSocket_Disconnect(channel->socketmodule); // TCP socket close + } + + sem_wait(&(module->Lock)); + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + } + else + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + } + } + else if (channel->status == LME_CS_PENDING_LMS_DISCONNECT) + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); + } + else + { + LMSDEBUG("Channel %d CLOSE, UNEXPECTED STATE %d\r\n", channel->ourid, channel->status); + } + } + break; + case APF_CHANNEL_DATA: // (94) Intel AMT is sending data that we must relay into an LMS TCP connection. + { + //sem_wait(&(module->Lock)); + + struct LMEChannel* channel; + APF_CHANNEL_DATA_MESSAGE *pMessage = (APF_CHANNEL_DATA_MESSAGE *)rxBuffer; + struct LMEChannelDataMessage channelData; + enum ILibAsyncSocket_SendStatus r; + channelData.MessageType = APF_CHANNEL_DATA; + channelData.RecipientChannel = ntohl(pMessage->RecipientChannel); + channelData.DataLength = ntohl(pMessage->DataLength); + channelData.Data = (unsigned char*)rxBuffer + sizeof(APF_CHANNEL_DATA_MESSAGE); + channel = &(module->Sessions[channelData.RecipientChannel]); + + + if (channel == NULL || channel->socketmodule == NULL){ + sem_post(&(module->Lock)); + break; + } + + if (channel->sockettype == 0) + { + r = ILibAsyncSocket_Send(channel->socketmodule, (char*)(channelData.Data), channelData.DataLength, ILibAsyncSocket_MemoryOwnership_USER); // TCP socket + } + + channel->rxwindow += channelData.DataLength; + if (r == ILibAsyncSocket_ALL_DATA_SENT && channel->rxwindow > 1024) { + LME_ChannelWindowAdjust(lmemodule, channel->amtid, channel->rxwindow); channel->rxwindow = 0; + } + //sem_post(&(module->Lock)); + } + break; + case APF_CHANNEL_WINDOW_ADJUST: // 93 + { + //sem_wait(&(module->Lock)); + + struct LMEChannel* channel; + APF_WINDOW_ADJUST_MESSAGE *pMessage = (APF_WINDOW_ADJUST_MESSAGE *)rxBuffer; + struct LMEChannelWindowAdjustMessage channelWindowAdjust; + channelWindowAdjust.MessageType = APF_CHANNEL_WINDOW_ADJUST; + channelWindowAdjust.RecipientChannel = ntohl(pMessage->RecipientChannel); + channelWindowAdjust.BytesToAdd = ntohl(pMessage->BytesToAdd); + channel = &(module->Sessions[channelWindowAdjust.RecipientChannel]); + if (channel == NULL || channel->status == LME_CS_FREE){ + sem_post(&(module->Lock)); + break; + } + channel->txwindow += channelWindowAdjust.BytesToAdd; + if (channel->sockettype == 0) + { + ILibAsyncSocket_Resume(channel->socketmodule); // TCP socket + } + //sem_post(&(module->Lock)); + } + break; + case APF_PROTOCOLVERSION: // 192 + { + APF_PROTOCOL_VERSION_MESSAGE *pMessage = (APF_PROTOCOL_VERSION_MESSAGE *)rxBuffer; + struct LMEProtocolVersionMessage protVersion; + protVersion.MajorVersion = ntohl(pMessage->MajorVersion); + protVersion.MinorVersion = ntohl(pMessage->MinorVersion); + protVersion.TriggerReason = (APF_TRIGGER_REASON)ntohl(pMessage->TriggerReason); + + switch (module->handshakingStatus) + { + case LME_AGREED: + case LME_NOT_INITIATED: + { + LME_ProtocolVersion(lmemodule, 1, 0, protVersion.TriggerReason); + } + case LME_INITIATED: + if (protVersion.MajorVersion != 1 || protVersion.MinorVersion != 0) + { + LMSDEBUG("LME Version %d.%d is not supported.\r\n", protVersion.MajorVersion, protVersion.MinorVersion); + LME_Disconnect(lmemodule, APF_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED); + LME_Deinit(lmemodule); + sem_post(&(module->Lock)); + return; + } + module->AmtProtVersionMajor = protVersion.MajorVersion; + module->AmtProtVersionMinor = protVersion.MinorVersion; + module->handshakingStatus = LME_AGREED; + break; + default: + LME_Disconnect(lmemodule, APF_DISCONNECT_BY_APPLICATION); + LME_Deinit(lmemodule); + break; + } + } + break; + case APF_USERAUTH_REQUEST: // 50 + { + // _lme.UserAuthSuccess(); + } + break; + default: + // Unknown request. + LMSDEBUG("**Unknown LME command: %d\r\n", ((unsigned char*)rxBuffer)[0]); + LME_Disconnect(lmemodule, APF_DISCONNECT_PROTOCOL_ERROR); + LME_Deinit(lmemodule); + break; + } + + sem_post(&(module->Lock)); + //if (disconnects > 0) ILibLMS_LaunchHoldingSessions(module); // If disconnects is set to anything, we have free session slots we can fill up. +} + +void ILibLMS_OnReceive(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncServerSocket_OnInterrupt *OnInterrupt, void **user, int *PAUSE) +{ + int r, maxread = endPointer; + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + struct LMEChannel* channel = (struct LMEChannel*)*user; + + UNREFERENCED_PARAMETER( AsyncServerSocketModule ); + UNREFERENCED_PARAMETER( ConnectionToken ); + UNREFERENCED_PARAMETER( OnInterrupt ); + UNREFERENCED_PARAMETER( PAUSE ); + + if (channel == NULL) return; + sem_wait(&(module->Lock)); + if (channel->socketmodule != ConnectionToken) { sem_post(&(module->Lock)); ILibAsyncSocket_Disconnect(ConnectionToken); return; } + if (channel->txwindow < endPointer) maxread = channel->txwindow; + if (channel->status != LME_CS_CONNECTED || maxread == 0) { *PAUSE = 1; sem_post(&(module->Lock)); return; } + //printf("%.*s", maxread, buffer); + r = LME_ChannelData(&(module->MeConnection), channel->amtid, maxread, (unsigned char*)buffer); + //LMSDEBUG("ILibLMS_OnReceive, status = %d, txwindow = %d, endPointer = %d, r = %d\r\n", channel->status, channel->txwindow, endPointer, r); + if (r != maxread) + { + //LMSDEBUG("ILibLMS_OnReceive, DISCONNECT %d\r\n", channel->ourid); + sem_post(&(module->Lock)); + ILibAsyncSocket_Disconnect(ConnectionToken); // Drop the connection + return; + } + channel->txwindow -= maxread; + *p_beginPointer = maxread; + sem_post(&(module->Lock)); +} + +void ILibLMS_SetupConnection(struct ILibLMS_StateModule* module, int i) +{ + int rport = 0; + char* laddr = NULL; + struct sockaddr_in6 remoteAddress; + + if (module->Sessions[i].sockettype == 0) + { + // Fetch the socket remote TCP address + ILibAsyncSocket_GetRemoteInterface(module->Sessions[i].socketmodule, (struct sockaddr*)&remoteAddress); + if (remoteAddress.sin6_family == AF_INET6) + { + laddr = "::1"; // TODO: decode this properly into a string + rport = ntohs(remoteAddress.sin6_port); + } + else + { + laddr = "127.0.0.1"; // TODO: decode this properly into a string + rport = ntohs(((struct sockaddr_in*)(&remoteAddress))->sin_port); + } + } + else + { + // Fetch the socket remote web socket address + laddr = "127.0.0.1"; // TODO: decode this properly into a string + rport = 123; // TODO: decode the remote port + } + + // Setup a new LME session + LME_ChannelOpenForwardedRequest(&(module->MeConnection), (unsigned int)i, laddr, module->Sessions[i].localport, laddr, rport); + //LMSDEBUG("ILibLMS_OnReceive, CONNECT %d\r\n", i); +} + + +int OnConnect = 0; +void ILibLMS_OnConnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void **user) +{ + OnConnect++; + LMSDEBUG("OnCOnnect:%d", OnConnect); + int i, sessionid = -1, activecount = 0; + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + + sem_wait(&(module->Lock)); + + // Count the number of active sessions + activecount = LMS_MAX_SESSIONS; + for (i = 0; i < LMS_MAX_SESSIONS; i++) { + if (module->Sessions[i].status == LME_CS_FREE || module->Sessions[i].status == LME_CS_CONNECTION_WAIT) { + activecount--; + } + } + + sessionid=GetFreeSlot(module); + + // Look for an empty session + //for (i = 1; i < LMS_MAX_SESSIONS; i++) { + // if (module->Sessions[i].status == LME_CS_FREE) { + // sessionid = i; + // break; + // } + //} + if (sessionid == -1) + { + sem_post(&(module->Lock)); + LMSDEBUG("ILibLMS_OnConnect NO SESSION SLOTS AVAILABLE\r\n"); + ILibAsyncSocket_Disconnect(ConnectionToken); // Drop the connection + return; + } + i = sessionid; + + // LMSDEBUG("USING SLOT OUR:%d\r\n", i); + module->Sessions[i].amtid = -1; + module->Sessions[i].ourid = i; + module->Sessions[i].sockettype = 0; // TCP Type + module->Sessions[i].socketmodule = ConnectionToken; + module->Sessions[i].rxwindow = 0; + module->Sessions[i].txwindow = 0; + module->Sessions[i].localport = ILibAsyncServerSocket_GetPortNumber(AsyncServerSocketModule); + module->Sessions[i].errorcount = 0; + *user = &(module->Sessions[i]); + + /* + if (activecount >= LMS_MAX_CONNECTIONS) + { + module->Sessions[i].status = LME_CS_CONNECTION_WAIT; + LMSDEBUG("ILibLMS_OnConnect %d (HOLDING)\r\n", i); + } + else + */ + { + module->Sessions[i].status = LME_CS_PENDING_CONNECT; + LMSDEBUG("ILibLMS_OnConnect %d\r\n", i); + ILibLMS_SetupConnection(module, i); + } + + sem_post(&(module->Lock)); +} + +void ILibLMS_OnDisconnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) +{ + int disconnects = 0; + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + struct LMEChannel* channel = (struct LMEChannel*)user; + UNREFERENCED_PARAMETER( ConnectionToken ); + + sem_wait(&(module->Lock)); + + if (channel == NULL || channel->socketmodule != ConnectionToken) + { + LMSDEBUG("****ILibLMS_OnDisconnect EXIT\r\n"); + sem_post(&(module->Lock)); + return; + } + + LMSDEBUG("ILibLMS_OnDisconnect, Channel %d, %d\r\n", channel->ourid, ConnectionToken); + + if (channel->status == LME_CS_CONNECTED) + { + channel->status = LME_CS_PENDING_LMS_DISCONNECT; + if (channel->amtid!=-1 && !LME_ChannelClose(&(module->MeConnection), channel->amtid, channel->ourid)) + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by LMS because of failed close\r\n", channel->ourid); + } + else + { + LMSDEBUG("Channel %d now PENDING_LMS_DISCONNECT by LMS\r\n", channel->ourid); + //LMSDEBUG("LME_ChannelClose OK\r\n"); + } + } + else if (channel->status == LME_CS_PENDING_CONNECT) + { + channel->status = LME_CS_PENDING_LMS_DISCONNECT; + LMSDEBUG("Channel %d now PENDING_DISCONNECT by LMS\r\n", channel->ourid); + } + else if (channel->status == LME_CS_CONNECTION_WAIT) + { + channel->status = LME_CS_FREE; + disconnects++; + LMSDEBUG("Channel %d now FREE by LMS\r\n", channel->ourid); + } + //LMSDEBUG("ILibLMS_OnDisconnect, DISCONNECT %d, status = %d\r\n", channel->ourid, channel->status); + sem_post(&(module->Lock)); + + //if (disconnects > 0) ILibLMS_LaunchHoldingSessions(module); +} + +void ILibLMS_OnSendOK(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) +{ + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + struct LMEChannel* channel = (struct LMEChannel*)user; + UNREFERENCED_PARAMETER( ConnectionToken ); + + // Ok to send more on this socket, adjust the window + sem_wait(&(module->Lock)); + if (channel->rxwindow != 0) + { + //LMSDEBUG("LME_ChannelWindowAdjust id=%d, rxwindow=%d\r\n", channel->amtid, channel->rxwindow); + LME_ChannelWindowAdjust(&(module->MeConnection), channel->amtid, channel->rxwindow); + channel->rxwindow = 0; + } + sem_post(&(module->Lock)); +} +// Private method called when the chain is destroyed, we want to do our cleanup here +void ILibLMS_Destroy(void *object) +{ + struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)object; + UNREFERENCED_PARAMETER( object ); + + sem_wait(&(module->Lock)); + LME_Disconnect(&(module->MeConnection), APF_DISCONNECT_BY_APPLICATION); + LME_Exit(&(module->MeConnection)); + sem_destroy(&(module->Lock)); + if (IlibExternLMS == module) { IlibExternLMS = NULL; } // Clear the global reference to the the LMS module. +} + +// Create a new Reflector module. +struct ILibLMS_StateModule *ILibLMS_Create(void *Chain) +{ + struct ILibLMS_StateModule *module; + + // Allocate the new module + module = (struct ILibLMS_StateModule*)malloc(sizeof(struct ILibLMS_StateModule)); + if (module == NULL) { ILIBCRITICALEXIT(254); } + memset(module, 0, sizeof(struct ILibLMS_StateModule)); + + // Setup MEI with LMS interface, if we can't return null + if (LME_Init(&(module->MeConnection), &ILibLMS_MEICallback, module) == 0) { + free(module); + return NULL; + } + + // Setup the module + module->ChainLink.DestroyHandler = &ILibLMS_Destroy; + module->ChainLink.ParentChain = Chain; + sem_init(&(module->Lock), 0, 1); + + // TCP servers + module->Server1 = ILibCreateAsyncServerSocketModule(Chain, LMS_MAX_CONNECTIONS, 16992, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); + if (module->Server1 == NULL) { sem_destroy(&(module->Lock)); free(module); return NULL; } + ILibAsyncServerSocket_SetTag(module->Server1, module); + + module->Server2 = ILibCreateAsyncServerSocketModule(Chain, LMS_MAX_CONNECTIONS, 16993, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); + if (module->Server2 == NULL) { sem_destroy(&(module->Lock)); free(module->Server1); free(module); return NULL; } + ILibAsyncServerSocket_SetTag(module->Server2, module); + + + + + // TODO: US6986 - The meshcommander site should not be served on localhost:16994. + // This code should be commented out - in the future(late 2018) IMC could be put there instead. + // Web Server + //module->WebServer = ILibWebServer_CreateEx(Chain, 8, 16994, 2, &ILibLMS_WebServer_OnSession, module); + + IlibExternLMS = module; // Set the global reference to the LMS module. + ILibAddToChain(Chain, module); // Add this module to the chain. + return module; +} + +#endif diff --git a/MicroLMS/microstack/ILibLMS.h b/MicroLMS/microstack/ILibLMS.h new file mode 100644 index 0000000..a0fcf7e --- /dev/null +++ b/MicroLMS/microstack/ILibLMS.h @@ -0,0 +1,26 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if !defined(_NOHECI) + +#ifndef __ILibLMS__ +#define __ILibLMS__ +#include "ILibAsyncServerSocket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ILibLMS_StateModule *ILibLMS_Create(void *Chain); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + diff --git a/MicroLMS/microstack/ILibParsers.c b/MicroLMS/microstack/ILibParsers.c new file mode 100644 index 0000000..0e4565c --- /dev/null +++ b/MicroLMS/microstack/ILibParsers.c @@ -0,0 +1,8763 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#if defined(_POSIX) +#ifndef _VX_CPU +#include +#endif +#include +#include +#include +#include +#define UNREFERENCED_PARAMETER(P) (P) +#endif +#ifdef _MINCORE +#define strncmp(a,b,c) strcmp(a,b) +#endif + +#if defined(WIN32) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#ifdef WIN32 +#include +#include +#include +#ifndef _MINCORE +#include +#endif +#include +#endif + +#include + +#if defined(WIN32) && !defined(WSA_FLAG_NO_HANDLE_INHERIT) +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +#include "ILibParsers.h" +#include "ILibRemoteLogging.h" + +#define MINPORTNUMBER 50000 +#define PORTNUMBERRANGE 15000 +#define UPNP_MAX_WAIT 86400 // 24 Hours + +#ifdef _REMOTELOGGINGSERVER +#include "ILibWebServer.h" +#endif + +#include + +#if defined(MICROSTACK_NOTLS) +char utils_HexTable[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; +char utils_HexTable2[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +#endif + +// This is to compile on Windows XP, not needed at runtime +#if !defined(IPV6_V6ONLY) +#define IPV6_V6ONLY 27 +#endif + +#if defined(WIN32) +static sem_t ILibChainLock = { 0 }; +#else +#ifndef errno +extern int errno; +#endif +static sem_t ILibChainLock; +#endif + +#ifdef WIN32 +char* ILibCriticalLogFilename = NULL; +#else +char* ILibCriticalLogFilename = NULL; +#endif + +#ifdef _MINCORE // Win32 mincore needs these defines +CONST IN6_ADDR in6addr_any = { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } }; +CONST IN6_ADDR in6addr_loopback = { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } } }; +#endif + +char ILibScratchPad[4096]; // General buffer +char ILibScratchPad2[65536]; // Often used for UDP packet processing +char ILibScratchPad_WWWAuth1[1024]; // Used to construct WWW auth digest request +char ILibScratchPad_WWWAuth2[4096]; // Used to construct WWW auth digest request + +void* gILibChain = NULL; // Global Chain Instance used for Remote Logging when a chain instance isn't otherwise exposed + +#define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) + +long long ILibGetUptime(); +static int ILibChainLock_RefCounter = 0; + +static int malloc_counter = 0; +void* dbg_malloc(int sz) +{ + ++malloc_counter; + return((void*)malloc(sz)); +} +void dbg_free(void* ptr) +{ + --malloc_counter; + free(ptr); +} +int dbg_GetCount() +{ + return(malloc_counter); +} + +unsigned long long ILibHTONLL(unsigned long long v) +{ + { union { unsigned long lv[2]; unsigned long long llv; } u; u.lv[0] = htonl(v >> 32); u.lv[1] = htonl(v & 0xFFFFFFFFULL); return u.llv; } +} +unsigned long long ILibNTOHLL(unsigned long long v) +{ + { union { unsigned long lv[2]; unsigned long long llv; } u; u.llv = v; return ((unsigned long long)ntohl(u.lv[0]) << 32) | (unsigned long long)ntohl(u.lv[1]); } +} + +//! Returns X where 2^X = Specified Integer +/*! + \param number Integer value to use to solve equation + \return X where 2^X = Specified Integer +*/ +int ILibWhichPowerOfTwo(int number) +{ + int retVal = 0; + if(number < 1) {return(-1);} + while((number = number >> 1) != 0) {++retVal;} + return(retVal); +} + + +// +// All of the following structures are meant to be private internal structures +// +typedef struct ILibSparseArray_Node +{ + int index; + void* ptr; +}ILibSparseArray_Node; +typedef struct ILibSparseArray_Root +{ + ILibSparseArray_Node* bucket; + int bucketSize; + ILibSparseArray_Bucketizer bucketizer; + sem_t LOCK; + int userMemorySize; +}ILibSparseArray_Root; +const int ILibMemory_SparseArray_CONTAINERSIZE = sizeof(ILibSparseArray_Root); + +typedef enum ILibHashtable_Flags +{ + ILibHashtable_Flags_NONE = 0x00, + ILibHashtable_Flags_ADD = 0x01, + ILibHashtable_Flags_REMOVE = 0x02 +}ILibHashtable_Flags; +typedef struct ILibHashtable_Node +{ + struct ILibHashtable_Node* next; + struct ILibHashtable_Node* prev; + void* Key1; + char* Key2; + int Key2Len; + void *Data; +}ILibHashtable_Node; +typedef struct ILibHashtable_Root +{ + ILibSparseArray table; + ILibHashtable_Hash_Func hashFunc; + ILibSparseArray_Bucketizer bucketizer; +}ILibHashtable_Root; + + + +struct ILibStackNode +{ + void *Data; + struct ILibStackNode *Next; +}; +struct ILibQueueNode +{ + struct ILibStackNode *Head; + struct ILibStackNode *Tail; + sem_t LOCK; +}; +struct HashNode_Root +{ + struct HashNode *Root; + int CaseInSensitive; + sem_t LOCK; +}; +struct HashNode +{ + struct HashNode *Next; + struct HashNode *Prev; + int KeyHash; + char *KeyValue; + int KeyLength; + void *Data; + int DataEx; +}; +struct HashNodeEnumerator +{ + struct HashNode *node; +}; +typedef struct ILibLinkedListNode +{ + void *Data; + struct ILibLinkedListNode_Root *Root; + struct ILibLinkedListNode *Next; + struct ILibLinkedListNode *Previous; +}ILibLinkedListNode; +typedef struct ILibLinkedListNode_Root +{ + sem_t LOCK; + long count; + void* Tag; + struct ILibLinkedListNode *Head; + struct ILibLinkedListNode *Tail; + void *ExtraMemory; +}ILibLinkedListNode_Root; + +struct ILibReaderWriterLock_Data +{ + ILibChain_PreSelect Pre; + ILibChain_PostSelect Post; + ILibChain_Destroy Destroy; + + sem_t WriteLock; + sem_t ReadLock; + sem_t CounterLock; + sem_t ExitLock; + int ActiveReaders; + int WaitingReaders; + int PendingWriters; + int Exit; +}; +//! Get the amount of pending data in the send buffer of the ILibTransport Abstraction +/*! + \param transport ILibTransport Abstraction to query + \return Number of pending bytes +*/ +unsigned int ILibTransport_PendingBytesToSend(void *transport) +{ + return(((ILibTransport*)transport)->PendingBytesPtr(transport)); +} +//! Send data on an ILibTransport Abstraction +/*! + \param transport ILibTransport Abstraction + \param buffer Data to send + \param bufferLength Data Length to send + \param ownership Who will free the data to send? + \param done Data complete? + \return Send Status +*/ +ILibTransport_DoneState ILibTransport_Send(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done) +{ + return(((ILibTransport*)transport)->SendPtr(transport, buffer, bufferLength, ownership, done)); +} +//! Close an ILibTransport Abstraction +/*! + \param transport ILibTransport Abstraction to close +*/ +void ILibTransport_Close(void *transport) +{ + if (transport != NULL) { ((ILibTransport*)transport)->ClosePtr(transport); } +} + + +#if defined(MICROSTACK_NOTLS) +int util_random_seeded = 0; +void __fastcall util_random(int length, char* result) +{ + short val; + int i; + + if (util_random_seeded == 0) + { + time_t t; + srand((unsigned int)time(&t)); + util_random_seeded = 1; + } + + for (i = 0; i < length; i += 2) + { + val = rand(); + memcpy_s(result + i, length - i, &val, (length - i) >= 2 ? 2 : (length - i)); + } +} +char* __fastcall util_tohex(char* data, int len, char* out) +{ + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for (i = 0; i < len; i++) + { + *(p++) = utils_HexTable[((unsigned char)data[i]) >> 4]; + *(p++) = utils_HexTable[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} +int __fastcall util_hexToint(char *hexString, int hexStringLength) +{ + int i, res = 0; + + // Ignore the leading zeroes + while (*hexString == '0' && hexStringLength > 0) { hexString++; hexStringLength--; } + + // Process the rest of the string + for (i = 0; i < hexStringLength; i++) + { + if (hexString[i] >= '0' && hexString[i] <= '9') { res = (res << 4) + (hexString[i] - '0'); } + else if (hexString[i] >= 'a' && hexString[i] <= 'f') { res = (res << 4) + (hexString[i] - 'a' + 10); } + else if (hexString[i] >= 'A' && hexString[i] <= 'F') { res = (res << 4) + (hexString[i] - 'A' + 10); } + } + return res; +} + +// Convert hex string to int +int __fastcall util_hexToBuf(char *hexString, int hexStringLength, char* output) +{ + int i, x = hexStringLength / 2; + for (i = 0; i < x; i++) { output[i] = (char)util_hexToint(hexString + (i * 2), 2); } + return i; +} +#endif + + +#ifdef WIN32 +int ILibGetLocalIPAddressNetMask(unsigned int address) +{ + SOCKET s; + DWORD bytesReturned; + SOCKADDR_IN* pAddrInet; + INTERFACE_INFO localAddr[10]; // Assume there will be no more than 10 IP interfaces + int numLocalAddr; + int i; + + if ((s = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0)) == INVALID_SOCKET) + { + fprintf(stderr, "Socket creation failed\n"); + return(0); + } + + // Enumerate all IP interfaces + if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, &localAddr, sizeof(localAddr), &bytesReturned, NULL, NULL) != SOCKET_ERROR) + { + fprintf(stderr, "WSAIoctl fails with error %d\n", (int)GetLastError()); + closesocket(s); + return(0); + } + + closesocket(s); + + // Display interface information + numLocalAddr = (bytesReturned/sizeof(INTERFACE_INFO)); + for (i=0; isin_addr.S_un.S_addr == address) + { + pAddrInet = (SOCKADDR_IN*)&localAddr[i].iiNetmask; + return(pAddrInet->sin_addr.s_addr); + } + } + return(0); +} +#endif + +//! Fetches the list of valid IPv4 adapters +/*! + \param[out] addresslist sockaddr_in array of adapters + \param includeloopback [0 = Include Loopback, 1 = Exclude Loopback] + \return number of adapters +*/ +int ILibGetLocalIPv4AddressList(struct sockaddr_in** addresslist, int includeloopback) +{ +#ifdef WIN32 + DWORD i, j = 0; + SOCKET sock; + DWORD interfaces_count; + INTERFACE_INFO interfaces[128]; + + // Fetch the list of interfaces + *addresslist = NULL; + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { return(0); } + if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &interfaces, 128 * sizeof(INTERFACE_INFO), &interfaces_count, NULL, NULL) != 0) { interfaces_count = 0; } + + if (interfaces_count > 0) + { + interfaces_count = interfaces_count / sizeof(INTERFACE_INFO); + for (i = 0; i < interfaces_count; i++) { if (includeloopback != 0 || interfaces[i].iiAddress.AddressIn.sin_addr.S_un.S_addr != 0x0100007F) j++; } // M S Static Analysis says this could read outside readable range, but that is only true if WSAioct returns the wrong value in interfaces_count + + // Allocate the IPv4 list and copy the elements into it + if ((*addresslist = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in) * j)) == NULL) ILIBCRITICALEXIT(254); + j = 0; + for (i = 0; i < interfaces_count; i++) + { + if (includeloopback != 0 || interfaces[i].iiAddress.AddressIn.sin_addr.S_un.S_addr != 0x0100007F) + { + memcpy_s(&((*addresslist)[j++]), sizeof(struct sockaddr_in), &(interfaces[i].iiAddress.AddressIn), sizeof(struct sockaddr_in)); + } + } + } + closesocket(sock); + return j; +#elif defined(_POSIX) + // + // Posix will also use an Ioctl call to get the IPAddress List + // + char szBuffer[16*sizeof(struct ifreq)]; + struct ifconf ifConf; + struct ifreq ifReq; + int nResult; + int LocalSock; + struct sockaddr_in LocalAddr; + //int tempresults[16]; + int ctr = 0; + int i; + if ((*addresslist = malloc(sizeof(struct sockaddr_in) * 32)) == NULL) ILIBCRITICALEXIT(254); // TODO: Support more than 32 adapters + + // Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. + if ((LocalSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "socket(..) error in ILibGetLocalIPv4AddressList()"); + ILIBCRITICALERREXIT(253); + } + // Get the interface configuration information... + ifConf.ifc_len = sizeof szBuffer; + ifConf.ifc_ifcu.ifcu_buf = (caddr_t)szBuffer; + nResult = ioctl(LocalSock, SIOCGIFCONF, &ifConf); + + if (nResult < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFCONF) error in ILibGetLocalIPv4AddressList()"); + ILIBCRITICALERREXIT(253); + } + // Cycle through the list of interfaces looking for IP addresses. + for (i = 0;(i < ifConf.ifc_len);) + { + struct ifreq *pifReq = (struct ifreq *)((caddr_t)ifConf.ifc_req + i); + i += sizeof *pifReq; + // See if this is the sort of interface we want to deal with. + strcpy_s (ifReq.ifr_name, sizeof(ifReq.ifr_name), pifReq -> ifr_name); + if (ioctl(LocalSock, SIOCGIFFLAGS, &ifReq) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFFLAGS) error in ILibGetLocalIPv4AddressList()"); + ILIBCRITICALERREXIT(253); + } + // Skip loopback, point-to-point and down interfaces, except don't skip down interfaces + // if we're trying to get a list of configurable interfaces. + if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) + { + continue; + } + if (pifReq->ifr_addr.sa_family == AF_INET) + { + // Get a pointer to the address. + memcpy_s(&LocalAddr, sizeof(LocalAddr), &pifReq -> ifr_addr, sizeof pifReq -> ifr_addr); + if (includeloopback != 0 || LocalAddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) + { + //tempresults[ctr] = LocalAddr.sin_addr.s_addr; + memcpy_s(&((*addresslist)[ctr]), sizeof(struct sockaddr_in), &LocalAddr, sizeof(struct sockaddr_in)); + ++ctr; + } + } + } + close(LocalSock); + //*pp_int = (int*)malloc(sizeof(int)*(ctr)); + //memcpy(*pp_int,tempresults,sizeof(int)*ctr); + + return ctr; +#endif +} + +/* +// Get the list of valid IPv6 adapters, skip loopback and other junk adapters. +int ILibGetLocalIPv6AddressList(struct sockaddr_in6** addresslist) +{ +#if defined(WINSOCK2) +PIP_ADAPTER_ADDRESSES pAddresses = NULL; +PIP_ADAPTER_ADDRESSES pAddressesPtr = NULL; +PIP_ADAPTER_UNICAST_ADDRESS_LH AddrPtr; +ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); +int AddrCount = 0; +struct sockaddr_in6 *ptr; + +// Perform first call +pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen); +if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) +{ +free(pAddresses); +pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen); +} + +// Perform second call +if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) != NO_ERROR) +{ +free(pAddresses); +return 0; +} + +// Count the total number of local IPv6 addresses +pAddressesPtr = pAddresses; +while (pAddressesPtr != NULL) +{ +// Skip any loopback adapters +if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->NoMulticast == 0) +{ +// For each adapter +AddrPtr = pAddressesPtr->FirstUnicastAddress; +while (AddrPtr != NULL) +{ +AddrCount++; +AddrPtr = AddrPtr->Next; +} +} +pAddressesPtr = pAddressesPtr->Next; +} + +// Allocate the array of IPv6 addresses +*addresslist = ptr = malloc(AddrCount * sizeof(struct sockaddr_in6)); + +// Copy each address into the array +pAddressesPtr = pAddresses; +while (pAddressesPtr != NULL) +{ +// Skip any loopback adapters +if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->NoMulticast == 0) +{ +// For each adapter +AddrPtr = pAddressesPtr->FirstUnicastAddress; +while (AddrPtr != NULL) +{ +memcpy(ptr, AddrPtr->Address.lpSockaddr, sizeof(struct sockaddr_in6)); +ptr++; +AddrPtr = AddrPtr->Next; +} +} +pAddressesPtr = pAddressesPtr->Next; +} + +free(pAddresses); +return AddrCount; +#endif +} +*/ + +//! Get the list of valid IPv6 adapter indexes, skip loopback and other junk adapters. +/*! + \param[out] indexlist List of valid IPv6 Adapter indexes + \return number of adapters +*/ +int ILibGetLocalIPv6IndexList(int** indexlist) +{ +#ifdef _MINCORE + return 0; +#else +#if defined(WIN32) + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pAddressesPtr = NULL; + ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); + int AddrCount = 0; + int* ptr; + + // Perform first call + if ((pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen)) == NULL) ILIBCRITICALEXIT(254); + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) + { + free(pAddresses); + if ((pAddresses = (IP_ADAPTER_ADDRESSES *)malloc(outBufLen)) == NULL) ILIBCRITICALEXIT(254); + } + + // Perform second call + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) != NO_ERROR) + { + free(pAddresses); + return 0; + } + + // Count the total number of local IPv6 addresses + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + // For each adapter + AddrCount++; + } + pAddressesPtr = pAddressesPtr->Next; + } + + // Allocate the array of IPv6 addresses + if ((*indexlist = ptr = (int*)malloc(AddrCount * sizeof(int))) == NULL) ILIBCRITICALEXIT(254); + + // Copy each address into the array + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + *ptr = pAddressesPtr->IfIndex; + ptr++; + } + pAddressesPtr = pAddressesPtr->Next; + } + + free(pAddresses); + return AddrCount; +#elif defined (_POSIX) + *indexlist = (int *)malloc(sizeof(int)); + *indexlist[0] = 0; + return 1; // TODO +#endif +#endif +} + +//! Get the list of valid IPv6 adapters, skip loopback and other junk adapters. +/*! + \param[out] list sockaddr_in6 array of valid IPv6 adapters + \return number of adapters +*/ +int ILibGetLocalIPv6List(struct sockaddr_in6** list) +{ +#ifdef _MINCORE + return 0; +#else +#if defined(WIN32) + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pAddressesPtr = NULL; + ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); + int AddrCount = 0; + struct sockaddr_in6* ptr; + *list = NULL; + + // Perform first call + pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) == ERROR_BUFFER_OVERFLOW) + { + free(pAddresses); + pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); + } + + // Perform second call + if (GetAdaptersAddresses(AF_INET6, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST, NULL, pAddresses, &outBufLen) != NO_ERROR) + { + free(pAddresses); + return 0; // Call failed, return no IPv6 addresses. This happens on Windows XP. + } + + // Count the total number of local IPv6 addresses + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + // For each adapter + AddrCount++; + } + pAddressesPtr = pAddressesPtr->Next; + } + + if (AddrCount > 0) + { + // Allocate the array of IPv6 addresses + *list = ptr = (struct sockaddr_in6*)malloc(AddrCount * sizeof(struct sockaddr_in6)); + + // Copy each address into the array + pAddressesPtr = pAddresses; + while (pAddressesPtr != NULL) + { + // Skip any loopback adapters + if (pAddressesPtr->PhysicalAddressLength != 0 && pAddressesPtr->ReceiveOnly != 1 && pAddressesPtr->NoMulticast != 1 && pAddressesPtr->OperStatus == IfOperStatusUp) + { + memcpy_s(ptr, sizeof(struct sockaddr_in6), pAddressesPtr->FirstUnicastAddress->Address.lpSockaddr, pAddressesPtr->FirstUnicastAddress->Address.iSockaddrLength); + ptr++; + } + pAddressesPtr = pAddressesPtr->Next; + } + } + + free(pAddresses); + return AddrCount; +#elif defined (_POSIX) + //*indexlist = malloc(sizeof(int)); + //*indexlist[0] = 0; + //return 1; // TODO + *list = NULL; + return 0; +#endif +#endif +} + + +/*! \fn ILibGetLocalIPAddressList(int** pp_int) +\brief Gets a list of IP Addresses +\par +\b NOTE: \a pp_int must be freed +\param[out] pp_int Array of IP Addresses +\return Number of IP Addresses returned +*/ +int ILibGetLocalIPAddressList(int** pp_int) +{ +#if defined(WIN32) + int i; + char buffer[16 * sizeof(SOCKET_ADDRESS_LIST)]; + DWORD bufferSize; + SOCKET sock; + LPSOCKADDR addr; + + *pp_int = NULL; + if ((sock = socket(AF_INET,SOCK_DGRAM,0)) == -1) return 0; + + if (WSAIoctl(sock, SIO_ADDRESS_LIST_QUERY, NULL, 0, buffer, 16 * sizeof(SOCKET_ADDRESS_LIST), &bufferSize, NULL, NULL) == SOCKET_ERROR) + { + closesocket(sock); + return 0; + } + + if ((*pp_int = (int*)malloc(sizeof(int) * (1 + ((SOCKET_ADDRESS_LIST*)buffer)->iAddressCount))) == NULL) ILIBCRITICALEXIT(254); + addr = ((((SOCKET_ADDRESS_LIST *)buffer))->Address)->lpSockaddr; + for (i = 0; i < ((SOCKET_ADDRESS_LIST*)buffer)->iAddressCount && i < 15; ++i) + { + (*pp_int)[i] = (((struct sockaddr_in*)addr)[i]).sin_addr.S_un.S_addr; + } + (*pp_int)[i] = inet_addr("127.0.0.1"); + closesocket(sock); + return(1 + ((SOCKET_ADDRESS_LIST*)buffer)->iAddressCount); +#elif defined(_POSIX) + // + // Posix will also use an Ioctl call to get the IPAddress List + // + char szBuffer[16*sizeof(struct ifreq)]; + struct ifconf ifConf; + struct ifreq ifReq; + int nResult; + int LocalSock; + struct sockaddr_in LocalAddr; + int tempresults[16]; + int ctr=0; + int i; + /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */ + if ((LocalSock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "socket(...) error in ILibGetLocalIPAddressList()"); + ILIBCRITICALERREXIT(253); + } + /* Get the interface configuration information... */ + ifConf.ifc_len = sizeof szBuffer; + ifConf.ifc_ifcu.ifcu_buf = (caddr_t)szBuffer; + nResult = ioctl(LocalSock, SIOCGIFCONF, &ifConf); + if (nResult < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFCONF) error in ILibGetLocalIPAddressList()"); + ILIBCRITICALERREXIT(253); + } + /* Cycle through the list of interfaces looking for IP addresses. */ + for (i = 0;(i < ifConf.ifc_len);) + { + struct ifreq *pifReq = (struct ifreq *)((caddr_t)ifConf.ifc_req + i); + i += sizeof *pifReq; + /* See if this is the sort of interface we want to deal with. */ + strcpy_s (ifReq.ifr_name, sizeof(ifReq.ifr_name), pifReq -> ifr_name); + if (ioctl (LocalSock, SIOCGIFFLAGS, &ifReq) < 0) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ioctl(SIOCGIFFLAGS) error in ILibGetLocalIPAddressList()"); + ILIBCRITICALERREXIT(253); + } + /* Skip loopback, point-to-point and down interfaces, */ + /* except don't skip down interfaces */ + /* if we're trying to get a list of configurable interfaces. */ + if ((ifReq.ifr_flags & IFF_LOOPBACK) || (!(ifReq.ifr_flags & IFF_UP))) + { + continue; + } + if (pifReq -> ifr_addr.sa_family == AF_INET) + { + /* Get a pointer to the address... */ + memcpy_s (&LocalAddr, sizeof(LocalAddr), &pifReq -> ifr_addr, sizeof pifReq -> ifr_addr); + if (LocalAddr.sin_addr.s_addr != htonl (INADDR_LOOPBACK)) + { + tempresults[ctr] = LocalAddr.sin_addr.s_addr; + ++ctr; + } + } + } + close(LocalSock); + if ((*pp_int = (int*)malloc(sizeof(int)*(ctr))) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(*pp_int, sizeof(int)*ctr, tempresults, sizeof(int)*ctr); + return(ctr); +#endif +} + + + +struct ILibChain_SubChain +{ + ILibChain_PreSelect PreSelect; + ILibChain_PostSelect PostSelect; + ILibChain_Destroy Destroy; + + void *subChain; +}; + +typedef struct LifeTimeMonitorData +{ + long long ExpirationTick; + void *data; + ILibLifeTime_OnCallback CallbackPtr; + ILibLifeTime_OnCallback DestroyPtr; +}LifeTimeMonitorData; +struct ILibLifeTime +{ + ILibChain_Link ChainLink; + struct LifeTimeMonitorData *LM; + long long NextTriggerTick; + + void *Reserved; + + void *ObjectList; + int ObjectCount; +}; + +typedef struct ILibChain_Link_Hook +{ + ILibChain_EventHookHandler Handler; + int MaxTimeout; +}ILibChain_Link_Hook; +struct ILibBaseChain_SafeData +{ + void *Chain; + void *Object; +}; + +typedef enum ILibChain_ContinuationStates +{ + ILibChain_ContinuationState_INACTIVE = 0, + ILibChain_ContinuationState_CONTINUE = 1, + ILibChain_ContinuationState_END_CONTINUE= 2 +}ILibChain_ContinuationStates; + +typedef struct ILibBaseChain +{ + int TerminateFlag; + int RunningFlag; +#ifdef _REMOTELOGGING + ILibRemoteLogging ChainLogger; +#ifdef _REMOTELOGGINGSERVER + void* LoggingWebServer; + ILibTransport* LoggingWebServerFileTransport; +#endif +#endif + /* + * + * DO NOT MODIFY STRUCT DEFINITION ABOVE THIS COMMENT BLOCK + * + */ + + +#if defined(WIN32) + DWORD ChainThreadID; + HANDLE ChainProcessHandle; + HANDLE MicrostackThreadHandle; + CONTEXT MicrostackThreadContext; +#else + pthread_t ChainThreadID; +#endif +#if defined(WIN32) + SOCKET TerminateSock; +#else + FILE *TerminateReadPipe; + FILE *TerminateWritePipe; +#endif + + void *Timer; + void *Reserved; + ILibLinkedList Links; + ILibLinkedList LinksPendingDelete; + ILibHashtable ChainStash; + ILibChain_ContinuationStates continuationState; + unsigned int PreSelectCount; + unsigned int PostSelectCount; + void *WatchDogThread; +#ifdef WIN32 + HANDLE WatchDogTerminator; +#else + int WatchDogTerminator[2]; +#endif + void *node; +}ILibBaseChain; + +const int ILibMemory_CHAIN_CONTAINERSIZE = sizeof(ILibBaseChain); + +void* ILibMemory_Allocate(int containerSize, int extraMemorySize, void** allocatedContainer, void **extraMemory) +{ + char* retVal = (char*)malloc(containerSize + extraMemorySize + (extraMemorySize > 0 ? 4 : 0)); + if (retVal == NULL) { ILIBCRITICALEXIT(254); } + memset(retVal, 0, containerSize + extraMemorySize + (extraMemorySize > 0 ? 4 : 0)); + if (extraMemorySize > 0) + { + ((int*)(retVal + containerSize))[0] = extraMemorySize; + if (extraMemory != NULL) { *extraMemory = (retVal + containerSize + 4); } + } + else + { + if (extraMemory != NULL) { *extraMemory = NULL; } + } + if (allocatedContainer != NULL) { *allocatedContainer = retVal; } + return(retVal); +} +ILibExportMethod void* ILibMemory_GetExtraMemory(void *container, int containerSize) +{ + return((char*)container + 4 + containerSize); +} +int ILibMemory_GetExtraMemorySize(void* extraMemory) +{ + if (extraMemory == NULL) { return(0); } + return(((int*)((char*)extraMemory - 4))[0]); +} + +ILibChain_Link* ILibChain_Link_Allocate(int structSize, int extraMemorySize) +{ + ILibChain_Link *retVal; + void* extraMemory; + ILibMemory_Allocate(structSize, extraMemorySize, (void**)&retVal, &extraMemory); + retVal->ExtraMemoryPtr = extraMemory; + return(retVal); +} +int ILibChain_Link_GetExtraMemorySize(ILibChain_Link* link) +{ + return(ILibMemory_GetExtraMemorySize(link->ExtraMemoryPtr)); +} + +//! Get the base Hashtable for this chain + +/*! + \b Note: Hashtable is created upon first use + \param chain Microstack Chain to fetch the hashtable from + \return Associated Hashtable +*/ +ILibHashtable ILibChain_GetBaseHashtable(void* chain) +{ + struct ILibBaseChain *b = (struct ILibBaseChain*)chain; + if(b->ChainStash == NULL) {b->ChainStash = ILibHashtable_Create();} + return(b->ChainStash); +} + +void ILibChain_OnDestroyEvent_Sink(void *object) +{ + ILibChain_Link *link = (ILibChain_Link*)object; + ILibChain_DestroyEvent e = (ILibChain_DestroyEvent)((void**)link->ExtraMemoryPtr)[0]; + + if (e != NULL) + { + e(link->ParentChain, ((void**)link->ExtraMemoryPtr)[1]); + } +} +//! Add an event handler to be dispatched when the Microstack Chain is shutdown +/*! + \param chain Microstack Chain to add an event handler to + \param sink Event Handler to dispatch on shutdown + \param user Custom user state data to dispatch +*/ +void ILibChain_OnDestroyEvent_AddHandler(void *chain, ILibChain_DestroyEvent sink, void *user) +{ + ILibChain_Link *link = ILibChain_Link_Allocate(sizeof(ILibChain_Link), 2 * sizeof(void*)); + link->ParentChain = chain; + link->DestroyHandler = (ILibChain_Destroy)&ILibChain_OnDestroyEvent_Sink; + ((void**)link->ExtraMemoryPtr)[0] = sink; + ((void**)link->ExtraMemoryPtr)[1] = user; + + if (ILibIsChainRunning(chain) == 0) + { + ILibAddToChain(chain, link); + } + else + { + ILibChain_SafeAdd(chain, link); + } +} +void ILibChain_OnStartEvent_Sink(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + ILibChain_Link* link = (ILibChain_Link*)object; + ILibChain_StartEvent e; + + UNREFERENCED_PARAMETER(readset); + UNREFERENCED_PARAMETER(writeset); + UNREFERENCED_PARAMETER(errorset); + UNREFERENCED_PARAMETER(blocktime); + + e = (ILibChain_StartEvent)((void**)link->ExtraMemoryPtr)[0]; + if (e != NULL) + { + e(link->ParentChain, ((void**)link->ExtraMemoryPtr)[1]); + } + + // Adding ourselves to be deleted, since we don't need to be called again. + // Don't need to lock anything, because we're on the Microstack Thread + ILibLinkedList_AddTail(((ILibBaseChain*)link->ParentChain)->LinksPendingDelete, object); +} +//! Add an event handler to the Microstack Chain to be dispatched on startup of the chain +/*! + \param chain Microstack Chain to add the event handler to + \param sink Event Handler to be dispatched on startup + \param user Custom user state data to dispatch +*/ +void ILibChain_OnStartEvent_AddHandler(void *chain, ILibChain_StartEvent sink, void *user) +{ + ILibChain_Link *link = ILibChain_Link_Allocate(sizeof(ILibChain_Link), 2 * sizeof(void*)); + link->ParentChain = chain; + ((void**)link->ExtraMemoryPtr)[0] = sink; + ((void**)link->ExtraMemoryPtr)[1] = user; + link->PreSelectHandler = ILibChain_OnStartEvent_Sink; + + if (ILibIsChainRunning(chain) == 0) + { + ILibAddToChain(chain, link); + } + else + { + ILibChain_SafeAdd(chain, link); + } +} + +#if defined(_REMOTELOGGING) && defined(_REMOTELOGGINGSERVER) +unsigned char ILibDefaultLogger_HTML[4783] = +{ + 0x1F,0x8B,0x08,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xED,0x3C,0xEB,0x72,0xDB,0x3A,0x73,0xFF,0x3B,0xD3,0x77,0x40,0x98,0x39,0xC7,0x52,0xA3,0xFB,0xCD,0xB6,0x64,0x39, + 0x63,0x4B,0x4A,0xE2,0xF9,0x1C,0x27,0xB1,0x9C,0xE4,0x9C,0x39,0x73,0x9A,0xA1,0x44,0x48,0xE2,0x17,0x8A,0x54,0x49,0xCA,0xB6,0x8E,0xEB,0x27,0xEB,0x8F,0x3E,0x52,0x5F, + 0xA1,0xBB,0x00,0x48,0x82,0x24,0x48,0x5A,0x4E,0x7A,0x99,0x69,0xE5,0x4C,0x4C,0x82,0x8B,0xBD,0x61,0x77,0xB1,0xBB,0x84,0xFC,0x1F,0xFF,0xF6,0xEF,0x27,0x2F,0xC6,0x1F, + 0x46,0x37,0xBF,0x7F,0x9C,0x90,0x95,0xBF,0xB6,0xC8,0xC7,0xCF,0xE7,0x97,0x17,0x23,0xA2,0x55,0xEB,0xF5,0xAF,0xED,0x51,0xBD,0x3E,0xBE,0x19,0x93,0xDF,0xDE,0xDD,0xBC, + 0xBF,0x24,0xCD,0x5A,0x83,0xDC,0xB8,0xBA,0xED,0x99,0xBE,0xE9,0xD8,0xBA,0x55,0xAF,0x4F,0xAE,0x34,0xA2,0xAD,0x7C,0x7F,0xD3,0xAF,0xD7,0xEF,0xEE,0xEE,0x6A,0x77,0xED, + 0x9A,0xE3,0x2E,0xEB,0x37,0xD7,0xF5,0x7B,0xC4,0xD5,0xC4,0xC9,0xE2,0xB2,0xEA,0x4B,0x33,0x6B,0x86,0x6F,0x68,0xA7,0xE4,0x04,0x9F,0xE0,0x2F,0xAA,0x1B,0xF0,0x6B,0x4D, + 0x7D,0x9D,0xCC,0x1D,0xDB,0xA7,0xB6,0x3F,0xD4,0x7C,0x7A,0xEF,0xD7,0x11,0x60,0x40,0xE6,0x2B,0xDD,0xF5,0xA8,0x3F,0xDC,0xFA,0x8B,0xEA,0x91,0x46,0x90,0x60,0x95,0xFE, + 0xCB,0xD6,0xBC,0x1D,0x6A,0x23,0x0E,0x5E,0xBD,0xD9,0x6D,0xA8,0x16,0xE0,0x90,0x01,0x7E,0xAB,0x7E,0x3E,0xAB,0x8E,0x9C,0xF5,0x46,0xF7,0xCD,0x99,0x45,0xB5,0x88,0xC0, + 0xC5,0x64,0x38,0x19,0xBF,0x9D,0x84,0xB3,0x6C,0x7D,0x4D,0x87,0xDA,0xC2,0x71,0xD7,0xBA,0x5F,0x35,0xA8,0x4F,0xE7,0xC8,0xAC,0x26,0x73,0x64,0xD1,0xCD,0xCA,0xB1,0xE9, + 0xD0,0x76,0x70,0x96,0x6F,0xFA,0x16,0x3D,0xFD,0x4A,0x67,0xD7,0x37,0x23,0x32,0xA6,0xB3,0xED,0xF2,0xA4,0xCE,0xC7,0xC8,0x89,0xE7,0xEF,0xF0,0xF7,0xCC,0x31,0x76,0x0F, + 0x6B,0xDD,0x5D,0x9A,0x76,0xBF,0x31,0xD8,0xE8,0x86,0x61,0xDA,0x4B,0xB8,0x9A,0x39,0xAE,0x41,0x5D,0xB8,0x98,0x3B,0x96,0xE3,0xF6,0x67,0x96,0x3E,0xFF,0x3E,0x58,0x00, + 0xA1,0xAA,0x67,0xFE,0x45,0xFB,0xCD,0xF6,0xE6,0x9E,0xDF,0x2E,0xF4,0xB5,0x69,0xED,0xFA,0xDA,0x8D,0x0B,0xF8,0xE7,0x2B,0xEA,0x93,0xF7,0x53,0xAD,0x42,0xCE,0x5C,0x53, + 0xB7,0x2A,0xE4,0x1D,0xB5,0x6E,0xA9,0x6F,0xCE,0xF5,0x0A,0xF1,0x40,0xBB,0x55,0x8F,0xBA,0xE6,0x62,0x30,0x03,0x64,0x4B,0xD7,0xD9,0xDA,0x46,0x95,0xA3,0x7F,0x69,0xB4, + 0x8D,0x63,0xA3,0x37,0x78,0x7C,0x89,0xB2,0xE8,0xA6,0x4D,0xDD,0x87,0x34,0xD0,0x62,0xB1,0x18,0xDC,0x99,0x86,0xBF,0xEA,0x37,0x1B,0x8D,0x06,0x30,0x10,0xF0,0x4D,0xF4, + 0xAD,0xEF,0x08,0x96,0xAB,0xBE,0xB3,0x09,0xF9,0xAF,0xBA,0xE6,0x72,0xE5,0xF7,0x9B,0x9B,0x7B,0xE2,0x39,0x96,0x69,0x90,0x97,0xB3,0x43,0xFC,0x09,0x1E,0xCF,0x1C,0xDF, + 0x77,0xD6,0x11,0xB8,0x45,0x17,0x2A,0xE8,0x48,0x2D,0x8F,0x2F,0xD7,0xBA,0xE7,0xA3,0x39,0x3C,0x70,0x4E,0x18,0x65,0x85,0xFE,0x9C,0x5B,0xEA,0x2E,0x2C,0xE7,0x8E,0x03, + 0xA0,0xA9,0x54,0x75,0xCB,0x5C,0xDA,0x7D,0xC6,0x91,0x42,0x03,0x8D,0x76,0x4F,0x08,0x77,0xDC,0x43,0xD9,0x22,0x88,0xBE,0xBB,0x9C,0x95,0x3A,0xDD,0xCA,0x51,0xAF,0xD2, + 0x6C,0x1F,0x96,0x07,0x44,0x7A,0x54,0x5D,0x3B,0x7F,0x55,0x2D,0xD0,0x97,0xEE,0x56,0x97,0xAE,0x6E,0x98,0x60,0x07,0x25,0x94,0xA2,0x42,0x60,0x96,0x1E,0x4D,0xAB,0x34, + 0xCB,0xA4,0xF1,0x8B,0x18,0x6D,0x54,0xBA,0xCD,0x4A,0xB3,0xD1,0xC2,0xC1,0xD6,0xF1,0x2F,0x09,0x94,0x77,0x74,0xF6,0xDD,0xF4,0x25,0x74,0x0C,0x7D,0x85,0x20,0x5A,0x02, + 0xDA,0x05,0x24,0x28,0x03,0xBF,0x64,0xDC,0x57,0x3D,0xB8,0x2E,0x01,0xFA,0x14,0xCD,0x72,0x0C,0x02,0x68,0x55,0x92,0x0C,0x94,0xD5,0xD4,0xF7,0x90,0xE9,0x09,0x22,0x39, + 0x3F,0x19,0xDF,0xDA,0xFB,0xB9,0x08,0x93,0xC8,0x7C,0x87,0xEB,0x78,0x3F,0x84,0x0B,0xD3,0xF2,0xC1,0x67,0x37,0xAE,0xB3,0x34,0x8D,0xFE,0xF8,0xB7,0x8B,0xB5,0xBE,0xA4, + 0x2C,0x1E,0x62,0xCC,0xA8,0xBD,0x37,0xE7,0xAE,0xE3,0x39,0x0B,0xBF,0x16,0xD2,0x21,0x9E,0xAF,0xBB,0xFE,0x08,0x57,0xC8,0xF3,0xDD,0xE1,0xC1,0xCB,0x96,0xD1,0xED,0x1D, + 0x1D,0x1F,0x54,0x08,0xB5,0x0D,0x69,0xB8,0xD1,0x68,0xB7,0x7B,0xBD,0x83,0xCA,0x5B,0x31,0x11,0x83,0xD9,0xB0,0x49,0x80,0x26,0x7A,0xAC,0xB5,0x5D,0xDB,0xDF,0xAC,0x87, + 0x8D,0xC3,0xA3,0x67,0xDF,0xA5,0x16,0xC4,0xB2,0x5B,0x3A,0x00,0x07,0xD0,0xFD,0x3E,0x6A,0x26,0x30,0xED,0xB6,0xEC,0xB6,0x91,0xBB,0x90,0x66,0x37,0x66,0xF2,0xB2,0xC7, + 0x3F,0xBE,0x5C,0x38,0x0E,0xC8,0xF5,0x30,0xB7,0x40,0x47,0x7D,0x70,0xD9,0x55,0xC2,0xBB,0x64,0xB7,0x91,0x3C,0x6D,0x0E,0x8C,0x52,0x57,0x81,0xB5,0xD9,0x6C,0x1F,0xF7, + 0x5A,0x01,0x75,0x16,0x30,0x90,0x7C,0x70,0x2F,0x82,0x02,0x0E,0x05,0xB4,0x89,0xFE,0x20,0x71,0xC4,0x68,0x18,0x74,0xEE,0xB8,0x3A,0x93,0x17,0x50,0x53,0x17,0x97,0x50, + 0x82,0xEF,0xAF,0x90,0xC7,0xBC,0x59,0xB6,0x83,0x13,0x42,0xC4,0xE8,0xFF,0x39,0x88,0x6B,0x2C,0x5E,0xB7,0x1F,0xD2,0xF2,0xF1,0xF9,0x77,0x2B,0xD3,0xA7,0x0A,0x59,0x8F, + 0x1A,0xF8,0xC3,0x43,0xF5,0x1D,0x65,0xD1,0x70,0xE6,0x58,0x46,0x80,0xB0,0xF7,0x20,0x6B,0xA1,0x95,0xD6,0x42,0x4B,0xB9,0x2E,0xA3,0x06,0xFE,0x00,0x12,0x30,0x39,0x7A, + 0xE3,0x38,0xD6,0x4C,0x77,0x65,0xD6,0xD8,0x92,0xA7,0xA7,0x4D,0x3A,0x93,0xE3,0xC9,0xA1,0x14,0xA8,0xAB,0x8C,0x89,0x3E,0x8B,0xB6,0xF2,0x30,0x5F,0x51,0xA4,0xFD,0x58, + 0xBB,0xF7,0xF8,0x3E,0xA7,0xD8,0x10,0x26,0x0D,0xFC,0x09,0xCD,0x08,0x56,0x8C,0xB4,0x1A,0xF8,0x5F,0x70,0x15,0x6E,0x03,0x60,0xB6,0x5B,0x0F,0xB6,0x0D,0x86,0x71,0x61, + 0xDE,0x53,0x03,0x35,0xF2,0x20,0xEF,0x60,0x73,0x67,0xEB,0x9A,0x14,0xE2,0x9C,0x26,0xAE,0x88,0x4D,0xEF,0x60,0x27,0x5B,0x3B,0xB6,0xE3,0x6D,0xF4,0x39,0x2E,0x82,0x69, + 0x2F,0x1C,0xD0,0xBA,0xBB,0x8B,0xA9,0xAD,0xC9,0xB0,0x5E,0x9E,0x9D,0x4F,0x2E,0x6F,0x7E,0xBB,0x79,0x20,0xF2,0x96,0xF9,0x58,0x9B,0xDE,0x7C,0xBE,0xFA,0x76,0x31,0x9A, + 0x7C,0x7B,0xFF,0x61,0xFC,0xF9,0x72,0x12,0x3D,0xDE,0x22,0xC6,0xF1,0xCD,0xE5,0x34,0xF1,0xC4,0xD0,0x5D,0x90,0x93,0x52,0x1B,0x27,0x8F,0x6E,0x3E,0x2A,0x1E,0xDF,0x9A, + 0x8E,0x05,0x3B,0xED,0x63,0xED,0x6C,0xFA,0xFB,0xD5,0x68,0xFA,0x61,0xF4,0xB7,0xC9,0x4D,0x02,0x0C,0x5C,0x7D,0x37,0x73,0x9D,0x3B,0x1B,0xA0,0xBE,0x4E,0xCE,0x93,0xD4, + 0xC5,0x93,0xB7,0x93,0xAB,0xC9,0xF5,0xC5,0x28,0x49,0x82,0xD2,0xCD,0xC6,0xB4,0xBF,0x03,0xC0,0xE7,0xAB,0xBF,0x5D,0x7D,0xF8,0x7A,0x95,0x44,0x4E,0xD1,0x82,0x2E,0x3F, + 0xBC,0xFD,0x76,0x39,0xF9,0x32,0xB9,0xFC,0xD6,0x7C,0x20,0xF2,0x6D,0x2B,0x7E,0xDB,0x8E,0xDF,0x76,0xE2,0xB7,0xDD,0xF8,0x6D,0x2F,0xB8,0xFD,0x72,0x31,0xBD,0x38,0x47, + 0x92,0x86,0xE9,0x6D,0x2C,0x7D,0xD7,0x37,0x6D,0xE1,0x0A,0xF8,0xF4,0xDD,0xC5,0x78,0x3C,0xB9,0x8A,0x1E,0x72,0x6F,0xAA,0xB1,0x54,0xE8,0xDE,0x7F,0x4F,0xED,0xAD,0x64, + 0x2F,0xFD,0x97,0x6F,0x8E,0xF1,0x07,0xCC,0xE1,0xBE,0xEA,0xAD,0x74,0x03,0x22,0x47,0x83,0x40,0xD4,0x01,0x0B,0xE3,0x11,0x96,0x34,0x2A,0xE2,0x5F,0xAD,0x0D,0x61,0x4D, + 0x64,0x3F,0x52,0x26,0x30,0x9F,0xCF,0x07,0x71,0x62,0x61,0xB8,0xD3,0x67,0x00,0xB3,0x05,0xEF,0xE3,0x99,0x07,0xCB,0x21,0xE0,0x97,0xE9,0xF9,0xC2,0xBC,0x19,0x78,0x2A, + 0xE8,0x61,0x7C,0x59,0x9B,0xB6,0xB0,0xF5,0x2E,0x0F,0x8C,0xF7,0x81,0xE9,0xB3,0xFB,0xBF,0xAA,0x26,0x44,0x80,0x7B,0x78,0xD8,0x90,0x73,0xAF,0x23,0x60,0x0B,0x44,0x5D, + 0xA3,0xA4,0x41,0xF8,0xE8,0x74,0x3A,0x83,0xB8,0xA2,0xAA,0x33,0xCB,0x01,0x03,0x0C,0x0C,0x95,0xB1,0x75,0x24,0x39,0x38,0x4F,0x8D,0xE4,0x91,0x9C,0x40,0xA8,0x0C,0x5E, + 0x22,0xEE,0x76,0x7F,0x19,0xCC,0xB7,0xAE,0xC7,0xEC,0x66,0xA1,0x6F,0x2D,0x3F,0x0A,0xCF,0x2B,0xD3,0x30,0xC0,0x8E,0xD3,0x3B,0x43,0xC0,0x7E,0x22,0x4C,0x1E,0xE3,0x8F, + 0x9C,0xFA,0x30,0xB9,0x1E,0x6B,0x6B,0x58,0xCF,0x44,0x88,0xF9,0xA1,0xE8,0x27,0x94,0xCC,0x92,0xC8,0x15,0x1F,0x6F,0xF7,0xE0,0x5A,0xC8,0xB1,0x71,0x4C,0x86,0x3C,0xAE, + 0x73,0x79,0x3B,0x93,0x55,0xC6,0x97,0x43,0xB0,0x28,0x04,0x52,0xE4,0x77,0xE3,0xA3,0xEE,0xF8,0x4D,0x24,0x4B,0x6B,0x6F,0x61,0x1A,0xB3,0x2E,0x3D,0x9E,0xFF,0x77,0x0A, + 0xD3,0x2A,0x94,0xE6,0xA4,0x2E,0xEA,0x88,0x93,0xBA,0xA8,0x91,0xB0,0xA0,0x20,0x8E,0x0D,0xD8,0x8D,0xA1,0x66,0x2E,0x48,0xC9,0x87,0x5C,0xC1,0x59,0x94,0x58,0xA6,0xB1, + 0xDD,0x94,0xC9,0x8B,0xE1,0x90,0x1C,0xE0,0xCE,0xB6,0x00,0x23,0x35,0x0E,0xCA,0x44,0x3C,0x29,0x95,0x07,0x58,0xB3,0x18,0xE6,0x2D,0x31,0x61,0x6A,0x58,0x08,0xC8,0x83, + 0x41,0xF2,0xAD,0x11,0x46,0x76,0xA8,0x09,0x79,0x7B,0x28,0x6F,0x58,0x1A,0xFC,0x92,0x34,0xC0,0x00,0x85,0x98,0x24,0x89,0x2E,0xCF,0x17,0xA2,0xCD,0x8F,0xF0,0x27,0xEE, + 0x37,0x6C,0x2F,0x49,0xA8,0x49,0x3B,0x85,0x1A,0xCA,0x75,0xEC,0xE5,0xE9,0x09,0xAA,0x36,0x44,0x1E,0xAA,0xB9,0xD3,0x4B,0xD4,0x48,0xB9,0x35,0x91,0x76,0xCA,0xF3,0x33, + 0x1F,0x74,0x4D,0xAE,0xA9,0x6E,0xF9,0xE6,0x9A,0x92,0x4B,0x67,0x09,0x51,0x03,0xCA,0x36,0xC4,0x73,0x8A,0xDA,0xE6,0x14,0xEB,0x20,0xCE,0x8F,0x0A,0xD5,0x4D,0xC8,0xD4, + 0xEC,0x3C,0x41,0x28,0x04,0xDA,0x47,0xA8,0x13,0xD8,0x3A,0x6D,0xB6,0x76,0xAC,0xF0,0x5C,0x81,0x80,0x1A,0xCA,0x01,0xA3,0xA7,0x59,0x52,0x49,0xC2,0xB1,0x89,0xCE,0x06, + 0x6C,0x91,0xD5,0xB3,0x3A,0x14,0xC8,0x01,0x4F,0xD2,0x72,0x0B,0x81,0x5B,0x98,0x30,0x40,0x39,0x4C,0x2D,0x4B,0xC8,0x35,0xD4,0x1A,0xFC,0x1E,0x37,0xF0,0xF0,0xDE,0xD2, + 0x3D,0x0F,0x64,0x8A,0x52,0x17,0x86,0xDB,0xC5,0xFF,0x0C,0x46,0x71,0x0A,0x75,0xF4,0xDC,0xA7,0xC6,0x7B,0xC7,0xD8,0x5A,0xD4,0x0B,0xA7,0x08,0xB7,0xD0,0x4E,0x03,0x00, + 0x22,0x20,0xA0,0xAC,0x36,0xA2,0xE9,0x5F,0xA8,0x3B,0xC3,0x88,0xB7,0xE3,0x60,0xE9,0xE9,0x21,0x40,0x9F,0x34,0xE3,0x53,0x47,0x98,0xE3,0x66,0x4D,0x63,0x0F,0xD1,0x24, + 0x12,0xF4,0x3E,0xEA,0x5B,0x8F,0x66,0x4D,0x62,0x0F,0xE3,0xF0,0x6F,0x40,0x72,0x61,0x58,0x69,0x78,0x7C,0x28,0xC0,0xEB,0x4C,0x27,0x75,0xA6,0xF5,0xF4,0xB2,0x6C,0xA0, + 0xB8,0xF8,0x26,0x5A,0x0F,0x71,0xC7,0xE5,0xF5,0x80,0x34,0x76,0xE0,0xD2,0x85,0x4B,0xBD,0xD5,0xF9,0x16,0x36,0x14,0x7B,0x6C,0xDE,0x1E,0xC4,0xAD,0x96,0xD7,0xC4,0x7C, + 0x87,0xE4,0x89,0x28,0xFA,0x9B,0xBC,0xDB,0x82,0xCD,0x98,0xF6,0x66,0x0B,0x45,0x27,0x56,0x1E,0xDA,0x8C,0x21,0xD2,0xC8,0xAD,0x0E,0x59,0xD4,0x50,0xBB,0xE6,0xE8,0x35, + 0x88,0x3B,0x73,0xCB,0x9C,0x7F,0x1F,0x6A,0x82,0x20,0x8B,0x29,0x12,0xE3,0xA7,0x27,0xAB,0x66,0x64,0x8C,0xF8,0x68,0xD5,0x0C,0x9F,0x6F,0x44,0x88,0x31,0x6D,0x59,0x1C, + 0x70,0x48,0x7F,0xEB,0xC1,0x5D,0x32,0xE6,0x74,0x1A,0x69,0x26,0xF9,0x34,0x16,0xD3,0x41,0x11,0x2C,0xA8,0x87,0xD3,0x82,0x04,0x20,0xE9,0x75,0xAC,0x6C,0x89,0x1C,0x0C, + 0x45,0x47,0x44,0xA1,0xDF,0x70,0x06,0x70,0xB3,0x08,0x1D,0x87,0x24,0x3C,0x45,0xED,0x37,0xB0,0xC4,0x5F,0x21,0x7F,0x70,0xEE,0xB4,0x6C,0x97,0xE9,0x31,0x21,0x82,0x70, + 0x59,0xDD,0xF5,0x3D,0x88,0x41,0x16,0x5B,0xBB,0x04,0x36,0x5E,0xD8,0x44,0x5E,0x98,0xEB,0x65,0xCD,0x86,0x82,0x66,0xC2,0xCB,0x0E,0xEE,0x39,0xCA,0xD0,0x16,0x92,0xF5, + 0x43,0x62,0xFD,0x43,0x13,0x4E,0x83,0x73,0xFB,0xE1,0xA1,0xCE,0xC2,0x6B,0x28,0x70,0x77,0xDA,0xE9,0x85,0xCD,0x7B,0x65,0x90,0x79,0x10,0xDD,0x27,0x27,0x7A,0x30,0x35, + 0x09,0x49,0x56,0x60,0x2F,0x43,0xD6,0x27,0xF4,0xFA,0xF5,0x3A,0x66,0xF9,0xB0,0x05,0x7A,0x2B,0x5C,0x41,0x57,0xB7,0x20,0xAB,0x5C,0x6B,0xA7,0xAA,0xD1,0x93,0xBA,0x7E, + 0x9A,0xEB,0x2C,0x09,0x25,0x4A,0xE9,0x69,0xE8,0x79,0xF2,0x98,0x00,0x0D,0x9E,0xB0,0x14,0x49,0xB2,0x6A,0x1F,0x9C,0xD6,0xA2,0xAC,0x91,0xC7,0x03,0x4F,0xE9,0x60,0xEA, + 0x6F,0xED,0x8B,0x39,0x1D,0xAD,0xE8,0xFC,0x3B,0x64,0xB7,0x58,0xB9,0xDF,0x02,0x7F,0xE5,0xD0,0x61,0x58,0x3C,0x8B,0x03,0x69,0xC2,0x8B,0xE6,0xE1,0xBD,0xA1,0xFB,0x7A, + 0x75,0xBE,0x06,0xD0,0xC6,0x7D,0xA3,0xA5,0x89,0x96,0x61,0x9F,0x60,0xED,0x52,0x87,0xDA,0xE5,0x64,0xE6,0xC6,0xF6,0x9C,0x3D,0x18,0x1C,0xFB,0x96,0x97,0xCF,0x9D,0x0C, + 0x51,0xC0,0x5A,0x27,0x62,0x0D,0x0B,0xA7,0x1F,0x60,0x6B,0x3A,0xF7,0x37,0x05,0x4A,0x93,0x20,0x0A,0xD8,0x3A,0x92,0x34,0x06,0x05,0xDB,0x0F,0xB0,0x75,0xE6,0xED,0xEC, + 0xF9,0x14,0xF2,0x76,0xEA,0xE7,0x73,0xA7,0x00,0xCC,0x67,0xB2,0xD3,0x90,0x33,0x8C,0x3E,0x91,0x10,0xFC,0x00,0xBF,0x20,0xF6,0x94,0xBA,0x10,0x3E,0xF2,0xB9,0x4D,0x81, + 0xE5,0xF3,0x7A,0x94,0xE0,0x35,0x9C,0xFE,0x03,0x9C,0xBE,0xA5,0x90,0x4B,0x9A,0xF3,0x7C,0x3E,0x13,0x40,0xF9,0x5C,0x42,0x4C,0x8B,0xB3,0x29,0x66,0xCB,0x4C,0x26,0xFC, + 0xFF,0x36,0xD8,0xF8,0x33,0x23,0x40,0x10,0xA2,0x54,0xFB,0x4A,0x96,0xB4,0x10,0xE8,0x2F,0x41,0x18,0xAB,0xD4,0x8C,0x84,0x8A,0xA5,0x18,0x4F,0xD4,0x5A,0x88,0xA7,0xA5, + 0xC4,0xD3,0xDA,0x1B,0x4F,0x5B,0x89,0xA7,0xBD,0x37,0x9E,0x8E,0x12,0x4F,0x67,0x6F,0x3C,0x5D,0x25,0x9E,0x6E,0xCE,0x82,0x2D,0xA2,0x54,0xE9,0xA7,0x2E,0x19,0x37,0x50, + 0x29,0x11,0x2B,0x1D,0x48,0x37,0x13,0x1B,0x77,0x11,0x43,0x6D,0xA3,0x69,0xB8,0x94,0x99,0x9E,0x8A,0x07,0x4F,0x55,0x10,0xA4,0x4A,0xD4,0x47,0xBC,0x6F,0x2C,0x7D,0xE9, + 0x95,0x80,0xDE,0x35,0x8E,0x10,0x76,0xFB,0x74,0x24,0xBA,0x01,0x7C,0x21,0x1A,0x8E,0x41,0x37,0x30,0x57,0x25,0x2C,0xA1,0x7C,0x22,0x0E,0xD6,0xE2,0x95,0x90,0x84,0x19, + 0x6F,0x0A,0x4B,0xF0,0x0B,0xCB,0x4D,0x56,0x7D,0xE2,0x8B,0xBA,0x13,0xC8,0x5E,0xCC,0x4D,0x90,0x24,0xB2,0xF7,0x73,0x7F,0xD7,0x6F,0x75,0x3E,0xAA,0x9D,0xFE,0xE3,0x3F, + 0x10,0xF1,0xB9,0x05,0xAC,0x1F,0xCF,0x3E,0x4F,0x27,0x63,0x32,0x24,0x0B,0xDD,0xF2,0xE8,0x20,0xFE,0xF0,0xCE,0x63,0x91,0x31,0x31,0x0A,0xAB,0x6E,0xF3,0xBC,0x5F,0x3D, + 0x6B,0xF4,0x1E,0xF1,0x3D,0x10,0xED,0xEA,0xC3,0xD5,0x44,0xEB,0x13,0xD8,0x1A,0x1A,0x15,0xA2,0x5D,0x7E,0x78,0xFB,0x76,0x72,0xCD,0xEF,0xC1,0x45,0xB5,0xA0,0x23,0xC8, + 0x47,0xC0,0xD9,0x34,0xDC,0xCC,0xF8,0x5D,0x07,0x9F,0xC3,0x1E,0xC2,0xEF,0x8E,0xE0,0x4E,0x6A,0xF1,0xB1,0xC1,0x0E,0xA2,0xFC,0x3A,0x39,0x9F,0x4E,0xAE,0xBF,0x08,0xAC, + 0x47,0x38,0x24,0x5A,0x79,0x6C,0x00,0x42,0x13,0x79,0x4C,0x30,0xC7,0xB9,0xF8,0xF6,0xE6,0xF2,0xEC,0xED,0x94,0x73,0x39,0xB9,0x3A,0x3B,0xBF,0x9C,0x04,0x13,0x00,0xC5, + 0xF5,0x64,0x3A,0xB9,0xF9,0xF6,0xE6,0x42,0x0C,0xB6,0xC4,0xE0,0xD9,0x58,0x10,0x96,0x60,0x10,0x8B,0xA0,0x0D,0xA4,0xE2,0x94,0x42,0xDF,0x02,0x32,0x4D,0xE0,0x22,0x7A, + 0xBA,0xD8,0xDA,0xAC,0x97,0x4B,0x3E,0x95,0xEE,0xCB,0xC0,0x82,0x4B,0xFD,0xAD,0x6B,0x13,0xC3,0x99,0x6F,0xA1,0x00,0xF1,0x6B,0x4B,0xEA,0x4F,0x2C,0x8A,0x97,0xE7,0xBB, + 0x0B,0x03,0x60,0x06,0xE4,0x91,0xD4,0xEB,0x44,0xFB,0xA4,0xA9,0x90,0x4C,0x63,0x58,0x10,0x27,0xEF,0x6A,0xE3,0xAC,0xC4,0x87,0x23,0xE1,0xAE,0xAA,0x42,0x35,0x29,0xDD, + 0x57,0xC8,0x0E,0xB1,0x31,0x34,0xE0,0xCB,0xCC,0x83,0x40,0x82,0x17,0xBB,0x04,0x3A,0x81,0x8A,0x32,0x1F,0x53,0xE1,0xFA,0x22,0x70,0x15,0x7F,0x04,0xAA,0x5B,0xD3,0x33, + 0x63,0xB8,0x1E,0xA2,0x4B,0xFC,0x60,0x1B,0x05,0x94,0x39,0x24,0x61,0xDB,0xA4,0x9C,0x04,0xC1,0x8F,0xD0,0x43,0x89,0xE9,0xA5,0x26,0xC2,0x11,0x4E,0x3B,0xC0,0x90,0x74, + 0x40,0x5E,0x73,0xA3,0x25,0x7D,0xE2,0xBB,0x5B,0x5A,0x1E,0xC4,0x31,0x3C,0xC6,0x6F,0x29,0x40,0xC6,0x47,0x14,0x14,0x13,0x94,0x90,0xCB,0xD7,0xE4,0xE0,0x00,0x28,0x70, + 0x92,0x39,0x34,0x1E,0x89,0x4A,0x75,0x67,0xF1,0x65,0x30,0xC1,0xDF,0x5C,0xF6,0x6A,0xFF,0xD5,0x90,0x24,0xD6,0x41,0xE8,0x4E,0xDF,0x6C,0xA8,0x6D,0xA8,0x70,0xBD,0xCB, + 0xC2,0x95,0x42,0x15,0xE0,0xC2,0x20,0xA2,0xC2,0x34,0x2A,0xB2,0x56,0xEF,0x7C,0x37,0xC2,0x90,0x76,0xA5,0xAF,0x29,0xB7,0x5A,0x25,0x1A,0x61,0x18,0x99,0xEB,0x8C,0xCE, + 0x43,0x05,0x46,0xE0,0x92,0xD1,0x4D,0x68,0x10,0x2A,0x1A,0x52,0x42,0x38,0x13,0x00,0x1A,0x64,0x00,0xBF,0x4F,0xC2,0x39,0x35,0x8B,0xDA,0x4B,0x7F,0x05,0x83,0xAF,0x5E, + 0x95,0x0B,0x17,0x2F,0x98,0xF5,0x87,0xF9,0x27,0x77,0x1B,0x69,0x21,0x77,0x39,0x0B,0xA7,0x74,0xE8,0x0B,0xDB,0x6F,0xB7,0x6E,0x9C,0xA9,0xEF,0x96,0x6E,0x25,0x5D,0xC1, + 0x3D,0xEC,0x53,0xB5,0x85,0xEB,0xAC,0x47,0x2B,0xDD,0x1D,0x39,0x06,0x2D,0x95,0x6E,0xC9,0xE9,0x29,0x69,0x75,0xCA,0xE4,0x57,0x88,0x1F,0x6F,0xDE,0x54,0x08,0x1F,0x69, + 0xF6,0x92,0x23,0x47,0xD1,0xC0,0xAD,0xB8,0xCA,0xD0,0x2D,0x90,0x6F,0xF6,0xF6,0x21,0xBF,0x17,0xEA,0xA3,0xA7,0x61,0x2E,0x40,0x84,0x3B,0xE2,0xF9,0xCE,0xA7,0x25,0x4C, + 0x22,0x2B,0x64,0xE3,0xBB,0x12,0x3E,0x36,0x58,0x9B,0x0B,0x54,0x67,0x7E,0x09,0x1F,0xE7,0x20,0x9A,0xAE,0x1C,0xD7,0x7F,0x3A,0x26,0x72,0x72,0x82,0x22,0xBF,0x22,0x8A, + 0x87,0x30,0xDA,0xCC,0xA1,0x04,0xF2,0xEF,0x41,0xE7,0x9F,0x60,0x19,0x0F,0x0F,0x0F,0x5B,0xB8,0x98,0xAF,0x94,0x30,0x8C,0x1C,0xC0,0xF5,0xBA,0xDD,0x76,0x1E,0x50,0x0B, + 0x81,0x5A,0xDD,0x5E,0x36,0xD7,0xED,0x1C,0xAE,0x2F,0x1D,0xC8,0xA9,0x94,0x6C,0x67,0xF1,0x7D,0xD8,0x6A,0x74,0x0F,0xBB,0xC7,0x9D,0x46,0xFB,0xF0,0xB8,0x75,0x78,0x9C, + 0xCB,0x1B,0x13,0xA0,0x75,0xD4,0xEC,0x1C,0x76,0x8E,0x0F,0x7B,0x87,0xCD,0x46,0xAF,0x5B,0x28,0x4A,0xB3,0x71,0x7C,0xDC,0x6D,0x36,0x7B,0x2D,0x50,0x4F,0x1E,0x70,0x1B, + 0x81,0x3B,0xAD,0xE3,0xCE,0x71,0xEF,0xB0,0x75,0x9C,0x23,0x7E,0xE7,0x89,0xDA,0xEE,0x3E,0x45,0xDB,0xBD,0x42,0x6D,0x1F,0x72,0x6D,0x2B,0xF4,0x3D,0x65,0x87,0x05,0xD6, + 0x6B,0xDD,0x36,0x4A,0x50,0x1B,0x55,0x18,0x82,0xFC,0x28,0xE7,0x01,0xCA,0xA1,0xEC,0xB5,0x30,0x2F,0x20,0x3D,0x48,0x83,0xCF,0x4C,0x1B,0xC0,0x6D,0x7A,0xC7,0xBC,0xF1, + 0xCC,0x75,0xF5,0x5D,0x09,0x50,0x88,0x78,0x97,0x8C,0x91,0x79,0x11,0x93,0x07,0xCC,0x68,0x2E,0x8F,0x95,0x60,0x1E,0x40,0x02,0x02,0x21,0x80,0xE0,0x33,0x49,0x78,0x33, + 0xC8,0x41,0x46,0x8E,0x0D,0xE5,0x1B,0xB6,0xC4,0xD1,0xF3,0x89,0xEF,0xE0,0x14,0xDD,0xDD,0x65,0x12,0x16,0x39,0x64,0xCD,0x03,0xF5,0x94,0x00,0x56,0x66,0x53,0xAD,0xC8, + 0x64,0x1E,0x9E,0xAB,0xC2,0x35,0x6F,0x3D,0xA3,0x50,0x90,0x66,0xC6,0xD2,0x2C,0xFC,0x60,0xCE,0xF0,0xA9,0x94,0x6A,0xFD,0x94,0x6B,0xAC,0x4A,0x60,0x19,0x44,0x88,0xE2, + 0x5F,0x87,0x98,0xBA,0x86,0x6F,0xAA,0x07,0xC9,0x5C,0x40,0xE0,0x8A,0x35,0x6A,0xF2,0x10,0x61,0x3A,0x3B,0x88,0xED,0xF0,0x32,0x47,0x72,0x5F,0x25,0x97,0x1D,0x48,0x83, + 0x33,0xB1,0xA8,0xFA,0x1F,0x79,0xC8,0xA4,0x2C,0x3A,0x4B,0xBC,0x74,0x93,0x22,0x0F,0x61,0x98,0x81,0x67,0xB2,0x98,0x6C,0x26,0xE4,0x61,0x13,0xC9,0x3B,0xC7,0x15,0xC7, + 0x26,0x7B,0x17,0x82,0xF2,0x3C,0xBE,0x22,0x3B,0x8F,0x9C,0xDA,0xD7,0xA4,0x04,0x1D,0x3D,0x4A,0x02,0x13,0x34,0x13,0xA3,0x58,0x94,0xE0,0xF6,0x10,0x66,0xED,0xB8,0xED, + 0x28,0x4C,0x5A,0x15,0x69,0x47,0xA2,0x5E,0xCB,0xB3,0x55,0xC8,0xC3,0xA4,0x56,0x34,0x54,0x10,0x5A,0xD2,0x5F,0xCF,0x0C,0x2C,0x1C,0x4B,0xDA,0x1F,0xA3,0xCB,0xC9,0xD9, + 0xF5,0x64,0xFC,0xA7,0x56,0xEC,0x2A,0xEF,0x4C,0x83,0x7E,0xB6,0x79,0x19,0x6D,0xE4,0x50,0x2F,0x72,0x03,0x4C,0x8F,0x59,0x56,0xCC,0xF2,0x44,0xC8,0xD0,0xB4,0xC4,0x79, + 0x0D,0xE4,0x98,0x55,0xF4,0xE5,0xC1,0x3E,0x5E,0x91,0xC6,0x2B,0x9D,0xF4,0x90,0x70,0x66,0xD8,0xA2,0xDA,0x49,0x14,0xCC,0x46,0xE7,0x43,0x8A,0x91,0xE6,0xFA,0x4C,0x1A, + 0x77,0xFA,0x6C,0x49,0x31,0x89,0x1C,0x1F,0x4A,0x13,0x88,0x8E,0xA5,0x14,0x23,0xCE,0x74,0xA6,0x34,0xDA,0xF8,0x99,0x96,0x0C,0xD4,0x6A,0xAB,0xBA,0x61,0xF6,0xC4,0x5E, + 0x99,0x5D,0x53,0x0F,0xB2,0xFC,0x1C,0xCB,0x0A,0xFB,0x07,0x2F,0xF8,0xD5,0x20,0x6D,0xF6,0xF2,0x8B,0x39,0xC8,0x69,0xC5,0x8C,0xD7,0x50,0x44,0x33,0xE4,0x1A,0xD4,0x49, + 0x1C,0x46,0x2B,0x27,0x7D,0xE2,0x53,0x7C,0x36,0x88,0x1B,0xD4,0x17,0x40,0x31,0x42,0x14,0xBC,0xA8,0x67,0xA8,0x82,0x77,0x78,0x83,0x7C,0xA7,0x4D,0xF7,0x9E,0x4C,0x23, + 0xE8,0x34,0x65,0x7B,0x31,0x28,0x36,0xD9,0x0C,0xAB,0x08,0xC5,0xA7,0x58,0x37,0x0D,0x69,0x79,0xC8,0x8B,0x52,0x6C,0x24,0x09,0xBE,0x6F,0x7C,0xE3,0x5D,0x0B,0x11,0xC4, + 0x44,0x66,0x1E,0x27,0xF9,0x9A,0x35,0x5A,0x08,0xEF,0xBF,0x48,0xD1,0xEC,0x29,0x6A,0x91,0x7B,0xC6,0x4A,0xB5,0x90,0x34,0x36,0xFC,0x44,0x3B,0x32,0x16,0x6E,0x8C,0x1D, + 0xCC,0x65,0x60,0x3F,0xAF,0x41,0x6A,0xB3,0xBF,0x86,0xD2,0xD8,0x17,0x98,0x13,0x84,0xC8,0x23,0x59,0x15,0x01,0x9C,0x8B,0xDE,0x4C,0xA1,0x91,0x55,0xCD,0x99,0x8D,0xA9, + 0x99,0x51,0x28,0xA7,0xA9,0xE3,0xDA,0xCB,0x0D,0xCF,0x8C,0x75,0x0F,0xDB,0xAC,0x21,0x23,0x81,0xFA,0x94,0xC9,0x09,0x7B,0x54,0xDB,0xB8,0xEC,0xF7,0x98,0x9F,0x1C,0x2A, + 0x13,0xD5,0x68,0x29,0x49,0x29,0x9A,0x8E,0xC7,0x8A,0x3F,0xBA,0xCE,0x46,0x5F,0xB2,0xD7,0x7C,0xC1,0xFC,0xC4,0x70,0xA9,0x5C,0xE0,0x12,0x21,0xEB,0xD6,0xAD,0x55,0xE8, + 0x0A,0x72,0x7B,0x0B,0xE0,0x15,0x7E,0x9F,0x3C,0x00,0x00,0x21,0x48,0xEA,0x37,0x6B,0x60,0xBA,0x30,0x2F,0xE5,0x36,0xA0,0xE5,0xF8,0xFB,0x01,0x49,0xCF,0x69,0x73,0xE0, + 0xD9,0xAC,0x22,0xD5,0x35,0x59,0xD7,0x0D,0xD3,0xDC,0x21,0xE9,0x0D,0xC8,0xAB,0x57,0x66,0x71,0x2B,0x80,0xC5,0xCD,0xE8,0x0C,0x1E,0x72,0x68,0x56,0x38,0x0A,0xE0,0x14, + 0x03,0x0D,0x3F,0x53,0xC6,0xC2,0x8C,0x88,0xA6,0xB9,0xDD,0xA3,0xC4,0xF6,0x5C,0xE0,0xF2,0xD8,0x2E,0xAD,0x14,0xE5,0x24,0xFF,0xDB,0x6D,0x48,0x64,0x31,0x73,0x28,0x1B, + 0x2A,0x64,0xED,0x2D,0x2B,0xDC,0x63,0xF3,0x93,0x78,0x83,0x85,0x75,0x58,0x31,0x4D,0x53,0xF0,0x07,0x58,0x8A,0xBB,0x7E,0x9F,0xCE,0x12,0xF9,0x95,0xD4,0x61,0x3F,0x08, + 0x0E,0xA5,0x1E,0x90,0x53,0x66,0x77,0xC8,0x1D,0xFC,0xD2,0x4E,0xEA,0x61,0x2F,0x3D,0xB5,0x94,0xF8,0xE1,0xC5,0x72,0x7A,0x8D,0xD3,0x3C,0xF2,0xBD,0x28,0x2A,0xB0,0x45, + 0xB5,0x34,0x06,0xCD,0x11,0x0B,0x32,0x43,0x72,0xB7,0x82,0x6D,0x83,0x6C,0x70,0x33,0x33,0x54,0x86,0x6C,0xA1,0xE3,0x8D,0x02,0x35,0x10,0xE9,0x50,0xA9,0xA6,0x36,0x7C, + 0xDB,0xF4,0x4D,0xDD,0xFA,0x82,0x3D,0x53,0xD3,0xE2,0x6E,0xC8,0x43,0x17,0x9A,0xAB,0x2A,0x1A,0xBE,0x16,0x68,0xD9,0x89,0x52,0x6C,0x6E,0xA3,0x15,0x93,0xE8,0x14,0x69, + 0x4A,0x03,0xDE,0x9D,0xE9,0xCF,0x57,0x02,0x6B,0xB1,0xF7,0xCC,0x21,0xCC,0xB3,0x4E,0x7E,0x3F,0xFD,0x0C,0x3F,0xF9,0x12,0xAA,0x66,0xCC,0x5C,0xAA,0x7F,0x57,0x3C,0x0B, + 0x28,0x75,0xF6,0xA5,0xD4,0x7A,0x2E,0xA5,0xA3,0x7D,0x29,0xB5,0x9F,0x49,0xA9,0xD9,0xD8,0x97,0x52,0xE7,0x99,0x94,0x5A,0x7B,0x53,0xEA,0xEE,0x4B,0x49,0x1C,0x84,0xFD, + 0x2F,0xB5,0x87,0x94,0x37,0x06,0x56,0xCB,0x7C,0xFC,0x9F,0xF9,0xAB,0x99,0xC6,0x13,0xAD,0x57,0x2E,0xFF,0x33,0xB8,0x8E,0x42,0x55,0xB2,0x42,0x7A,0xCE,0x32,0x04,0x6D, + 0x82,0x42,0x62,0x72,0xD9,0xF4,0x5C,0x42,0x58,0x25,0x15,0x4B,0x25,0x95,0x52,0xCF,0x25,0x24,0x95,0x4C,0x85,0xF4,0x14,0xE5,0xD5,0x73,0xC9,0x86,0xBD,0x88,0x42,0xA2, + 0x52,0xC9,0xF5,0x5C,0x62,0xA2,0xBC,0x2A,0x24,0x95,0x28,0xC3,0x7E,0xAA,0x07,0x45,0x54,0xE2,0xDF,0x4F,0xF8,0x21,0xFF,0xC9,0xDD,0x48,0x71,0xF3,0x14,0x54,0x5F,0xC9, + 0x0E,0xFC,0x4A,0xB1,0x1F,0xC1,0xFE,0x1A,0xEC,0xB7,0xB8,0x83,0xE7,0x6C,0xB7,0xAA,0x3C,0x22,0x3C,0x0E,0x9D,0x74,0xD6,0xF0,0xBD,0x93,0x78,0x5D,0x0E,0xE2,0xAF,0x20, + 0x83,0xB2,0xE8,0x28,0xCA,0xCD,0x53,0x3B,0x66,0xD0,0x5B,0x99,0x78,0x78,0x32,0xCD,0xF4,0x56,0xD8,0xBB,0x14,0xEF,0xB1,0x81,0x5A,0xAD,0x56,0x4B,0xED,0x7E,0xA2,0x6F, + 0x29,0x1A,0xAE,0x58,0xD1,0xB3,0xFB,0x92,0x76,0x87,0x87,0xE3,0x50,0xAA,0x3B,0xA6,0xA2,0x9A,0xE5,0xCC,0x59,0x6A,0x54,0xC3,0x53,0xBD,0x28,0x66,0x3D,0x0B,0x57,0x8D, + 0xB7,0x4A,0xF1,0x6B,0x63,0xB8,0x68,0x3A,0x36,0x70,0x67,0xDB,0xC5,0x82,0xBA,0xC9,0x15,0x0B,0x26,0x38,0xB6,0xB3,0xA1,0xD8,0xF4,0x0D,0xF5,0x02,0xD9,0x9A,0x8F,0x69, + 0x86,0xFC,0x12,0x1E,0xDF,0x63,0x0E,0x42,0x19,0xC3,0x27,0x5C,0x2A,0xDE,0x4E,0xE5,0x29,0x38,0x20,0xF0,0x4A,0xE9,0xE6,0x42,0x44,0x6D,0x6E,0x39,0x1E,0x2D,0x22,0xC7, + 0xDF,0xF9,0x87,0xF4,0x46,0xA1,0x1A,0x21,0xD3,0xF1,0x7C,0x41,0x35,0x93,0xC4,0x9A,0x7A,0x9E,0xBE,0x4C,0x13,0x29,0x8C,0xD0,0xAC,0xFA,0x13,0xCB,0x81,0x65,0x3B,0xBE, + 0xDF,0xA0,0x6E,0x2A,0xA3,0x65,0x56,0x54,0xE3,0x47,0xEF,0xE3,0x44,0xCA,0x69,0x40,0x05,0x99,0x80,0x14,0xEF,0xCD,0xD3,0x1A,0xD8,0xE1,0x12,0x18,0x07,0x2D,0xE2,0x77, + 0x3A,0xD4,0xF0,0xD9,0x58,0xD8,0x2E,0x34,0x94,0xDE,0x55,0x01,0xDA,0x0A,0x69,0xA8,0x98,0x0E,0x65,0x14,0x15,0x6E,0x62,0x4E,0x2B,0x6B,0x0E,0xA6,0x9F,0x7C,0xB7,0xFB, + 0x35,0xD8,0xED,0x30,0x5D,0x56,0x6E,0x7C,0x05,0x72,0xE3,0x27,0x96,0xC0,0xE3,0x5B,0x00,0x6F,0x3B,0xE3,0xCD,0xFE,0x52,0xA7,0x1C,0x64,0xF3,0x19,0xAC,0x3C,0xAA,0x87, + 0xD3,0x2F,0xD1,0x9F,0xC0,0x07,0x14,0x1D,0x73,0x30,0x95,0xF0,0x8D,0x0A,0xE3,0x87,0x51,0x4F,0xB3,0xB5,0x07,0x3F,0x8A,0xA1,0x45,0x0D,0x0F,0xED,0x9C,0x79,0xE7,0xCC, + 0x3B,0xF9,0x2B,0xCD,0x12,0x5A,0xD9,0xB9,0xE5,0xCC,0x4A,0x7F,0x80,0x71,0xB2,0x26,0xC6,0x9F,0x29,0x32,0x8F,0xF9,0x21,0x2C,0x4F,0x82,0xFC,0x77,0x43,0x3C,0x87,0x61, + 0x33,0xF6,0xC8,0x5C,0x78,0x7F,0x28,0x63,0xAF,0x40,0x2B,0x11,0x96,0x35,0x24,0xAA,0x4E,0xD2,0xDE,0x0B,0x24,0x98,0x4C,0xBC,0xCF,0x6D,0x94,0x33,0x30,0x15,0x60,0x0B, + 0x25,0xC1,0x6E,0x55,0x86,0x0C,0xF2,0xE7,0x53,0x49,0x75,0x04,0x4C,0x6E,0x27,0x25,0x8F,0x26,0x65,0x7D,0xB2,0xF6,0x5E,0x15,0x67,0xCD,0x9F,0xC2,0x19,0x8B,0xD7,0x3F, + 0xCE,0x58,0x86,0xB7,0x65,0x0C,0x67,0x6C,0xFE,0x8A,0xCB,0xF8,0x8B,0x38,0x79,0xE7,0xC8,0x34,0xD8,0xFC,0x77,0x0B,0xCA,0xA5,0xC8,0x79,0x67,0x90,0x05,0x9F,0xF5,0x3A, + 0x20,0x0B,0xBE,0xA0,0xD3,0x9F,0x35,0x2D,0xB7,0x7B,0xAF,0x9C,0xF4,0x33,0x9A,0xB7,0x60,0x5D,0xED,0x72,0x51,0x56,0x94,0xCA,0x74,0x4A,0x4F,0x68,0x58,0x67,0xB6,0xD3, + 0x92,0x80,0x4F,0xE8,0x6E,0x16,0x34,0xC0,0xD3,0x7D,0x0A,0x3C,0x5E,0x03,0x5A,0x0B,0xB3,0x36,0x71,0xDC,0xE6,0x8D,0xEB,0xAC,0x3F,0xE2,0x57,0xF2,0x82,0xE6,0x15,0xA4, + 0x04,0xBF,0x91,0x6A,0x04,0x87,0x27,0x1A,0x6B,0xFC,0x6B,0x18,0x97,0xEC,0x6F,0x0A,0x44,0x70,0xBF,0x67,0xC0,0xDD,0x38,0x1B,0x65,0x7B,0x0B,0x39,0xF8,0xF5,0x57,0xCE, + 0xC9,0x0B,0xC8,0x1F,0xB6,0x96,0x15,0xDC,0xD7,0x4C,0xF6,0xE6,0x24,0xF5,0x55,0xA7,0xA7,0x65,0x23,0x42,0x5F,0xF8,0xC2,0x01,0x73,0x64,0x6C,0x4A,0xC7,0x74,0xA8,0xDA, + 0x96,0x70,0x9E,0x0B,0xB4,0x38,0x74,0x8A,0x2E,0x9E,0xA6,0x3A,0xC7,0xAF,0x18,0x82,0x72,0x47,0x16,0xFE,0x91,0x81,0x6B,0x00,0x50,0x26,0x39,0x71,0xEA,0xE2,0xEC,0x12, + 0xFB,0xEB,0x14,0x43,0x46,0x81,0x5F,0x43,0x3E,0xBA,0xB9,0x57,0x15,0x04,0xCA,0xE9,0xBE,0xB3,0x09,0x66,0xE3,0xE5,0x7E,0x93,0xA3,0x73,0x53,0x1A,0xFB,0xD2,0x6D,0x72, + 0xA2,0xE2,0x68,0xDD,0x93,0x17,0x28,0xD9,0x4B,0x7E,0xFE,0x02,0xC5,0xBD,0xA1,0x78,0x89,0x52,0x94,0xFF,0x7F,0x89,0xD4,0x4B,0x24,0x7F,0x69,0xAF,0x78,0x79,0xA4,0x77, + 0xE6,0x3F,0x91,0x87,0xD8,0xCB,0xC2,0x42,0x1E,0x14,0xEF,0x39,0x7F,0x22,0x2F,0xF2,0xF7,0x0B,0x9F,0x6F,0xAE,0xC9,0x60,0x5B,0x6C,0xB0,0x31,0xBA,0xFF,0x87,0x8C,0xF5, + 0x7F,0xF2,0x4D,0x08,0x7E,0x82,0x93,0x75,0xA9,0x7D,0x93,0xB5,0x58,0x14,0x9B,0x39,0x7F,0x2F,0xC4,0xFE,0xBE,0x4C,0xC9,0x64,0x22,0x63,0x65,0xA3,0xFB,0x34,0xE7,0x1C, + 0x07,0x03,0xFB,0x43,0x4B,0x7E,0x15,0x5D,0xE3,0x47,0xB6,0x60,0x2E,0x9E,0x3C,0xC6,0x3F,0xAA,0xC1,0x4E,0x1F,0x2F,0x5D,0xBA,0x3B,0x28,0x3C,0x44,0x12,0xFB,0xCA,0x82, + 0x44,0x3A,0xB9,0x48,0xEC,0xF3,0x63,0x89,0x0F,0x9E,0xA2,0x4F,0x26,0x39,0x0A,0x8E,0xE2,0x5F,0x80,0xC8,0xD4,0xC6,0x33,0xCF,0x04,0x5D,0x40,0xEA,0xA5,0x48,0xB4,0x4E, + 0xEA,0xFC,0x1B,0x12,0xA7,0xFF,0x09,0xDE,0x39,0xEA,0x1B,0x6C,0x4D,0x00,0x00 +}; +void ILibDefaultLogger_WebSocket_OnReceive(struct ILibWebServer_Session *sender, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebServer_DoneFlag done) +{ + ILibRemoteLogging logger = ILibChainGetLogger(ILibTransportChain(sender)); + + if (logger == NULL) { return; } + if (done == ILibWebServer_DoneFlag_Done) + { + // Web Socket Disconnected + ILibRemoteLogging_DeleteUserContext(logger, sender); + return; + } + if (done != ILibWebServer_DoneFlag_NotDone) { *beginPointer = endPointer; return; } // We're only going to handle the case where we receive a full fragment + + if (ILibRemoteLogging_Dispatch(logger, bodyBuffer, endPointer, sender) < 0) + { + ILibWebServer_WebSocket_Close(sender); + } + + *beginPointer = endPointer; +} +void ILibDefaultLogger_Session_OnReceive(struct ILibWebServer_Session *sender, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebServer_DoneFlag done) +{ + ILibRemoteLogging logger = ILibChainGetLogger(ILibTransportChain(sender)); + if (done != ILibWebServer_DoneFlag_Done || logger == NULL) { return; } + + switch (ILibWebServer_WebSocket_GetDataType(sender)) + { + case ILibWebServer_WebSocket_DataType_UNKNOWN: + if (header->DirectiveLength == 3 && strncasecmp(header->Directive, "GET", 3) == 0) + { + if (header->DirectiveObjLength == 1 && strncasecmp(header->DirectiveObj, "/", 1) == 0) + { + int flen; + char* f; + + flen = ILibReadFileFromDiskEx(&f, "web\\index.html"); + if (flen == 0) + { + ILibWebServer_StreamHeader_Raw(sender, 200, "OK", "\r\nContent-Type: text/html\r\nContent-Encoding: gzip", ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, (char*)ILibDefaultLogger_HTML, sizeof(ILibDefaultLogger_HTML), ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + else + { + ILibWebServer_StreamHeader_Raw(sender, 200, "OK", "\r\nContent-Type: text/html", ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, f, flen, ILibAsyncSocket_MemoryOwnership_CHAIN, ILibWebServer_DoneFlag_Done); + } + } + else + { + ILibWebServer_StreamHeader_Raw(sender, 404, "Not Found", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + } + else + { + ILibWebServer_StreamHeader_Raw(sender, 400, "Bad Request", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + break; + case ILibWebServer_WebSocket_DataType_REQUEST: + if (ILibWebServer_IsCrossSiteRequest(sender) == NULL) + { + sender->OnReceive = &ILibDefaultLogger_WebSocket_OnReceive; + ILibWebServer_UpgradeWebSocket(sender, 8192); + } + else + { + ILibRemoteLogging_printf(logger, ILibRemoteLogging_Modules_Microstack_Web, ILibRemoteLogging_Flags_VerbosityLevel_1, "Cross-Site Request!"); + ILibWebServer_StreamHeader_Raw(sender, 400, "Bad Request", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + } + break; + default: + ILibWebServer_StreamHeader_Raw(sender, 400, "Bad Request", NULL, ILibAsyncSocket_MemoryOwnership_STATIC); + ILibWebServer_StreamBody(sender, NULL, 0, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_Done); + break; + } +} +void ILibDefaultLogger_OnSession(struct ILibWebServer_Session *SessionToken, void *User) +{ + SessionToken->OnReceive = &ILibDefaultLogger_Session_OnReceive; +} +void ILibDefaultLogger_OnWrite(ILibRemoteLogging module, char* data, int dataLen, void *userContext) +{ + ILibTransport_Send(userContext, data, dataLen, ILibTransport_MemoryOwnership_USER, ILibTransport_DoneState_COMPLETE); +} +//! Initializes and starts a default ILibRemoteLogging module +/*! + \param chain Microstack chain to associate the logger with + \param portNumber Local port number to bind the WebListener to, which will serve the Web Interface (0 = random port) +\param path Path to the file to use for logging + \return The locally bound port number allocated to the WebListener +*/ +ILibExportMethod unsigned short ILibStartDefaultLoggerEx(void* chain, unsigned short portNumber, char *path) +{ + ((ILibBaseChain*)chain)->ChainLogger = ILibRemoteLogging_Create(ILibDefaultLogger_OnWrite); + ((ILibBaseChain*)chain)->LoggingWebServer = ILibWebServer_Create(chain, 5, portNumber, ILibDefaultLogger_OnSession, NULL); + if (path != NULL) + { + ((ILibBaseChain*)chain)->LoggingWebServerFileTransport = ILibRemoteLogging_CreateFileTransport(((ILibBaseChain*)chain)->ChainLogger, ILibRemoteLogging_Modules_UNKNOWN, ILibRemoteLogging_Flags_NONE, path, -1); + } + + ILibForceUnBlockChain(chain); + return(ILibWebServer_GetPortNumber(((ILibBaseChain*)chain)->LoggingWebServer)); +} +#endif + + +// +// Do not Modify this method. +// This decompresses compressed string blocks, which is used to store the +// various description documents +// +char* ILibDecompressString(unsigned char* CurrentCompressed, const int bufferLength, const int DecompressedLength) +{ + unsigned char *RetVal = (unsigned char*)malloc(DecompressedLength+1); + unsigned char *CurrentUnCompressed = RetVal; + unsigned char *EndPtr = RetVal + DecompressedLength; + int offset,length; + if (RetVal == NULL) ILIBCRITICALEXIT(254); + + UNREFERENCED_PARAMETER( bufferLength ); + + do + { + // UnCompressed Data Block + memcpy_s(CurrentUnCompressed, (int)*CurrentCompressed, CurrentCompressed + 1, (int)*CurrentCompressed); + MEMCHECK(assert((int)*CurrentCompressed <= (DecompressedLength+1) );) + + CurrentUnCompressed += (int)*CurrentCompressed; + CurrentCompressed += 1+((int)*CurrentCompressed); + + // CompressedBlock +#ifdef REQUIRES_MEMORY_ALIGNMENT + length = (unsigned short)((*(CurrentCompressed)) & 63); + offset = ((unsigned short)(*(CurrentCompressed+1))<<2) + (((unsigned short)(*(CurrentCompressed))) >> 6); +#else + length = (*((unsigned short*)(CurrentCompressed)))&((unsigned short)63); + offset = (*((unsigned short*)(CurrentCompressed)))>>6; +#endif + memcpy_s(CurrentUnCompressed, length, CurrentUnCompressed - offset, length); + MEMCHECK(assert(length <= (DecompressedLength+1)-(CurrentUnCompressed - RetVal));) + + CurrentCompressed += 2; + CurrentUnCompressed += length; + } while (CurrentUnCompressed < EndPtr); + + RetVal[DecompressedLength] = 0; + return((char*)RetVal); +} + +void ILibChain_RunOnMicrostackThreadSink(void *obj) +{ + void* chain = ((void**)obj)[0]; + ILibChain_StartEvent handler = (ILibChain_StartEvent)((void**)obj)[1]; + void* user = ((void**)obj)[2]; + + if (ILibIsChainBeingDestroyed(chain) == 0) + { + // Only Dispatch if the chain is still running + if (handler != NULL) { handler(chain, user); } + } + free(obj); +} +//! Dispatch an operation to the Microstack Chain thread +/*! + \param chain Microstack Chain to dispatch to + \param handler Event to dispatch on the microstack thread + \param user Custom user data to dispatch to the microstack thread +*/ +void ILibChain_RunOnMicrostackThreadEx(void *chain, ILibChain_StartEvent handler, void *user) +{ + void** value = NULL; + + value = (void**)ILibMemory_Allocate(3 * sizeof(void*), 0, NULL, NULL); + + value[0] = chain; + value[1] = handler; + value[2] = user; + ILibLifeTime_AddEx(ILibGetBaseTimer(chain), value, 0, &ILibChain_RunOnMicrostackThreadSink, &ILibChain_RunOnMicrostackThreadSink); +} + +void ILibChain_Safe_Destroy(void *object) +{ + free(object); +} +void ILibChain_SafeAddSink(void *object) +{ + struct ILibBaseChain_SafeData *data = (struct ILibBaseChain_SafeData*)object; + + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_2, "ILibChain_SafeAdd: %p [Pre: %p, Post: %p, Destroy: %p]", (void*)data->Object, (void*)((ILibChain_Link*)data->Object)->PreSelectHandler, (void*)((ILibChain_Link*)data->Object)->PostSelectHandler, (void*)((ILibChain_Link*)data->Object)->DestroyHandler); + + ILibAddToChain(data->Chain,data->Object); + free(data); +} + +/*! \fn void ILibChain_SafeAdd(void *chain, void *object) +\brief Dynamically add a link to a chain that is already running. +\par +\b Note: All objects added to the chain must extend/implement ILibChain +\param chain The chain to add the link to +\param object The link to add to the chain +*/ +void ILibChain_SafeAdd(void *chain, void *object) +{ + struct ILibBaseChain *baseChain = (struct ILibBaseChain*)chain; + struct ILibBaseChain_SafeData *data; + if ((data = (struct ILibBaseChain_SafeData*)malloc(sizeof(struct ILibBaseChain_SafeData))) == NULL) ILIBCRITICALEXIT(254); + memset(data, 0, sizeof(struct ILibBaseChain_SafeData)); + data->Chain = chain; + data->Object = object; + + ILibLifeTime_Add(baseChain->Timer, data, 0, &ILibChain_SafeAddSink, &ILibChain_Safe_Destroy); +} +void ILibChain_SafeRemoveEx(void *chain, void *object) +{ + ILibLinkedList links = ILibChain_GetLinks(chain); + void *node = ILibLinkedList_GetNode_Search(links, NULL, object); + ILibChain_Link *link = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(node); + + if (link != NULL) + { + if (link->DestroyHandler != NULL) { link->DestroyHandler(link); } + free(link); + ILibLinkedList_Remove(node); + } +} +/*! \fn void ILibChain_SafeRemove(void *chain, void *object) +\brief Dynamically remove a link from a chain that is already running. +\param chain The chain to remove a link from +\param object The link to remove +*/ +void ILibChain_SafeRemove(void *chain, void *object) +{ + if (ILibIsChainBeingDestroyed(chain) == 0) + { + // + // We are always going to context switch to microstack thread, even if we are already on microstack thread, + // because we cannot remove an item from the chain, if the call originated from the link that is being removed. + // By forcing a context switch, we will gaurantee that the call originates from the BaseTimer link. + // + ILibRemoteLogging_printf(ILibChainGetLogger(chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_2, "ILibChain_SafeRemove (%p)", (void*)object); + ILibChain_RunOnMicrostackThreadEx(chain, ILibChain_SafeRemoveEx, object); + } +} +//! Get the Links of a Chain object +/*! + \param chain Chain object to query + \return ILibLinkedList data structure holding the links +*/ +ILibLinkedList ILibChain_GetLinks(void *chain) +{ + return(((ILibBaseChain*)chain)->Links); +} + +void *ILibCreateChainEx(int extraMemorySize) +{ + struct ILibBaseChain *RetVal; + +#if defined(WIN32) + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 0); + + + if (WSAStartup(wVersionRequested, &wsaData) != 0) { ILIBCRITICALEXIT(1); } +#endif + RetVal = ILibMemory_Allocate(sizeof(ILibBaseChain), extraMemorySize, NULL, NULL); + + RetVal->Links = ILibLinkedList_CreateEx(sizeof(ILibChain_Link_Hook)); + RetVal->LinksPendingDelete = ILibLinkedList_Create(); + +#if defined(WIN32) + RetVal->TerminateFlag = 1; + RetVal->TerminateSock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT); + RetVal->ChainProcessHandle = GetCurrentProcess(); + if (!SymInitialize(RetVal->ChainProcessHandle, NULL, TRUE)) { RetVal->ChainProcessHandle = NULL; } +#endif + + RetVal->TerminateFlag = 0; + if (ILibChainLock_RefCounter == 0) + { + sem_init(&ILibChainLock, 0, 1); + } + ILibChainLock_RefCounter++; + + RetVal->Timer = ILibCreateLifeTime(RetVal); + + return(RetVal); +} + +/*! \fn ILibCreateChain() +\brief Creates an empty Chain +\return Chain +*/ +ILibExportMethod void *ILibCreateChain() +{ + return(ILibCreateChainEx(0)); +} + +/*! \fn ILibAddToChain(void *Chain, void *object) +\brief Add links to the chain +\par +\b Notes: +

Do NOT use this method to add a link to a chain that is already running.
All objects added to the chain must extend/implement ILibChain. +\param Chain The chain to add the link to +\param object The link to add to the chain +*/ +void ILibAddToChain(void *Chain, void *object) +{ + // + // Add link to the end of the chain (Linked List) + // + ILibLinkedList_AddTail(((ILibBaseChain*)Chain)->Links, object); + ((ILibChain_Link*)object)->ParentChain = Chain; +} + +//! Return the base timer for this chain. Most of the time, new timers probably don't need to be created +/*! + \param chain Microstack Chain to fetch the timer from + \return Base Timer +*/ +void* ILibGetBaseTimer(void *chain) +{ + //return ILibCreateLifeTime(chain); + return ((struct ILibBaseChain*)chain)->Timer; +} + +#ifdef WIN32 +void __stdcall ILibForceUnBlockChain_APC(ULONG_PTR obj) +{ + struct ILibBaseChain *c = (struct ILibBaseChain*)obj; + if (c->TerminateSock != ~0) + { + closesocket(c->TerminateSock); + } + c->TerminateSock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT); +} +#endif +/*! \fn ILibForceUnBlockChain(void *Chain) +\brief Forces a Chain to unblock, and check for pending operations +\param Chain The chain to unblock +*/ +void ILibForceUnBlockChain(void* Chain) +{ + struct ILibBaseChain *c = (struct ILibBaseChain*)Chain; + +#if defined(WIN32) + QueueUserAPC((PAPCFUNC)ILibForceUnBlockChain_APC, c->MicrostackThreadHandle !=NULL ? c->MicrostackThreadHandle : GetCurrentThread(), (ULONG_PTR)c); +#else + // + // Writing data on the pipe will trigger the select on Posix + // + if (c->TerminateWritePipe != NULL) + { + fprintf(c->TerminateWritePipe," "); + fflush(c->TerminateWritePipe); + } +#endif +} + +/*! \fn void ILibChain_DestroyEx(void *subChain) +\brief Destroys a chain or subchain that was never started. +\par +Do NOT call this on a chain that is running, or has been stopped. +
Do NOT call this on a subchain that was already added to another chain. +\param subChain The chain or subchain to destroy +*/ +void ILibChain_DestroyEx(void *subChain) +{ + ILibChain_Link* module; + void* node; + + if (subChain == NULL) return; + ((ILibBaseChain*)subChain)->TerminateFlag = 1; + + node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)subChain)->Links); + while (node != NULL && (module = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(node)) != NULL) + { + if (module->DestroyHandler != NULL) { module->DestroyHandler((void*)module); } + free(module); + node = ILibLinkedList_GetNextNode(node); + } + ILibLinkedList_Destroy(((ILibBaseChain*)subChain)->Links); + ILibLinkedList_Destroy(((ILibBaseChain*)subChain)->LinksPendingDelete); + +#ifdef _REMOTELOGGINGSERVER + ILibRemoteLogging_Destroy(((ILibBaseChain*)subChain)->ChainLogger); + if(((ILibBaseChain*)subChain)->LoggingWebServerFileTransport != NULL) { ILibTransport_Close(((ILibBaseChain*)subChain)->LoggingWebServerFileTransport); } +#endif + + free(subChain); +} +ILibChain_EventHookToken ILibChain_SetEventHook(void* chainLinkObject, int maxTimeout, ILibChain_EventHookHandler handler) +{ + ILibChain_Link_Hook *hook; + ILibChain_Link *link = (ILibChain_Link*)chainLinkObject; + ILibBaseChain *bChain = (ILibBaseChain*)link->ParentChain; + void *node = ILibLinkedList_GetNode_Search(bChain->Links, NULL, chainLinkObject); + hook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(node); + if (node != NULL && hook != NULL) + { + if (maxTimeout <= 0 && hook != NULL) { memset(hook, 0, sizeof(ILibChain_Link_Hook)); return(NULL); } + hook->MaxTimeout = maxTimeout; + hook->Handler = handler; + return(node); + } + else + { + return(NULL); + } +} +void ILibChain_UpdateEventHook(ILibChain_EventHookToken token, int maxTimeout) +{ + ILibChain_Link_Hook *hook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(token); + if (hook == NULL) { return; } + if (maxTimeout > 0) + { + hook->MaxTimeout = maxTimeout; + } + else + { + memset(hook, 0, sizeof(ILibChain_Link_Hook)); + } +} +ILibExportMethod void ILibChain_Continue(void *chain, ILibChain_Link **modules, int moduleCount, int maxTimeout) +{ + int i; + ILibBaseChain *root = (ILibBaseChain*)chain; + int slct = 0, v = 0, vX = 0; + struct timeval tv; + long endTime; + fd_set readset; + fd_set errorset; + fd_set writeset; + void *currentNode; + + if (root->continuationState != ILibChain_ContinuationState_INACTIVE) { return; } + root->continuationState = ILibChain_ContinuationState_CONTINUE; + currentNode = root->node; + + gettimeofday(&tv, NULL); + endTime = tv.tv_sec + maxTimeout; + + ILibRemoteLogging_printf(ILibChainGetLogger(chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ContinueChain..."); + + while (root->TerminateFlag == 0 && root->continuationState == ILibChain_ContinuationState_CONTINUE) + { + slct = 0; + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + + gettimeofday(&tv, NULL); + + if (tv.tv_sec >= endTime) + { + break; + } + + tv.tv_sec = endTime - tv.tv_sec; + tv.tv_usec = 0; + v = (int)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + + for (i = 0; i < moduleCount; ++i) + { + if (modules[i]->PreSelectHandler != NULL) + { + root->node = modules[i]; + vX = v; + modules[i]->PreSelectHandler((void*)modules[i], &readset, &writeset, &errorset, &vX); + if (vX < v) { v = vX; } + } + } + tv.tv_sec = v / 1000; + tv.tv_usec = 1000 * (v % 1000); + +#if defined(WIN32) + // + // Put the Terminate socket in the FDSET, for ILibForceUnblockChain + // + + FD_SET(root->TerminateSock, &errorset); +#else + // + // Put the Read end of the Pipe in the FDSET, for ILibForceUnBlockChain + // + FD_SET(fileno(root->TerminateReadPipe), &readset); +#endif + + + slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv); + if (slct == -1) + { + // + // If the select simply timed out, we need to clear these sets + // + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&errorset); + } + +#ifndef WIN32 + if (FD_ISSET(fileno(root->TerminateReadPipe), &readset)) + { + // + // Empty the pipe + // + while (fgetc(root->TerminateReadPipe) != EOF) + { + } + } +#endif + + for (i = 0; i < moduleCount && root->continuationState == ILibChain_ContinuationState_CONTINUE; ++i) + { + if (modules[i]->PostSelectHandler != NULL) + { + root->node = modules[i]; + modules[i]->PostSelectHandler((void*)modules[i], slct, &readset, &writeset, &errorset); + } + } + + } + ILibRemoteLogging_printf(ILibChainGetLogger(chain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "ContinueChain...Ending..."); + + root->continuationState = ILibChain_ContinuationState_INACTIVE; + root->node = currentNode; +} + +ILibExportMethod void ILibChain_EndContinue(void *chain) +{ + ((ILibBaseChain*)chain)->continuationState = ILibChain_ContinuationState_END_CONTINUE; + ILibForceUnBlockChain(chain); +} + +#if defined(WIN32) +int ILib_WindowsExceptionFilter(DWORD exceptionCode, void *exceptionInfo, CONTEXT *exceptionContext) +{ + if (IsDebuggerPresent()) { return(EXCEPTION_CONTINUE_SEARCH); } + if (exceptionCode == EXCEPTION_ACCESS_VIOLATION || exceptionCode == EXCEPTION_STACK_OVERFLOW || exceptionCode == EXCEPTION_INVALID_HANDLE) + { + memcpy_s(exceptionContext, sizeof(CONTEXT), ((EXCEPTION_POINTERS*)exceptionInfo)->ContextRecord, sizeof(CONTEXT)); + return(EXCEPTION_EXECUTE_HANDLER); + } + return(EXCEPTION_CONTINUE_SEARCH); +} + +void ILib_WindowsExceptionDebug(CONTEXT* exceptionContext) +{ + char buffer[4096]; + STACKFRAME64 StackFrame; + char symBuffer[4096]; + char imgBuffer[4096]; + DWORD MachineType; + int len = 0; + + ZeroMemory(&StackFrame, sizeof(STACKFRAME64)); + buffer[0] = 0; + +#ifndef WIN64 + MachineType = IMAGE_FILE_MACHINE_I386; + StackFrame.AddrPC.Offset = exceptionContext->Eip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = exceptionContext->Ebp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = exceptionContext->Esp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#else + MachineType = IMAGE_FILE_MACHINE_AMD64; + StackFrame.AddrPC.Offset = exceptionContext->Rip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = exceptionContext->Rsp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = exceptionContext->Rsp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#endif + + HANDLE hProcess = GetCurrentProcess(); + SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS); + if (SymInitialize(hProcess, NULL, TRUE)) + { + if (StackWalk64( + MachineType, + GetCurrentProcess(), + GetCurrentThread(), + &StackFrame, + MachineType == IMAGE_FILE_MACHINE_I386 + ? NULL + : exceptionContext, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) + { + + if (StackFrame.AddrPC.Offset != 0) + { + // + // Valid frame. + // + PIMAGEHLP_LINE64 pimg = (PIMAGEHLP_LINE64)imgBuffer; + DWORD64 tmp = 0; + DWORD tmp2; + PSYMBOL_INFO psym = (PSYMBOL_INFO)symBuffer; + psym->SizeOfStruct = sizeof(SYMBOL_INFO); + psym->MaxNameLen = MAX_SYM_NAME; + pimg->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + len = sprintf_s(buffer, sizeof(buffer), "FATAL UNHANDLED WINDOWS EXCEPTION."); + + if (SymFromAddr(GetCurrentProcess(), StackFrame.AddrPC.Offset, &tmp, psym)) + { + len += sprintf_s(buffer + len, sizeof(buffer) - len, "[%s", (char*)(psym->Name)); + if (SymGetLineFromAddr64(GetCurrentProcess(), StackFrame.AddrPC.Offset, &tmp2, pimg)) + { + len += sprintf_s(buffer + len, sizeof(buffer) - len, " => %s:%d]\n", (char*)(pimg->FileName), pimg->LineNumber); + } + else + { + len += sprintf_s(buffer + len, sizeof(buffer) - len, "]\n"); + } + } + else + { + util_tohex((char*)&(StackFrame.AddrPC.Offset), sizeof(DWORD64), imgBuffer); + len += sprintf_s(buffer + len, sizeof(buffer) - len, "[FuncAddr: 0x%s]\n", imgBuffer); + } + } + } + ILIBCRITICALEXITMSG(254, buffer); + } +} +#elif defined(_POSIX) +char ILib_POSIX_CrashParamBuffer[5 * sizeof(void*)]; +void ILib_POSIX_CrashHandler(int code) +{ + char msgBuffer[16384]; + int msgLen = 0; + void *buffer[100]; + char **strings; + int sz, i, c; + char *ptr; + + signal(SIGSEGV, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + + sz = backtrace(buffer, 100); + strings = backtrace_symbols(buffer, sz); + + if (code == SIGSEGV || code == SIGABRT) + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "** CRASH **\n", 12); + msgLen += 12; + } + else if (code == 254) + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "** CRITICAL EXIT **\n", 20); + msgLen += 20; + } + else + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "** Microstack Thread STUCK **\n", 30); + msgLen += 30; + } + + for (i = 1; i < sz; ++i) + { + pid_t pid; + int fd[2]; + size_t symlen = strnlen_s(strings[i], sizeof(msgBuffer)); + + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, strings[i], symlen); + msgLen += symlen; + msgBuffer[msgLen] = '\n'; + msgLen += 1; + + + ptr = strings[i]; + for (c = 0; c < (int)symlen; ++c) + { + if ((strings[i])[c] == '[') { ptr = strings[i] + c + 1; } + if ((strings[i])[c] == ']') { (strings[i])[c] = 0; } + if ((strings[i])[c] == 0) { break; } + } + + if (pipe(fd) == 0) + { + pid = vfork(); + if (pid == 0) + { + dup2(fd[1], STDOUT_FILENO); + close(fd[1]); + + ((char**)ILib_POSIX_CrashParamBuffer)[1] = ptr; + execv("/usr/bin/addr2line", (char**)ILib_POSIX_CrashParamBuffer); + if (write(STDOUT_FILENO, "??:0", 4)) {} + exit(0); + } + if (pid > 0) + { + char tmp[8192]; + int len; + + len = read(fd[0], tmp, 8192); + if (len > 0 && tmp[0] != '?') + { + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, "=> ", 3); + msgLen += 3; + memcpy_s(msgBuffer + msgLen, sizeof(msgBuffer) - msgLen, tmp, len); + msgLen += len; + } + close(fd[0]); + } + } + } + msgBuffer[msgLen] = 0; + ILIBCRITICALEXITMSG(254, buffer); +} +char* ILib_POSIX_InstallCrashHandler(char *exename) +{ + char *retVal = NULL; + if ((retVal = realpath(exename, NULL)) != NULL) + { + ((char**)ILib_POSIX_CrashParamBuffer)[0] = "addr2line"; + ((char**)ILib_POSIX_CrashParamBuffer)[2] = "-e"; + ((char**)ILib_POSIX_CrashParamBuffer)[3] = retVal; + ((char**)ILib_POSIX_CrashParamBuffer)[4] = NULL; + signal(SIGSEGV, ILib_POSIX_CrashHandler); + } + return(retVal); +} +#endif + +void ILibChain_DebugOffset(char *buffer, int bufferLen, uint64_t addrOffset) +{ +#ifdef WIN32 + int len = 0; + char symBuffer[4096]; + char imgBuffer[4096]; + PIMAGEHLP_LINE64 pimg = (PIMAGEHLP_LINE64)imgBuffer; + DWORD64 tmp = 0; + DWORD tmp2; + PSYMBOL_INFO psym = (PSYMBOL_INFO)symBuffer; + psym->SizeOfStruct = sizeof(SYMBOL_INFO); + psym->MaxNameLen = MAX_SYM_NAME; + pimg->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + SymInitialize(GetCurrentProcess(), NULL, TRUE); + + sprintf_s(buffer, bufferLen, "[Unable to decode function address]"); + + if (SymFromAddr(GetCurrentProcess(), (DWORD64)addrOffset, &tmp, psym)) + { + len += sprintf_s(buffer + len, bufferLen - len, "[%s", (char*)(psym->Name)); + if (SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addrOffset, &tmp2, pimg)) + { + len += sprintf_s(buffer + len, bufferLen - len, " => %s:%d]\n", (char*)(pimg->FileName), pimg->LineNumber); + } + else + { + len += sprintf_s(buffer + len, bufferLen - len, "]\n"); + } + } +#endif +} + +char* ILibChain_Debug(void *chain, char* buffer, int bufferLen) +{ + char *retVal = NULL; + ILibBaseChain *bChain = (ILibBaseChain*)chain; +#if defined(WIN32) + char symBuffer[4096]; + char imgBuffer[4096]; + DWORD MachineType; + STACKFRAME64 StackFrame; + DWORD64 addrOffset; + int len = 0; + CONTEXT ctx; + + ZeroMemory(&StackFrame, sizeof(STACKFRAME64)); + if (chain != NULL) + { + if (bChain->MicrostackThreadHandle != NULL) + { + SuspendThread(bChain->MicrostackThreadHandle); + memset(&(bChain->MicrostackThreadContext), 0, sizeof(CONTEXT)); + bChain->MicrostackThreadContext.ContextFlags = CONTEXT_FULL; + if (GetThreadContext(bChain->MicrostackThreadHandle, &(bChain->MicrostackThreadContext)) == 0) + { + len = GetLastError(); + } + ResumeThread(bChain->MicrostackThreadHandle); + } + } + else + { + RtlCaptureContext(&ctx); + } + + if (len != 0) { return(NULL); } + +#ifndef WIN64 + MachineType = IMAGE_FILE_MACHINE_I386; + StackFrame.AddrPC.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Eip : ctx.Eip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Ebp : ctx.Ebp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Esp : ctx.Esp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#else + MachineType = IMAGE_FILE_MACHINE_AMD64; + StackFrame.AddrPC.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Rip : ctx.Rip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Rsp : ctx.Rsp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = bChain != NULL ? bChain->MicrostackThreadContext.Rsp : ctx.Rsp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#endif + + buffer[0] = 0; + retVal = buffer; + + while (1) + { + if (!StackWalk64( + MachineType, + bChain != NULL ? bChain->ChainProcessHandle : GetCurrentProcess(), + bChain != NULL ? bChain->MicrostackThreadHandle : GetCurrentThread(), + &StackFrame, + MachineType == IMAGE_FILE_MACHINE_I386 + ? NULL + : (bChain!=NULL ? &(bChain->MicrostackThreadContext) : &ctx), + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) + { + // + // Maybe it failed, maybe we have finished walking the stack. + // + break; + } + + if (StackFrame.AddrPC.Offset != 0) + { + // + // Valid frame. + // + addrOffset = StackFrame.AddrPC.Offset; + { + PIMAGEHLP_LINE64 pimg = (PIMAGEHLP_LINE64)imgBuffer; + DWORD64 tmp = 0; + DWORD tmp2; + PSYMBOL_INFO psym = (PSYMBOL_INFO)symBuffer; + psym->SizeOfStruct = sizeof(SYMBOL_INFO); + psym->MaxNameLen = MAX_SYM_NAME; + pimg->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + if (SymFromAddr(bChain!=NULL && bChain->ChainProcessHandle != NULL ? bChain->ChainProcessHandle : GetCurrentProcess(), addrOffset, &tmp, psym)) + { + len += sprintf_s(buffer + len, bufferLen - len, "[%s", (char*)(psym->Name)); + if (SymGetLineFromAddr64(bChain != NULL && bChain->ChainProcessHandle != NULL ? bChain->ChainProcessHandle : GetCurrentProcess(), addrOffset, &tmp2, pimg)) + { + len += sprintf_s(buffer + len, bufferLen - len, " => %s:%d]\n", (char*)(pimg->FileName), pimg->LineNumber); + } + else + { + len += sprintf_s(buffer + len, bufferLen - len, "]\n"); + } + } + else + { + char addrBuffer[255]; + util_tohex((char*)&addrOffset, sizeof(DWORD64), addrBuffer); + len += sprintf_s(buffer + len, bufferLen - len, "[FuncAddr: 0x%s]\n", addrBuffer); + } + } + if (bChain != NULL && bChain->MicrostackThreadHandle == NULL) + { + break; + } + } + else + { + // + // Base reached. + // + break; + } + } +#else + pthread_kill(bChain->ChainThreadID, SIGUSR1); + return(NULL); +#endif + return(retVal); +} + + +#ifdef ILibChain_WATCHDOG_TIMEOUT +void ILibChain_WatchDogStart(void *obj) +{ + char msg[4096]; + ILibBaseChain *chain = (ILibBaseChain*)obj; + +#ifndef WIN32 + fd_set readset, writeset, errorset; + int slct; + struct timeval tv; + long stamp = ILibGetTimeStamp(); + tv.tv_usec = 0; + tv.tv_sec = ILibChain_WATCHDOG_TIMEOUT / 1000; +#endif + int Pre1=0, Pre2=0, Post1=0, Post2=0; + sprintf_s(msg, sizeof(msg), "Microstack STUCK: @ "); + +#ifdef WIN32 + while (WaitForSingleObject(chain->WatchDogTerminator, ILibChain_WATCHDOG_TIMEOUT) == WAIT_TIMEOUT) +#else + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + FD_SET(chain->WatchDogTerminator[0], &readset); + while((slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv)) == 0 || slct == EINTR) +#endif + { +#ifndef WIN32 + tv.tv_usec = 0; + tv.tv_sec = ILibChain_WATCHDOG_TIMEOUT / 1000; + + if (slct == EINTR) + { + slct = (int)(ILibGetTimeStamp() - stamp); + if (slct < ILibChain_WATCHDOG_TIMEOUT) + { + tv.tv_sec = (ILibChain_WATCHDOG_TIMEOUT - slct) / 1000; + continue; + } + } + stamp = ILibGetTimeStamp(); + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + FD_SET(chain->WatchDogTerminator[0], &readset); +#endif + Pre1 = chain->PreSelectCount; + Post1 = chain->PostSelectCount; + + if (Pre1 == Post1) + { + if (Pre1 == Pre2 && Pre2 != 0) + { + // STUCK! +#ifdef WIN32 + ILibChain_Debug(chain, msg+20, sizeof(msg)-20); + ILIBCRITICALEXITMSG(254, msg); +#else + pthread_kill(chain->ChainThreadID, SIGUSR1); + break; +#endif + } + else + { + Pre2 = Pre1; + Post2 = Post1; + } + } + } +} +#endif + +/*! \fn ILibStartChain(void *Chain) +\brief Starts a Chain +\par +This method will use the current thread. This thread will be refered to as the +microstack thread. All events and processing will be done on this thread. This method +will not return until ILibStopChain is called. +\param Chain The chain to start +*/ +ILibExportMethod void ILibStartChain(void *Chain) +{ + ILibBaseChain *chain = (ILibBaseChain*)Chain; + ILibChain_Link *module; + ILibChain_Link_Hook *nodeHook; + + fd_set readset; + fd_set errorset; + fd_set writeset; + + struct timeval tv; + int slct; + int v, vX; +#if !defined(WIN32) + int TerminatePipe[2]; + int flags; +#endif + + if (Chain == NULL) { return; } + chain->PreSelectCount = chain->PostSelectCount = 0; + +#if defined(WIN32) + chain->ChainThreadID = GetCurrentThreadId(); + chain->ChainProcessHandle = GetCurrentProcess(); + chain->MicrostackThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, chain->ChainThreadID); +#else + chain->ChainThreadID = pthread_self(); +#endif + + if (gILibChain == NULL) {gILibChain = Chain;} // Set the global instance if it's not already set +#if defined(ILibChain_WATCHDOG_TIMEOUT) +#ifdef WIN32 + chain->WatchDogTerminator = CreateEvent(NULL, TRUE, FALSE, NULL); +#else + if (pipe(chain->WatchDogTerminator) == 0) + { + flags = fcntl(chain->WatchDogTerminator[0], F_GETFL, 0); + fcntl(chain->WatchDogTerminator[0], F_SETFL, O_NONBLOCK | flags); + signal(SIGUSR1, ILib_POSIX_CrashHandler); + } +#endif + chain->WatchDogThread = ILibSpawnNormalThread(ILibChain_WatchDogStart, chain); +#endif + + // + // Use this thread as if it's our own. Keep looping until we are signaled to stop + // + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + +#if !defined(WIN32) + // + // For posix, we need to use a pipe to force unblock the select loop + // + if (pipe(TerminatePipe) == -1) {} // TODO: Pipe error + flags = fcntl(TerminatePipe[0],F_GETFL,0); + // + // We need to set the pipe to nonblock, so we can blindly empty the pipe + // + fcntl(TerminatePipe[0],F_SETFL,O_NONBLOCK|flags); + ((struct ILibBaseChain*)Chain)->TerminateReadPipe = fdopen(TerminatePipe[0],"r"); + ((struct ILibBaseChain*)Chain)->TerminateWritePipe = fdopen(TerminatePipe[1],"w"); +#endif + + chain->RunningFlag = 1; + while (chain->TerminateFlag == 0) + { + slct = 0; + FD_ZERO(&readset); + FD_ZERO(&errorset); + FD_ZERO(&writeset); + tv.tv_sec = UPNP_MAX_WAIT; + tv.tv_usec = 0; + + // + // Iterate through all the PreSelect function pointers in the chain + // + chain->node = ILibLinkedList_GetNode_Head(chain->Links); + v = (int)((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); + while(chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + if(module->PreSelectHandler != NULL) + { +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + vX = v; + module->PreSelectHandler((void*)module, &readset, &writeset, &errorset, &vX); + if (vX < v) { v = vX; } +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + } + nodeHook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(chain->node); + if (nodeHook->MaxTimeout > 0 && nodeHook->MaxTimeout < v) { v = nodeHook->MaxTimeout; } + chain->node = ILibLinkedList_GetNextNode(chain->node); + } + tv.tv_sec = v / 1000; + tv.tv_usec = 1000 * (v % 1000); + + +#if defined(WIN32) + // + // Add the fake socket, for ILibForceUnBlockChain + // + FD_SET(chain->TerminateSock, &errorset); +#else + // + // Put the Read end of the Pipe in the FDSET, for ILibForceUnBlockChain + // + FD_SET(TerminatePipe[0], &readset); +#endif + sem_wait(&ILibChainLock); + while(ILibLinkedList_GetCount(((ILibBaseChain*)Chain)->LinksPendingDelete) > 0) + { + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->LinksPendingDelete); + module = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node); + ILibLinkedList_Remove_ByData(((ILibBaseChain*)Chain)->Links, module); + ILibLinkedList_Remove(chain->node); + if (module != NULL) + { + if (module->DestroyHandler != NULL) { module->DestroyHandler((void*)module); } + free(module); + } + } + sem_post(&ILibChainLock); + + // + // The actual Select Statement + // + chain->PreSelectCount++; +#ifdef WIN32 + if (readset.fd_count == 0 && writeset.fd_count == 0) + { + SleepEx(v, TRUE); // If there is no pending IO, we must force the thread into an alertable wait state, so ILibForceUnblockChain can function. + slct = -1; + } + else + { + slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv); + } +#else + slct = select(FD_SETSIZE, &readset, &writeset, &errorset, &tv); +#endif + chain->PostSelectCount++; + + if (slct == -1) + { + // + // If the select simply timed out, we need to clear these sets + // + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&errorset); + } + +#ifndef WIN32 + if (FD_ISSET(TerminatePipe[0], &readset)) + { + // + // Empty the pipe + // + while (fgetc(((struct ILibBaseChain*)Chain)->TerminateReadPipe)!=EOF) + { + } + } +#endif + // + // Iterate through all of the PostSelect in the chain + // + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->Links); + while(chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + if (module->PostSelectHandler != NULL) + { +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + module->PostSelectHandler((void*)module, slct, &readset, &writeset, &errorset); +#ifdef MEMORY_CHECK +#ifdef WIN32 + //_CrtCheckMemory(); +#endif +#endif + } + nodeHook = (ILibChain_Link_Hook*)ILibLinkedList_GetExtendedMemory(chain->node); + if (nodeHook->Handler != NULL) { nodeHook->Handler(module, chain->node); } + chain->node = ILibLinkedList_GetNextNode(chain->node); + } + } + + // + // This loop will start, when the Chain was signaled to quit. Clean up the chain by iterating + // through all the Destroy methods. Not all modules in the chain will have a destroy method, + // but call the ones that do. + // + if (chain->WatchDogThread != NULL) + { +#ifdef WIN32 + SetEvent(chain->WatchDogTerminator); +#else + if (write(chain->WatchDogTerminator[1], " ", 1)) {} +#endif + } + + // Because many modules in the chain are using the base chain timer which is the first node + // in the chain in the base timer), these modules may start cleaning up their timers. So, we + // are going to make an exception and Destroy the base timer last, something that would usualy + // be destroyed first since it's at the start of the chain. This will allow modules to clean + // up nicely. + // + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->Links); + if(chain->node != NULL) {chain->node = ILibLinkedList_GetNextNode(chain->node);} // Skip the base timer which is the first element in the chain. + + // + // Set the Terminate Flag to 1, so that ILibIsChainBeingDestroyed returns non-zero when we start cleanup + // + ((struct ILibBaseChain*)Chain)->TerminateFlag = 1; + + while(chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + // + // If this module has a destroy method, call it. + // + if (module->DestroyHandler != NULL) module->DestroyHandler((void*)module); + // + // After calling the Destroy, we free the module and move to the next + // + free(module); + chain->node = ILibLinkedList_Remove(chain->node); + } + + if (gILibChain == Chain) { gILibChain = NULL; } // Reset the global instance if it was set + + // + // Go back and destroy the base timer for this chain, the first element in the chain. + // + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->Links); + if (chain->node!=NULL && (module=(ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node))!=NULL) + { + module->DestroyHandler((void*)module); + free(module); + ((ILibBaseChain*)Chain)->Timer = NULL; + } + + ILibLinkedList_Destroy(((ILibBaseChain*)Chain)->Links); + + chain->node = ILibLinkedList_GetNode_Head(((ILibBaseChain*)Chain)->LinksPendingDelete); + while(chain->node != NULL && (module = (ILibChain_Link*)ILibLinkedList_GetDataFromNode(chain->node)) != NULL) + { + if(module->DestroyHandler != NULL) {module->DestroyHandler((void*)module);} + free(module); + chain->node = ILibLinkedList_GetNextNode(chain->node); + } + ILibLinkedList_Destroy(((ILibBaseChain*)Chain)->LinksPendingDelete); + +#ifdef _REMOTELOGGINGSERVER + if (((ILibBaseChain*)Chain)->LoggingWebServer != NULL && ((ILibBaseChain*)Chain)->ChainLogger != NULL) + { + if (((ILibBaseChain*)Chain)->LoggingWebServerFileTransport != NULL) { ILibTransport_Close(((ILibBaseChain*)Chain)->LoggingWebServerFileTransport); } + ILibRemoteLogging_Destroy(((ILibBaseChain*)Chain)->ChainLogger); + } +#endif + if(((struct ILibBaseChain*)Chain)->ChainStash != NULL) {ILibHashtable_Destroy(((struct ILibBaseChain*)Chain)->ChainStash);} + + // + // Now we actually free the chain, before we just freed what the chain pointed to. + // +#if !defined(WIN32) + // + // Free the pipe resources + // + fclose(((ILibBaseChain*)Chain)->TerminateReadPipe); + fclose(((ILibBaseChain*)Chain)->TerminateWritePipe); + ((ILibBaseChain*)Chain)->TerminateReadPipe=0; + ((ILibBaseChain*)Chain)->TerminateWritePipe=0; +#endif +#if defined(WIN32) + if (((ILibBaseChain*)Chain)->TerminateSock != ~0) + { + closesocket(((ILibBaseChain*)Chain)->TerminateSock); + ((ILibBaseChain*)Chain)->TerminateSock = (SOCKET)~0; + } +#endif + +#ifdef WIN32 + WSACleanup(); +#endif + if (ILibChainLock_RefCounter == 1) + { + sem_destroy(&ILibChainLock); + } + --ILibChainLock_RefCounter; + free(Chain); +} + +/*! \fn ILibStopChain(void *Chain) +\brief Stops a chain +\par +This will signal the microstack thread to shutdown. When the chain cleans itself up, +the thread that is blocked on ILibStartChain will return. +\param Chain The Chain to stop +*/ +ILibExportMethod void ILibStopChain(void *Chain) +{ + ((struct ILibBaseChain*)Chain)->TerminateFlag = 2; + ILibForceUnBlockChain(Chain); +} + +/*! \fn ILibDestructXMLNodeList(struct ILibXMLNode *node) +\brief Frees resources from an XMLNodeList tree that was returned from ILibParseXML +\param node The XML Tree to clean up +*/ +void ILibDestructXMLNodeList(struct ILibXMLNode *node) +{ + struct ILibXMLNode *temp; + while (node!=NULL) + { + temp = node->Next; + if (node->Reserved2!=NULL) + { + // If there was a namespace table, delete it + ILibDestroyHashTree(node->Reserved2); + } + free(node); + node = temp; + } +} + +/*! \fn ILibDestructXMLAttributeList(struct ILibXMLAttribute *attribute) +\brief Frees resources from an AttributeList that was returned from ILibGetXMLAttributes +\param attribute The Attribute Tree to clean up +*/ +void ILibDestructXMLAttributeList(struct ILibXMLAttribute *attribute) +{ + struct ILibXMLAttribute *temp; + while (attribute!=NULL) + { + temp = attribute->Next; + free(attribute); + attribute = temp; + } +} + +/*! \fn ILibProcessXMLNodeList(struct ILibXMLNode *nodeList) +\brief Pro-process an XML node list +\par +Checks XML for validity, while at the same time populate helper properties on each node, +such as Parent, Peer, etc, to aid in XML parsing. +\param nodeList The XML Tree to process +\return 0 if the XML is valid, nonzero otherwise +*/ +int ILibProcessXMLNodeList(struct ILibXMLNode *nodeList) +{ + int RetVal = 0; + struct ILibXMLNode *current = nodeList; + struct ILibXMLNode *temp; + void *TagStack; + + ILibCreateStack(&TagStack); + + // + // Iterate through the node list, and setup all the pointers + // such that all StartElements have pointers to EndElements, + // And all StartElements have pointers to siblings and parents. + // + while (current != NULL) + { + if (current->Name == NULL) { ILibClearStack(&TagStack); return -1; } + if (memcmp(current->Name, "!", 1) == 0) + { + // Comment + temp = current; + current = (struct ILibXMLNode*)ILibPeekStack(&TagStack); + if (current!=NULL) + { + current->Next = temp->Next; + } + else + { + current = temp; + } + } + else if (current->StartTag!=0) + { + // Start Tag + current->Parent = (struct ILibXMLNode*)ILibPeekStack(&TagStack); + ILibPushStack(&TagStack,current); + } + else + { + // Close Tag + + // + // Check to see if there is supposed to be an EndElement + // + temp = (struct ILibXMLNode*)ILibPopStack(&TagStack); + if (temp != NULL) + { + // + // Checking to see if this EndElement is correct in scope + // + if (temp->NameLength == current->NameLength && memcmp(temp->Name, current->Name, current->NameLength)==0) + { + // + // Now that we know this EndElement is correct, set the Peer + // pointers of the previous sibling + // + if (current->Next != NULL) + { + if (current->Next->StartTag != 0) + { + temp->Peer = current->Next; + } + } + temp->ClosingTag = current; + current->StartingTag = temp; + } + else + { + // Illegal Close Tag Order + RetVal = -2; + break; + } + } + else + { + // Illegal Close Tag + RetVal = -1; + break; + } + } + current = current->Next; + } + + // + // If there are still elements in the stack, that means not all the StartElements + // have associated EndElements, which means this XML is not valid XML. + // + if (TagStack!=NULL) + { + // Incomplete XML + RetVal = -3; + ILibClearStack(&TagStack); + } + + return(RetVal); +} + +/*! \fn ILibXML_LookupNamespace(struct ILibXMLNode *currentLocation, char *prefix, int prefixLength) +\brief Resolves a namespace prefix from the scope of the given node +\param currentLocation The node used to start the resolve +\param prefix The namespace prefix to resolve +\param prefixLength The lenght of the prefix +\return The resolved namespace. NULL if unable to resolve +*/ +char* ILibXML_LookupNamespace(struct ILibXMLNode *currentLocation, char *prefix, int prefixLength) +{ + struct ILibXMLNode *temp = currentLocation; + int done = 0; + char* RetVal = ""; + + // + // If the specified Prefix is zero length, we interpret that to mean + // they want to lookup the default namespace + // + if (prefixLength == 0) + { + // + // This is the default namespace prefix + // + prefix = "xmlns"; + prefixLength = 5; + } + + // + // From the current node, keep traversing up the parents, until we find a match. + // Each step we go up, is a step wider in scope. + // + do + { + if (temp->Reserved2 != NULL) + { + if (ILibHasEntry(temp->Reserved2, prefix, prefixLength) != 0) + { + // + // As soon as we find the namespace declaration, stop + // iterating the tree, as it would be a waste of time + // + RetVal = (char*)ILibGetEntry(temp->Reserved2, prefix, prefixLength); + done = 1; + } + } + temp = temp->Parent; + } while (temp != NULL && done == 0); + return RetVal; +} + +/*! \fn ILibXML_BuildNamespaceLookupTable(struct ILibXMLNode *node) +\brief Builds the lookup table used by ILibXML_LookupNamespace +\param node This node will be the highest scoped +*/ +void ILibXML_BuildNamespaceLookupTable(struct ILibXMLNode *node) +{ + struct ILibXMLAttribute *attr,*currentAttr; + struct ILibXMLNode *current = node; + + // + // Iterate through all the StartElements, and build a table of the declared namespaces + // + while (current != NULL) + { + if (current->StartTag != 0) + { + // + // Reserved2 is the HashTable containing the fully qualified namespace + // keyed by the namespace prefix + // + current->Reserved2 = ILibInitHashTree(); + currentAttr = attr = ILibGetXMLAttributes(current); + if (attr != NULL) + { + // + // Iterate through all the attributes to find namespace declarations + // + while (currentAttr != NULL) + { + if (currentAttr->NameLength == 5 && currentAttr->Name != NULL && memcmp(currentAttr->Name, "xmlns", 5) == 0) + { + // Default Namespace Declaration + currentAttr->Value[currentAttr->ValueLength] = 0; + ILibAddEntry(current->Reserved2, "xmlns", 5, currentAttr->Value); + } + else if (currentAttr->PrefixLength == 5 && currentAttr->Prefix != NULL && memcmp(currentAttr->Prefix, "xmlns", 5) == 0) + { + // Other Namespace Declaration + currentAttr->Value[currentAttr->ValueLength] = 0; + ILibAddEntry(current->Reserved2, currentAttr->Name, currentAttr->NameLength, currentAttr->Value); + } + currentAttr=currentAttr->Next; + } + ILibDestructXMLAttributeList(attr); + } + } + current = current->Next; + } +} + + +/*! \fn ILibReadInnerXML(struct ILibXMLNode *node, char **RetVal) +\brief Reads the data segment from an ILibXMLNode +\par +The data is a pointer into the original string that the XML was read from. +\param node The node to read the data from +\param[out] RetVal The data +\return The length of the data read +*/ +int ILibReadInnerXML(struct ILibXMLNode *node, char **RetVal) +{ + struct ILibXMLNode *x = node; + int length = 0; + void *TagStack; + *RetVal = NULL; + + // + // Starting with the current StartElement, we use this stack to find the matching + // EndElement, so we can figure out what we need to return + // + ILibCreateStack(&TagStack); + do + { + if (x == NULL) + { + ILibClearStack(&TagStack); + return(0); + } + if (x->StartTag != 0) { ILibPushStack(&TagStack, x); } + + x = x->Next; + + if (x==NULL) + { + ILibClearStack(&TagStack); + return(0); + } + }while (!(x->StartTag==0 && ILibPopStack(&TagStack)==node && x->NameLength==node->NameLength && memcmp(x->Name,node->Name,node->NameLength)==0)); + + // + // The Reserved fields of the StartElement and EndElement are used as pointers representing + // the data segment of the XML + // + length = (int)((char*)x->Reserved - (char*)node->Reserved - 1); + if (length<0) {length=0;} + *RetVal = (char*)node->Reserved; + return(length); +} + +/*! \fn ILibGetXMLAttributes(struct ILibXMLNode *node) +\brief Reads the attributes from an XML node +\param node The node to read the attributes from +\return A linked list of attributes +*/ +struct ILibXMLAttribute *ILibGetXMLAttributes(struct ILibXMLNode *node) +{ + struct ILibXMLAttribute *RetVal = NULL; + struct ILibXMLAttribute *current = NULL; + char *c; + int EndReserved = (node->EmptyTag==0)?1:2; + int i; + int CheckName = node->Name[node->NameLength]==0?1:0; + + struct parser_result *xml; + struct parser_result_field *field,*field2; + struct parser_result *temp2; + struct parser_result *temp3; + + + // + // The reserved field is used to show where the data segments start and stop. We + // can also use them to figure out where the attributes start and stop + // + c = (char*)node->Reserved - 1; + while (*c!='<') + { + // + // The Reserved field of the StartElement points to the first character after + // the '>' of the StartElement. Just work our way backwards to find the start of + // the StartElement + // + c = c -1; + } + c = c +1; + + // + // Now that we isolated the string in between the '<' and the '>' we can parse the + // string as delimited by ' ', because thats what delineates attributes. We need + // to use ILibParseStringAdv because these attributes can be within quotation marks + // + // + // But before we start, replace linefeeds and carriage-return-linefeeds to spaces + // + for(i=0;i<(int)((char*)node->Reserved - c -EndReserved);++i) + { + if (c[i]==10 || c[i]==13 || c[i]==9 || c[i]==0) + { + c[i]=' '; + } + } + xml = ILibParseStringAdv(c,0,(int)((char*)node->Reserved - c -EndReserved)," ",1); + field = xml->FirstResult; + // + // We skip the first token, because the first token, is the Element name + // + if (field != NULL) {field = field->NextResult;} + + // + // Iterate through all the other tokens, as these are all attributes + // + while (field != NULL) + { + if (field->datalength > 0) + { + if (RetVal == NULL) + { + // + // If we haven't already created an Attribute node, create it now + // + if ((RetVal = (struct ILibXMLAttribute*)malloc(sizeof(struct ILibXMLAttribute))) == NULL) ILIBCRITICALEXIT(254); + RetVal->Next = NULL; + } + else + { + // + // We already created an Attribute node, so simply create a new one, and + // attach it on the beginning of the old one. + // + if ((current = (struct ILibXMLAttribute*)malloc(sizeof(struct ILibXMLAttribute))) == NULL) ILIBCRITICALEXIT(254); + current->Next = RetVal; + RetVal = current; + } + + // + // Parse each token by the ':' + // If this results in more than one token, we can figure that the first token + // is the namespace prefix + // + temp2 = ILibParseStringAdv(field->data,0,field->datalength,":",1); + if (temp2->NumResults == 1) + { + // + // This attribute has no prefix, so just parse on the '=' + // The first token is the attribute name, the other is the value + // + RetVal->Prefix = NULL; + RetVal->PrefixLength = 0; + temp3 = ILibParseStringAdv(field->data,0,field->datalength,"=",1); + if (temp3->NumResults == 1) + { + // + // There were whitespaces around the '=' + // + field2 = field->NextResult; + while (field2!=NULL) + { + if (!(field2->datalength==1 && memcmp(field2->data,"=",1)==0) && field2->datalength>0) + { + ILibDestructParserResults(temp3); + temp3 = ILibParseStringAdv(field->data,0,(int)((field2->data-field->data)+field2->datalength),"=",1); + field = field2; + break; + } + field2 = field2->NextResult; + } + } + } + else + { + // + // Since there is a namespace prefix, seperate that out, and parse the remainder + // on the '=' to figure out what the attribute name and value are + // + RetVal->Prefix = temp2->FirstResult->data; + RetVal->PrefixLength = temp2->FirstResult->datalength; + temp3 = ILibParseStringAdv(field->data,RetVal->PrefixLength+1,field->datalength-RetVal->PrefixLength-1,"=",1); + if (temp3->NumResults==1) + { + // + // There were whitespaces around the '=' + // + field2 = field->NextResult; + while (field2!=NULL) + { + if (!(field2->datalength==1 && memcmp(field2->data,"=",1)==0) && field2->datalength>0) + { + ILibDestructParserResults(temp3); + temp3 = ILibParseStringAdv(field->data,RetVal->PrefixLength+1,(int)((field2->data-field->data)+field2->datalength-RetVal->PrefixLength-1),"=",1); + field = field2; + break; + } + field2 = field2->NextResult; + } + } + } + // + // After attaching the pointers, we can free the results, as all the data + // is a pointer into the original string. We can think of the nodes we created + // as templates. Once done, we don't need them anymore. + // + ILibDestructParserResults(temp2); + RetVal->Parent = node; + RetVal->Name = temp3->FirstResult->data; + RetVal->Value = temp3->LastResult->data; + RetVal->NameLength = ILibTrimString(&(RetVal->Name),temp3->FirstResult->datalength); + RetVal->ValueLength = ILibTrimString(&(RetVal->Value),temp3->LastResult->datalength); + // + // Remove the Quote or Apostraphe if it exists + // + if (RetVal->ValueLength>=2 && (RetVal->Value[0]=='"' || RetVal->Value[0]=='\'')) + { + RetVal->Value += 1; + RetVal->ValueLength -= 2; + } + ILibDestructParserResults(temp3); + } + field = field->NextResult; + } + + ILibDestructParserResults(xml); + if (CheckName) + { + node->Name[node->NameLength]=0; + } + return(RetVal); +} + +/*! \fn ILibParseXML(char *buffer, int offset, int length) +\brief Parses an XML string. +\par +The strings are never copied. Everything is referenced via pointers into the original buffer +\param buffer The string to parse +\param offset starting index of \a buffer +\param length Length of \a buffer +\return A tree of ILibXMLNodes, representing the XML document +*/ +struct ILibXMLNode *ILibParseXML(char *buffer, int offset, int length) +{ + struct parser_result *xml; + struct parser_result_field *field; + struct parser_result *temp2; + struct parser_result *temp3; + char* TagName; + int TagNameLength; + int StartTag; + int EmptyTag; + int i; + int wsi; + + struct ILibXMLNode *RetVal = NULL; + struct ILibXMLNode *current = NULL; + struct ILibXMLNode *x = NULL; + + char *NSTag; + int NSTagLength; + + char *CommentEnd = 0; + int CommentIndex; + + // + // Even though "technically" the first character of an XML document must be < + // we're going to be nice, and not enforce that + // + while (buffer[offset]!='<' && length>0) + { + ++offset; + --length; + } + + if (length==0) + { + // Garbage in Garbage out :) + if ((RetVal = (struct ILibXMLNode*)malloc(sizeof(struct ILibXMLNode))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct ILibXMLNode)); + return(RetVal); + } + + // + // All XML Elements start with a '<' character. If we delineate the string with + // this character, we can go from there. + // + xml = ILibParseString(buffer,offset,length,"<",1); + field = xml->FirstResult; + while (field!=NULL) + { + // + // Ignore the XML declarator + // + if (field->datalength !=0 && memcmp(field->data,"?",1)!=0 && (field->data > CommentEnd)) + { + if (field->datalength>3 && memcmp(field->data,"!--",3)==0) + { + // + // XML Comment, find where it ends + // + CommentIndex = 3; + while (field->data+CommentIndex < buffer+offset+length && memcmp(field->data+CommentIndex,"-->",3)!=0) + { + ++CommentIndex; + } + CommentEnd = field->data + CommentIndex; + field = field->NextResult; + continue; + } + else + { + EmptyTag = 0; + if (memcmp(field->data,"/",1)==0) + { + // + // The first character after the '<' was a '/', so we know this is the + // EndElement + // + StartTag = 0; + field->data = field->data+1; + field->datalength -= 1; + // + // If we look for the '>' we can find the end of this element + // + temp2 = ILibParseString(field->data,0,field->datalength,">",1); + } + else + { + // + // The first character after the '<' was not a '/' so we know this is a + // StartElement + // + StartTag = -1; + // + // If we look for the '>' we can find the end of this element + // + temp2 = ILibParseString(field->data,0,field->datalength,">",1); + if (temp2->FirstResult->datalength>0 && temp2->FirstResult->data[temp2->FirstResult->datalength-1]=='/') + { + // + // If this element ended with a '/' this is an EmptyElement + // + EmptyTag = -1; + } + } + } + // + // Parsing on the ' ', we can isolate the Element name from the attributes. + // The first token, being the element name + // + wsi = ILibString_IndexOfFirstWhiteSpace(temp2->FirstResult->data,temp2->FirstResult->datalength); + // + // Now that we have the token that contains the element name, we need to parse on the ":" + // because we need to figure out what the namespace qualifiers are + // + temp3 = ILibParseString(temp2->FirstResult->data,0,wsi!=-1?wsi:temp2->FirstResult->datalength,":",1); + if (temp3->NumResults==1) + { + // + // If there is only one token, there was no namespace prefix. + // The whole token is the attribute name + // + NSTag = NULL; + NSTagLength = 0; + TagName = temp3->FirstResult->data; + TagNameLength = temp3->FirstResult->datalength; + } + else + { + // + // The first token is the namespace prefix, the second is the attribute name + // + NSTag = temp3->FirstResult->data; + NSTagLength = temp3->FirstResult->datalength; + TagName = temp3->FirstResult->NextResult->data; // Note: Klocwork reports that this may be NULL, but that is not possible, becuase NumResults is > 1 in this block + TagNameLength = temp3->FirstResult->NextResult->datalength; + } + ILibDestructParserResults(temp3); + + // + // Iterate through the tag name, to figure out what the exact length is, as + // well as check to see if its an empty element + // + for(i=0;i')||(TagName[i]=='\t')||(TagName[i]=='\r')||(TagName[i]=='\n') ) + { + if (i!=0) + { + if (TagName[i]=='/') + { + EmptyTag = -1; + } + TagNameLength = i; + break; + } + } + } + + if (TagNameLength!=0) + { + // + // Instantiate a new ILibXMLNode for this element + // + if ((x = (struct ILibXMLNode*)malloc(sizeof(struct ILibXMLNode))) == NULL) ILIBCRITICALEXIT(254); + memset(x,0,sizeof(struct ILibXMLNode)); + x->Name = TagName; + x->NameLength = TagNameLength; + x->StartTag = StartTag; + x->NSTag = NSTag; + x->NSLength = NSTagLength; + + if (StartTag==0) + { + // + // The Reserved field of StartElements point to te first character before + // the '<'. + // + x->Reserved = field->data; + do + { + x->Reserved = ((char*)x->Reserved) - 1; + }while (*((char*)x->Reserved)=='<'); + } + else + { + // + // The Reserved field of EndElements point to the end of the element + // + x->Reserved = temp2->LastResult->data; + } + + if (RetVal==NULL) + { + RetVal = x; + } + else + { + current->Next = x; + } + current = x; + if (EmptyTag!=0) + { + // + // If this was an empty element, we need to create a bogus EndElement, + // just so the tree is consistent. No point in introducing unnecessary complexity + // + if ((x = (struct ILibXMLNode*)malloc(sizeof(struct ILibXMLNode))) == NULL) ILIBCRITICALEXIT(254); + memset(x,0,sizeof(struct ILibXMLNode)); + x->Name = TagName; + x->NameLength = TagNameLength; + x->NSTag = NSTag; + x->NSLength = NSTagLength; + + x->Reserved = current->Reserved; + current->EmptyTag = -1; + current->Next = x; + current = x; + } + } + + ILibDestructParserResults(temp2); + } + field = field->NextResult; + } + + ILibDestructParserResults(xml); + return(RetVal); +} + +/*! \fn ILibQueue_Create() +\brief Create an empty Queue +\return An empty queue +*/ +ILibQueue ILibQueue_Create() +{ + return((ILibQueue)ILibLinkedList_Create()); +} + +/*! \fn ILibQueue_Lock(void *q) +\brief Locks a queue +\param q The queue to lock +*/ +void ILibQueue_Lock(ILibQueue q) +{ + ILibLinkedList_Lock((ILibLinkedList)q); +} + +/*! \fn ILibQueue_UnLock(void *q) +\brief Unlocks a queue +\param q The queue to unlock +*/ +void ILibQueue_UnLock(ILibQueue q) +{ + ILibLinkedList_UnLock((ILibLinkedList)q); +} + +/*! \fn ILibQueue_Destroy(void *q) +\brief Frees the resources associated with a queue +\param q The queue to free +*/ +void ILibQueue_Destroy(ILibQueue q) +{ + ILibLinkedList_Destroy((ILibLinkedList)q); +} + +/*! \fn ILibQueue_IsEmpty(void *q) +\brief Checks to see if a queue is empty +\param q The queue to check +\return zero value if not empty, non-zero if empty +*/ +int ILibQueue_IsEmpty(ILibQueue q) +{ + return(ILibLinkedList_GetNode_Head((ILibLinkedList)q)==NULL?1:0); +} + +/*! \fn ILibQueue_EnQueue(void *q, void *data) +\brief Add an item to the queue +\param q The queue to add to +\param data The data to add to the queue +*/ +void ILibQueue_EnQueue(ILibQueue q, void *data) +{ + ILibLinkedList_AddTail((ILibLinkedList)q,data); +} +/*! \fn ILibQueue_DeQueue(void *q) +\brief Removes an item from the queue +\param q The queue to pop the item from +\return The item popped off the queue. NULL if empty +*/ +void *ILibQueue_DeQueue(ILibQueue q) +{ + void *RetVal = NULL; + void *node; + + node = ILibLinkedList_GetNode_Head((ILibLinkedList)q); + if (node!=NULL) + { + RetVal = ILibLinkedList_GetDataFromNode(node); + ILibLinkedList_Remove(node); + } + return(RetVal); +} + +/*! \fn ILibQueue_PeekQueue(void *q) +\brief Peeks at an item from the queue +\param q The queue to peek an item from +\return The item from the queue. NULL if empty +*/ +void *ILibQueue_PeekQueue(ILibQueue q) +{ + void *RetVal = NULL; + void *node; + + node = ILibLinkedList_GetNode_Head((ILibLinkedList)q); + if (node!=NULL) + { + RetVal = ILibLinkedList_GetDataFromNode(node); + } + return(RetVal); +} + +/*! \fn ILibQueue_GetCount(void *q) +\brief Returns the number of items in the queue. +\param q The queue to query +\return The item count in the queue. +*/ +long ILibQueue_GetCount(ILibQueue q) +{ + return ILibLinkedList_GetCount((ILibLinkedList)q); +} + +/*! \fn ILibCreateStack(void **TheStack) +\brief Creates an empty Stack +\par +This module uses a void* that is preinitialized to NULL, eg:
+ +void *stack = NULL;
+ILibCreateStack(&stack);
+
+\param TheStack A void* to use for the stack. Simply pass in a void* by reference +*/ +void ILibCreateStack(void **TheStack) +{ + *TheStack = NULL; +} + +/*! \fn ILibPushStack(void **TheStack, void *data) +\brief Push an item onto the stack +\param TheStack The stack to push to +\param data The data to push onto the stack +*/ +void ILibPushStack(void **TheStack, void *data) +{ + struct ILibStackNode *RetVal; + if ((RetVal = (struct ILibStackNode*)malloc(sizeof(struct ILibStackNode))) == NULL) ILIBCRITICALEXIT(254); + RetVal->Data = data; + RetVal->Next = *TheStack; + *TheStack = RetVal; +} + +/*! \fn ILibPopStack(void **TheStack) +\brief Pop an item from the stack +\param TheStack The stack to pop from +\return The item that was popped from the stack +*/ +void *ILibPopStack(void **TheStack) +{ + void *RetVal = NULL; + void *Temp; + if (*TheStack!=NULL) + { + RetVal = ((struct ILibStackNode*)*TheStack)->Data; + Temp = *TheStack; + *TheStack = ((struct ILibStackNode*)*TheStack)->Next; + free(Temp); + } + return(RetVal); +} + +/*! \fn ILibPeekStack(void **TheStack) +\brief Peek at an item from the stack +\param TheStack The stack to peek from +\return The item that is currently on the top of the stack +*/ +void *ILibPeekStack(void **TheStack) +{ + void *RetVal = NULL; + if (*TheStack!=NULL) + { + RetVal = ((struct ILibStackNode*)*TheStack)->Data; + } + return(RetVal); +} + +/*! \fn ILibClearStack(void **TheStack) +\brief Clears all the items from the stack +\param TheStack The stack to clear +*/ +void ILibClearStack(void **TheStack) +{ + void *Temp = *TheStack; + do + { + ILibPopStack(&Temp); + }while (Temp!=NULL); + *TheStack = NULL; +} + +/*! \fn ILibHashTree_Lock(void *hashtree) +\brief Locks a HashTree +\param hashtree The HashTree to lock +*/ +void ILibHashTree_Lock(void *hashtree) +{ + struct HashNode_Root *r = (struct HashNode_Root*)hashtree; + sem_wait(&(r->LOCK)); +} + +/*! \fn ILibHashTree_UnLock(void *hashtree) +\brief Unlocks a HashTree +\param hashtree The HashTree to unlock +*/ +void ILibHashTree_UnLock(void *hashtree) +{ + struct HashNode_Root *r = (struct HashNode_Root*)hashtree; + sem_post(&(r->LOCK)); +} + +/*! \fn ILibDestroyHashTree(void *tree) +\brief Frees resources associated with a HashTree +\param tree The HashTree to free +*/ +void ILibDestroyHashTree(void *tree) +{ + struct HashNode_Root *r = (struct HashNode_Root*)tree; + struct HashNode *c = r->Root; + struct HashNode *n; + + sem_destroy(&(r->LOCK)); + while (c != NULL) + { + // + // Iterate through each node, and free all the resources + // + n = c->Next; + if (c->KeyValue!=NULL) free(c->KeyValue); + free(c); + c = n; + } + free(r); +} + +/*! \fn ILibHashTree_GetEnumerator(void *tree) +\brief Returns an Enumerator for a HashTree +\par +Functionally identicle to an IDictionaryEnumerator in .NET +\param tree The HashTree to get an enumerator for +\return An enumerator +*/ +void *ILibHashTree_GetEnumerator(void *tree) +{ + // + // The enumerator is basically a state machine that keeps track of which node we are at + // in the tree. So initialize it to the root. + // + struct HashNodeEnumerator *en; + if ((en = (struct HashNodeEnumerator*)malloc(sizeof(struct HashNodeEnumerator))) == NULL) ILIBCRITICALEXIT(254); + en->node = ((struct HashNode_Root*)tree)->Root; + return en; +} + +/*! \fn ILibHashTree_DestroyEnumerator(void *tree_enumerator) +\brief Frees resources associated with an Enumerator created by ILibHashTree_GetEnumerator +\param tree_enumerator The enumerator to free +*/ +void ILibHashTree_DestroyEnumerator(void *tree_enumerator) +{ + // + // The enumerator just contains a pointer, so we can just free the enumerator + // + free(tree_enumerator); +} + +/*! \fn ILibHashTree_MoveNext(void *tree_enumerator) +\brief Advances an enumerator to the next item +\param tree_enumerator The enumerator to advance +\return A zero value if successful, nonzero if no more items +*/ +int ILibHashTree_MoveNext(void *tree_enumerator) +{ + struct HashNodeEnumerator *en = (struct HashNodeEnumerator*)tree_enumerator; + if (en->node != NULL) + { + // + // Advance the enumerator to point to the next node. If there is a node + // return 0, else return 1 + // + en->node = en->node->Next; + return(en->node != NULL?0:1); + } + else + { + // + // There are no more nodes, so just return 1 + // + return 1; + } +} + +/*! \fn ILibHashTree_GetValue(void *tree_enumerator, char **key, int *keyLength, void **data) +\brief Reads from the current item of an enumerator +\param tree_enumerator The enumerator to read from +\param[out] key The key of the current item +\param[out] keyLength The length of the key of the current item +\param[out] data The data of the current item +*/ +void ILibHashTree_GetValue(void *tree_enumerator, char **key, int *keyLength, void **data) +{ + struct HashNodeEnumerator *en = (struct HashNodeEnumerator*)tree_enumerator; + + // + // All we do, is just assign the pointers. + // + if (key != NULL) {*key = en->node->KeyValue;} + if (keyLength != NULL) {*keyLength = en->node->KeyLength;} + if (data != NULL) {*data = en->node->Data;} +} +/*! \fn void ILibHashTree_GetValueEx(void *tree_enumerator, char **key, int *keyLength, void **data, int *dataEx) +\brief Reads from the current item of an enumerator +\param tree_enumerator The enumerator to read from +\param[out] key The key of the current item +\param[out] keyLength The length of the key of the current item +\param[out] data The data of the current item +\param[out] dataEx The extended data of the current item +*/ +void ILibHashTree_GetValueEx(void *tree_enumerator, char **key, int *keyLength, void **data, int *dataEx) +{ + struct HashNodeEnumerator *en = (struct HashNodeEnumerator*)tree_enumerator; + + // + // All we do, is just assign the pointers. + // + if (key != NULL) {*key = en->node->KeyValue;} + if (keyLength != NULL) {*keyLength = en->node->KeyLength;} + if (data != NULL) {*data = en->node->Data;} + if (dataEx != NULL) {*dataEx = en->node->DataEx;} +} + +/*! \fn ILibInitHashTree() +\brief Creates an empty ILibHashTree, whose keys are case sensitive. +\return An empty ILibHashTree +*/ +void* ILibInitHashTree() +{ + struct HashNode_Root *Root; + struct HashNode *RetVal; + if ((Root = (struct HashNode_Root*)malloc(sizeof(struct HashNode_Root))) == NULL) ILIBCRITICALEXIT(254); + if ((RetVal = (struct HashNode*)malloc(sizeof(struct HashNode))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal, 0, sizeof(struct HashNode)); + memset(Root, 0, sizeof(struct HashNode_Root)); + Root->Root = RetVal; + sem_init(&(Root->LOCK), 0, 1); + return(Root); +} +/*! \fn void* ILibInitHashTree_CaseInSensitive() +\brief Creates an empty ILibHashTree, whose keys are case insensitive. +\return An empty ILibHashTree +*/ +void* ILibInitHashTree_CaseInSensitive() +{ + struct HashNode_Root *Root = (struct HashNode_Root*)ILibInitHashTree(); + Root->CaseInSensitive = 1; + return(Root); +} + + +void ILibToUpper(const char *in, int inLength, char *out) +{ + int i; + + for(i = 0; i < inLength; ++i) + { + if (in[i]>=97 && in[i]<=122) + { + //LOWER + out[i] = in[i]-32; + } + else + { + //CAP + out[i] = in[i]; + } + } +} +void ILibToLower(const char *in, int inLength, char *out) +{ + int i; + + for(i = 0; i < inLength; ++i) + { + if (in[i] >= 65 && in[i] <= 90) + { + //CAP + out[i] = in[i]+32; + } + else + { + //LOWER + out[i] = in[i]; + } + } +} + +int ILibGetHashValueEx(char *key, int keylength, int caseInSensitiveText) +{ + int HashValue=0; + char TempValue[4]; + + if (keylength <= 4) + { + // + // If the key length is <= 4, the hash is just the key expressed as an integer + // + memset(TempValue, 0, 4); + if (caseInSensitiveText==0) + { + memcpy_s(TempValue, sizeof(TempValue), key, keylength); + } + else + { + ILibToLower(key, keylength, TempValue); + } + MEMCHECK(assert(keylength <= 4);) + + HashValue = *((int*)TempValue); + } + else + { + // + // If the key length is >4, the hash is the first 4 bytes XOR with the last 4 + // + + if (caseInSensitiveText == 0) + { + memcpy_s(TempValue, sizeof(TempValue), key, 4); + } + else + { + ILibToLower(key, 4, TempValue); + } + HashValue = *((int*)TempValue); + if (caseInSensitiveText==0) + { + memcpy_s(TempValue, sizeof(TempValue), (char*)key+(keylength-4),4); + } + else + { + ILibToLower((char*)key+(keylength-4),4,TempValue); + } + HashValue = HashValue^(*((int*)TempValue)); + + + // + // If the key length is >= 10, the hash is also XOR with the middle 4 bytes + // + if (keylength>=10) + { + if (caseInSensitiveText==0) + { + memcpy_s(TempValue, sizeof(TempValue), (char*)key+(keylength/2),4); + } + else + { + ILibToLower((char*)key+(keylength/2),4,TempValue); + } + HashValue = HashValue^(*((int*)TempValue)); + } + } + return(HashValue); +} + +/*! \fn ILibGetHashValue(char *key, int keylength) +\brief Calculates a numeric Hash from a given string +\par +Used by ILibHashTree methods +\param key The string to hash +\param keylength The length of the string to hash +\return A hash value +*/ +int ILibGetHashValue(char *key, int keylength) +{ + return(ILibGetHashValueEx(key,keylength,0)); +} + + + +// +// Determines if a key entry exists in a HashTree, and creates it if requested +// +struct HashNode* ILibFindEntry(void *hashtree, void *key, int keylength, int create) +{ + struct HashNode_Root *root = (struct HashNode_Root*)hashtree; + struct HashNode *current = root->Root; + int HashValue = ILibGetHashValueEx(key, keylength, root->CaseInSensitive); + int done = 0; + + if (keylength == 0){return(NULL);} + + // + // Iterate through our tree to see if we can find this key entry + // + while (done == 0) + { + // + // Integer compares are very fast, this will weed out most non-matches + // + if (current->KeyHash == HashValue) + { + // + // Verify this is really a match + // + if (root->CaseInSensitive == 0) + { + if (current->KeyLength == keylength && memcmp(current->KeyValue, key, keylength) == 0) + { + return current; + } + } + else + { + if (current->KeyLength == keylength && strncasecmp(current->KeyValue, key, keylength) == 0) + { + return current; + } + } + } + + if (current->Next != NULL) + { + current = current->Next; + } + else if (create != 0) + { + // + // If there is no match, and the create flag is set, we need to create an entry + // + if ((current->Next = (struct HashNode*)malloc(sizeof(struct HashNode))) == NULL) ILIBCRITICALEXIT(254); + memset(current->Next,0,sizeof(struct HashNode)); + current->Next->Prev = current; + current->Next->KeyHash = HashValue; + if ((current->Next->KeyValue = (void*)malloc(keylength + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(current->Next->KeyValue, keylength + 1, key ,keylength); + current->Next->KeyValue[keylength] = 0; + current->Next->KeyLength = keylength; + return(current->Next); + } + else + { + return NULL; + } + } + return NULL; +} + +/*! \fn ILibHasEntry(void *hashtree, char* key, int keylength) +\brief Determines if a key entry exists in a HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\return 0 if does not exist, nonzero otherwise +*/ +int ILibHasEntry(void *hashtree, char* key, int keylength) +{ + // + // This can be duplicated by calling Find entry, but setting the create flag to false + // + return(ILibFindEntry(hashtree, key, keylength, 0) != NULL?1:0); +} + +/*! \fn ILibAddEntry(void* hashtree, char* key, int keylength, void *value) +\brief Adds an item to the HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\param value The data to add into the HashTree +*/ +void ILibAddEntry(void* hashtree, char* key, int keylength, void *value) +{ + // + // This can be duplicated by calling FindEntry, and setting create to true + // + struct HashNode* n = ILibFindEntry(hashtree, key, keylength, 1); + if (n != NULL) n->Data = value; +} +/*! \fn ILibAddEntryEx(void* hashtree, char* key, int keylength, void *value, int valueLength) +\brief Adds an extended item to the HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\param value The data to add into the HashTree +\param valueEx An optional int value +*/ +void ILibAddEntryEx(void* hashtree, char* key, int keylength, void *value, int valueEx) +{ + // + // This can be duplicated by calling FindEntry, and setting create to true + // + struct HashNode* n = ILibFindEntry(hashtree, key, keylength, 1); + if (n != NULL) + { + n->Data = value; + n->DataEx = valueEx; + } +} + + +/*! \fn ILibGetEntry(void *hashtree, char* key, int keylength) +\brief Gets an item from a HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +\return The data in the HashTree. NULL if key does not exist +*/ +void* ILibGetEntry(void *hashtree, char* key, int keylength) +{ + // + // This can be duplicated by calling FindEntry and setting create to false. + // If a match is found, just return the data + // + struct HashNode* n = ILibFindEntry(hashtree, key, keylength, 0); + if (n == NULL) + { + return(NULL); + } + else + { + return(n->Data); + } +} +/*! \fn void ILibGetEntryEx(void *hashtree, char *key, int keyLength, void **value, int* valueEx) +\brief Gets an extended item from a HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keyLength The length of the key +\param[out] value The data in the HashTree. NULL if key does not exist +\param[out] valueEx The extended data in the HashTree. +*/ +ILibExportMethod void ILibGetEntryEx(void *hashtree, char *key, int keyLength, void **value, int* valueEx) +{ + // + // This can be duplicated by calling FindEntry and setting create to false. + // If a match is found, just return the data + // + struct HashNode* n = ILibFindEntry(hashtree, key, keyLength, 0); + if (n == NULL) + { + *value = NULL; + *valueEx = 0; + } + else + { + *value = n->Data; + *valueEx = n->DataEx; + } +} + +/*! \fn ILibDeleteEntry(void *hashtree, char* key, int keylength) +\brief Deletes a keyed item from the HashTree +\param hashtree The HashTree to operate on +\param key The key +\param keylength The length of the key +*/ +void ILibDeleteEntry(void *hashtree, char* key, int keylength) +{ + // + // First find the entry + // + struct HashNode* n = ILibFindEntry(hashtree,key,keylength,0); + if (n != NULL) + { + // + // Then remove it from the tree + // + n->Prev->Next = n->Next; + if (n->Next != NULL) n->Next->Prev = n->Prev; + free(n->KeyValue); + free(n); + } +} + +/*! \fn ILibGetLong(char *TestValue, int TestValueLength, long* NumericValue) +\brief Reads a long value from a string, in a validating fashion +\param TestValue The string to read from +\param TestValueLength The length of the string +\param NumericValue The long value extracted from the string +\return 0 if succesful, nonzero if there was an error +*/ +int ILibGetLong(char *TestValue, int TestValueLength, long* NumericValue) +{ + char* StopString; + char* TempBuffer; + + if ((TempBuffer = (char*)malloc(1+sizeof(char)*TestValueLength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(TempBuffer, 1 + sizeof(char)*TestValueLength, TestValue, TestValueLength); + TempBuffer[TestValueLength] = '\0'; + *NumericValue = strtol(TempBuffer,&StopString,10); + if (*StopString != '\0') + { + // + // If strtol stopped somewhere other than the end, there was an error + // + free(TempBuffer); + return(-1); + } + else + { + // + // Now just check errno to see if there was an error reported + // + free(TempBuffer); + if (errno!=ERANGE) return(0); else return(-1); + } +} + +/*! \fn int ILibGetULong(const char *TestValue, const int TestValueLength, unsigned long* NumericValue) +\brief Reads an unsigned long value from a string, in a validating fashion +\param TestValue The string to read from +\param TestValueLength The length of the string +\param NumericValue The long value extracted from the string +\return 0 if succesful, nonzero if there was an error +*/ +int ILibGetULong(const char *TestValue, const int TestValueLength, unsigned long* NumericValue) +{ + char* StopString; + char* TempBuffer; + + if ((TempBuffer = (char*)malloc(1 + sizeof(char)*TestValueLength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(TempBuffer, 1 + sizeof(char)*TestValueLength, (char*)TestValue, TestValueLength); + TempBuffer[TestValueLength] = '\0'; + *NumericValue = strtoul(TempBuffer, &StopString, 10); + + if (*StopString!='\0') + { + free(TempBuffer); + return(-1); + } + else + { + free(TempBuffer); + + if (errno!=ERANGE) + { + if (memcmp(TestValue,"-",1) == 0) return(-1); + return(0); + } + else + { + return(-1); + } + } +} + + +// +// Determines if a buffer offset is a delimiter +// +int ILibIsDelimiter (const char* buffer, int offset, int buffersize, const char* Delimiter, int DelimiterLength) +{ + // + // For simplicity sake, we'll assume a match unless proven otherwise + // + int i=0; + int RetVal = 1; + if (DelimiterLength>buffersize) + { + // + // If the offset plus delimiter length is greater than the buffersize + // There can't possible be a match, so don't bother looking + // + return(0); + } + + for(i=0;iFirstResult = NULL; + RetVal->NumResults = 0; + + // + // By default we will always return at least one token, which will be the + // entire string if the delimiter is not found. + // + // Iterate through the string to find delimiters + // + Token = buffer + offset; + for(i = offset;i < (length+offset);++i) + { + if (StringDelimiter == 0) + { + if (buffer[i] == '"') + { + // + // Ignore everything inside double quotes + // + StringDelimiter = '"'; + Ignore = 1; + } + else + { + if (buffer[i] == '\'') + { + // + // Ignore everything inside single quotes + // + StringDelimiter = '\''; + Ignore = 1; + } + } + } + else + { + // + // Once we isolated everything inside double or single quotes, we can get + // on with the real parsing + // + if (buffer[i] == StringDelimiter) + { + Ignore = ((Ignore == 0)?1:0); + } + } + if (Ignore == 0 && ILibIsDelimiter(buffer, i, length, Delimiter, DelimiterLength)) + { + // + // We found a delimiter in the string + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(253); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + + // + // After we populate the values, we advance the token to after the delimiter + // to prep for the next token + // + ++RetVal->NumResults; + i = i + DelimiterLength -1; + Token = Token + TokenLength + DelimiterLength; + TokenLength = 0; + } + else + { + // + // No match yet, so just increment this counter + // + ++TokenLength; + } + } + + // + // Create a result for the last token, since it won't be caught in the above loop + // because if there are no more delimiters, than the entire last portion of the string since the + // last delimiter is the token + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(254); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + ++RetVal->NumResults; + + return(RetVal); +} + +/*! \fn ILibTrimString(char **theString, int length) +\brief Trims leading and trailing whitespace characters +\param theString The string to trim +\param length Length of \a theString +\return Length of the trimmed string +*/ +int ILibTrimString(char **theString, int length) +{ + while (length > 0 && ((*theString)[0] == 9 || (*theString)[0] == 32)) { length--; (*theString)++; } // Remove any blank chars at the start + while (length > 0 && ((*theString)[length - 1] == 9 || (*theString)[length - 1] == 32)) { length--; } // Remove any blank chars at the end + return length; +} + +/*! \fn ILibParseString(char* buffer, int offset, int length, char* Delimiter, int DelimiterLength) +\brief Parses a string into a linked list of tokens. +\par +Differs from \a ILibParseStringAdv, in that this method does not ignore characters contained within +quotation marks, whereas \a ILibParseStringAdv does. +\param buffer The buffer to parse +\param offset The offset of the buffer to start parsing +\param length The length of the buffer to parse +\param Delimiter The delimiter +\param DelimiterLength The length of the delimiter +\return A list of tokens +*/ +struct parser_result* ILibParseString(char* buffer, int offset, int length, const char* Delimiter, int DelimiterLength) +{ + int i = 0; + char* Token = NULL; + int TokenLength = 0; + struct parser_result* RetVal; + struct parser_result_field *p_resultfield; + + if ((RetVal = (struct parser_result*)malloc(sizeof(struct parser_result))) == NULL) ILIBCRITICALEXIT(254); + RetVal->FirstResult = NULL; + RetVal->NumResults = 0; + + // + // By default we will always return at least one token, which will be the + // entire string if the delimiter is not found. + // + // Iterate through the string to find delimiters + // + Token = buffer + offset; + for(i = offset; i < length; ++i) + { + if (ILibIsDelimiter(buffer, i, length, Delimiter, DelimiterLength)) + { + // + // We found a delimiter in the string + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(254); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + + // + // After we populate the values, we advance the token to after the delimiter + // to prep for the next token + // + ++RetVal->NumResults; + i = i + DelimiterLength -1; + Token = Token + TokenLength + DelimiterLength; + TokenLength = 0; + } + else + { + // + // No match yet, so just increment this counter + // + ++TokenLength; + } + } + + // + // Create a result for the last token, since it won't be caught in the above loop + // because if there are no more delimiters, than the entire last portion of the string since the + // last delimiter is the token + // + if ((p_resultfield = (struct parser_result_field*)malloc(sizeof(struct parser_result_field))) == NULL) ILIBCRITICALEXIT(254); + p_resultfield->data = Token; + p_resultfield->datalength = TokenLength; + p_resultfield->NextResult = NULL; + if (RetVal->FirstResult != NULL) + { + RetVal->LastResult->NextResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + else + { + RetVal->FirstResult = p_resultfield; + RetVal->LastResult = p_resultfield; + } + ++RetVal->NumResults; + + return(RetVal); +} + +parser_result_field* ILibParseString_GetResultIndex(parser_result* r, int index) +{ + int i; + parser_result_field *retVal = r->FirstResult; + + for (i = 0; i < index - 1; ++i) + { + retVal = retVal != NULL ? retVal->NextResult : NULL; + } + return(retVal); +} +/*! \fn ILibDestructParserResults(struct parser_result *result) +\brief Frees resources associated with the list of tokens returned from ILibParseString and ILibParseStringAdv. +\param result The list of tokens to free +*/ +void ILibDestructParserResults(struct parser_result *result) +{ + // + // All of these nodes only contain pointers + // so we just need to iterate through all the nodes and free them + // + struct parser_result_field *node = result->FirstResult; + struct parser_result_field *temp; + + while (node != NULL) + { + temp = node->NextResult; + free(node); + node = temp; + } + free(result); +} + +/*! \fn ILibDestructPacket(struct packetheader *packet) +\brief Frees resources associated with a Packet that was created either by \a ILibCreateEmptyPacket or \a ILibParsePacket +\param packet The packet to free +*/ +void ILibDestructPacket(struct packetheader *packet) +{ + struct packetheader_field_node *node = packet->FirstField; // TODO: *********** CRASH ON EXIT SOMETIMES OCCURS HERE + struct packetheader_field_node *nextnode; + + // + // Iterate through all the headers + // + while (node != NULL) + { + nextnode = node->NextField; + if (node->UserAllocStrings != 0) + { + // + // If the user allocated the string, then we need to free it. + // Otherwise these are just pointers into others strings, in which + // case we don't want to free them + // + free(node->Field); + free(node->FieldData); + } + free(node); + node = nextnode; + } + if (packet->UserAllocStrings != 0) + { + // + // If this flag was set, it means the used ILibCreateEmptyPacket, + // and set these fields manually, which means the string was copied. + // In which case, we need to free the strings + // + if (packet->StatusData != NULL) {free(packet->StatusData);} + if (packet->Directive != NULL) {free(packet->Directive);} + if (packet->Reserved == NULL && packet->DirectiveObj != NULL) {free(packet->DirectiveObj);} + if (packet->Reserved != NULL) {free(packet->Reserved);} + if (packet->Body != NULL) free(packet->Body); + } + if (packet->UserAllocVersion != 0) + { + free(packet->Version); + } + ILibDestroyHashTree(packet->HeaderTable); + free(packet); +} + +/*! \fn ILibHTTPEscape(char* outdata, const char* data) +\brief Escapes a string according to HTTP Specifications. +\par +The string you would want to escape would typically be the string used in the Path portion +of an HTTP request. eg:
+GET foo/bar.txt HTTP/1.1
+
+\b Note: It should be noted that the output buffer needs to be allocated prior to calling this method. +The required space can be determined by calling \a ILibHTTPEscapeLength. +\param outdata The escaped string +\param[in,out] data The string to escape +\return The length of the escaped string +*/ +int ILibHTTPEscapeEx(char* outdata, const char* data, size_t dataLen) +{ + int i = 0; + int x = 0; + char hex[4]; + + while (data[x] != 0 && x < (int)dataLen) + { + if ( (data[x]>=63 && data[x]<=90) || (data[x]>=97 && data[x]<=122) || (data[x]>=47 && data[x]<=57) \ + || data[x]==59 || data[x]==47 || data[x]==63 || data[x]==58 || data[x]==64 || data[x]==61 \ + || data[x]==43 || data[x]==36 || data[x]==45 || data[x]==95 || data[x]==46 || data[x]==42) + { + // + // These are all the allowed values for HTTP. If it's one of these characters, we're ok + // + outdata[i] = data[x]; + ++i; + } + else + { + // + // If it wasn't one of these characters, then we need to escape it + // + sprintf_s(hex, 4, "%02X", (unsigned char)data[x]); + outdata[i] = '%'; + outdata[i+1] = hex[0]; + outdata[i+2] = hex[1]; + i+=3; + } + ++x; + } + outdata[i] = 0; + return (i+1); +} + +/*! \fn ILibHTTPEscapeLength(const char* data) +\brief Determines the buffer space required to HTTP escape a particular string. +\param data Calculates the length requirements as if \a data was escaped +\return The minimum required length +*/ +int ILibHTTPEscapeLengthEx(const char* data, size_t dataLen) +{ + int i=0; + int x=0; + while (data[x] != 0 && i < (int)dataLen) + { + if ((data[x] >= 63 && data[x] <= 90) || (data[x] >= 97 && data[x] <= 122) || (data[x] >= 47 && data[x] <= 57) \ + || data[x] == 59 || data[x] == 47 || data[x] == 63 || data[x] == 58 || data[x] == 64 || data[x] == 61 \ + || data[x] == 43 || data[x] == 36 || data[x] == 45 || data[x] == 95 || data[x] == 46 || data[x] == 42) + { + // No need to escape + ++i; + } + else + { + // Need to escape + i += 3; + } + ++x; + } + return(i+1); +} + +/*! \fn ILibInPlaceHTTPUnEscape(char* data) +\brief Unescapes a given string according to HTTP encoding rules +\par +The escaped representation of a string is always longer than the unescaped version +so this method will overwrite the escaped string, with the unescaped result. +\param[in,out] data The buffer to unescape +\return The length of the unescaped string +*/ +int ILibInPlaceHTTPUnEscapeEx(char* data, int length) +{ + char hex[3]; + char *stp; + int src_x=0; + int dst_x=0; + + hex[2]=0; + + while (src_xHeaderTable = ILibInitHashTree_CaseInSensitive(); + + // + // All the headers are delineated with a CRLF, so we parse on that + // + p = (struct parser_result*)ILibParseString(buffer, offset, length, "\r\n", 2); + _packet = p; + f = p->FirstResult; + // + // The first token is where we can figure out the Method, Path, Version, etc. + // + StartLine = (struct parser_result*)ILibParseString(f->data, 0, f->datalength, " ", 1); + HeaderLine = f->NextResult; + if (memcmp(StartLine->FirstResult->data, "HTTP/", 5) == 0 && StartLine->FirstResult->NextResult != NULL) + { + // + // If the StartLine starts with HTTP/, then we know this is a response packet. + // We parse on the '/' character to determine the Version, as it follows. + // eg: HTTP/1.1 200 OK + // + p = (struct parser_result*)ILibParseString(StartLine->FirstResult->data, 0, StartLine->FirstResult->datalength, "/", 1); + RetVal->Version = p->LastResult->data; + RetVal->VersionLength = p->LastResult->datalength; + RetVal->Version[RetVal->VersionLength] = 0; + ILibDestructParserResults(p); + if ((tempbuffer = (char*)malloc(1+sizeof(char)*(StartLine->FirstResult->NextResult->datalength))) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(tempbuffer,1 + StartLine->FirstResult->NextResult->datalength, StartLine->FirstResult->NextResult->data, StartLine->FirstResult->NextResult->datalength); + MEMCHECK(assert(StartLine->FirstResult->NextResult->datalength <= 1+(int)sizeof(char)*(StartLine->FirstResult->NextResult->datalength));) + + // + // The other tokens contain the Status code and data + // + tempbuffer[StartLine->FirstResult->NextResult->datalength] = '\0'; + RetVal->StatusCode = (int)atoi(tempbuffer); + free(tempbuffer); + RetVal->StatusData = StartLine->FirstResult->NextResult->NextResult->data; + RetVal->StatusDataLength = StartLine->FirstResult->NextResult->NextResult->datalength; + } + else + { + + // + // If the packet didn't start with HTTP/ then we know it's a request packet + // eg: GET /index.html HTTP/1.1 + // The method (or directive), is the first token, and the Path + // (or DirectiveObj) is the second, and version in the 3rd. + // + RetVal->Directive = StartLine->FirstResult->data; + RetVal->DirectiveLength = StartLine->FirstResult->datalength; + if (StartLine->FirstResult->NextResult!=NULL) + { + RetVal->DirectiveObj = StartLine->FirstResult->NextResult->data; + RetVal->DirectiveObjLength = StartLine->FirstResult->NextResult->datalength; + } + else + { + // Invalid packet + ILibDestructParserResults(_packet); + ILibDestructParserResults(StartLine); + ILibDestructPacket(RetVal); + return(NULL); + } + + RetVal->StatusCode = -1; + // + // We parse the last token on '/' to find the version + // + p = (struct parser_result*)ILibParseString(StartLine->LastResult->data, 0, StartLine->LastResult->datalength, "/", 1); + RetVal->Version = p->LastResult->data; + RetVal->VersionLength = p->LastResult->datalength; + RetVal->Version[RetVal->VersionLength] = 0; + ILibDestructParserResults(p); + + RetVal->Directive[RetVal->DirectiveLength] = '\0'; + RetVal->DirectiveObj[RetVal->DirectiveObjLength] = '\0'; + } + // + // Headerline starts with the second token. Then we iterate through the rest of the tokens + // + while (HeaderLine != NULL) + { + if (HeaderLine->datalength == 0 || HeaderLine->data == NULL) + { + // + // An empty line signals the end of the headers + // + break; + } + if (node != NULL && (HeaderLine->data[0] == ' ' || HeaderLine->data[0] == 9)) + { + // + // This is a multi-line continuation + // + if (node->UserAllocStrings == 0) + { + tempbuffer = node->FieldData; + if ((node->FieldData = (char*)malloc(node->FieldDataLength + HeaderLine->datalength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->FieldData, node->FieldDataLength + HeaderLine->datalength, tempbuffer, node->FieldDataLength); + + tempbuffer = node->Field; + if ((node->Field = (char*)malloc(node->FieldLength + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->Field, node->FieldLength + 1, tempbuffer, node->FieldLength); + + node->UserAllocStrings = -1; + } + else + { + if ((tmp = (char*)realloc(node->FieldData, node->FieldDataLength + HeaderLine->datalength)) == NULL) ILIBCRITICALEXIT(254); + node->FieldData = tmp; + } + memcpy_s(node->FieldData+node->FieldDataLength, HeaderLine->datalength, HeaderLine->data + 1, HeaderLine->datalength - 1); + node->FieldDataLength += (HeaderLine->datalength-1); + } + else + { + // + // Instantiate a new header entry for each new token + // + if ((node = (struct packetheader_field_node*)malloc(sizeof(struct packetheader_field_node))) == NULL) ILIBCRITICALEXIT(254); + memset(node, 0, sizeof(struct packetheader_field_node)); + for(i = 0; i < HeaderLine->datalength; ++i) + { + if (*((HeaderLine->data) + i) == ':') + { + node->Field = HeaderLine->data; + node->FieldLength = i; + node->FieldData = HeaderLine->data + i + 1; + node->FieldDataLength = (HeaderLine->datalength)-i-1; + break; + } + } + if (node->Field == NULL) + { + // + // Invalid header line. Let's just ignore it and move on + // + free(node); + node = NULL; + HeaderLine = HeaderLine->NextResult; + continue; + } + // + // We need to do white space processing, because we need to ignore them in the + // headers + // So do a 'trim' operation + // + node->FieldDataLength = ILibTrimString(&(node->FieldData),node->FieldDataLength); + node->Field[node->FieldLength] = '\0'; + node->FieldData[node->FieldDataLength] = '\0'; + + // + // Since we are parsing an existing string, we set this flag to zero, so that it doesn't + // get freed + // + node->UserAllocStrings = 0; + node->NextField = NULL; + + if (RetVal->FirstField==NULL) + { + // + // If there aren't any headers yet, this will be the first + // + RetVal->FirstField = node; + RetVal->LastField = node; + } + else + { + // + // There are already headers, so link this in the tail + // + RetVal->LastField->NextField = node; // Note: Klocwork says LastField could be NULL/dereferenced, but LastField is never going to be NULL. + } + RetVal->LastField = node; + ILibAddEntryEx(RetVal->HeaderTable,node->Field,node->FieldLength,node->FieldData,node->FieldDataLength); + } + HeaderLine = HeaderLine->NextResult; + } + ILibDestructParserResults(_packet); + ILibDestructParserResults(StartLine); + return(RetVal); +} + +/*! \fn ILibFragmentTextLength(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength) +\brief Determines the buffer size required to fragment a string +\param text The string to fragment +\param textLength Length of \a text +\param delimiter Line delimiter +\param delimiterLength Length of \a delimiter +\param tokenLength The maximum size of each fragment or token +\return The length of the buffer required to call \a ILibFragmentText +*/ +int ILibFragmentTextLength(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength) +{ + int RetVal; + + UNREFERENCED_PARAMETER( text ); + UNREFERENCED_PARAMETER( delimiter ); + + RetVal = textLength + (((textLength/tokenLength)==1?0:(textLength/tokenLength))*delimiterLength); + return(RetVal); +} + +/*! \fn ILibFragmentText(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength, char **RetVal) +\brief Fragments a string into multiple tokens +\param text The string to fragment +\param textLength Length of \a text +\param delimiter Line delimiter +\param delimiterLength Length of \a delimiter +\param tokenLength The maximum size of each fragment or token +\param RetVal The buffer to store the resultant string +\return The length of the written string +*/ +int ILibFragmentText(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength, char **RetVal) +{ + char *Buffer; + int i=0,i2=0; + int BufferSize = 0; + int allocSize = ILibFragmentTextLength(text, textLength, delimiter, delimiterLength, tokenLength); + if ((*RetVal = (char*)malloc(allocSize)) == NULL) ILIBCRITICALEXIT(254); + + Buffer = *RetVal; + + i2 = textLength; + while (i2!=0) + { + if (i2!=textLength) + { + memcpy_s(Buffer+i, allocSize - i, delimiter,delimiterLength); + i+=delimiterLength; + BufferSize += delimiterLength; + } + memcpy_s(Buffer+i, allocSize - i, text + (textLength-i2),i2>tokenLength?tokenLength:i2); + i+=i2>tokenLength?tokenLength:i2; + BufferSize += i2>tokenLength?tokenLength:i2; + i2 -= i2>tokenLength?tokenLength:i2; + } + return(BufferSize); +} + +/*! \fn ILibGetRawPacket(struct packetheader* packet,char **RetVal) +\brief Converts a packetheader structure into a raw char* buffer +\par +\b Note: The returned buffer must be freed +\param packet The packetheader struture to convert +\param[out] RetVal The output char* buffer +\return The length of the output buffer +*/ +int ILibGetRawPacket(struct packetheader* packet, char **RetVal) +{ + int i,i2; + int BufferSize = 0; + char* Buffer, *temp; + + void *en; + + char *Field; + int FieldLength; + void *FieldData; + int FieldDataLength; + + if (packet->StatusCode != -1) + { + BufferSize = 12 + packet->VersionLength + packet->StatusDataLength; + // + // HTTP/1.1 200 OK\r\n + // 12 is the total number of literal characters. Just add Version and StatusData + // HTTP/ OK \r\n + // + } + else + { + BufferSize = packet->DirectiveLength + packet->DirectiveObjLength + 12; + // + // GET / HTTP/1.1\r\n + // This is calculating the length for a request packet. + // ToDo: This isn't completely correct + // But it will work as long as the version is not > 9.9 + // It should also add the length of the Version, but it's not critical. + } + + en = ILibHashTree_GetEnumerator(packet->HeaderTable); + while (ILibHashTree_MoveNext(en)==0) + { + ILibHashTree_GetValueEx(en,&Field,&FieldLength,(void**)&FieldData,&FieldDataLength); + if (FieldDataLength < 0) { continue; } + // + // A conservative estimate adding the lengths of the header name and value, plus + // 4 characters for the ':' and CRLF + // + BufferSize += FieldLength + FieldDataLength + 4; + + // + // If the header is longer than MAX_HEADER_LENGTH, we need to break it up + // into multiple lines, so we need to calculate the space needed for the + // delimiters. + // + if (FieldDataLength>MAX_HEADER_LENGTH) + { + BufferSize += ILibFragmentTextLength(FieldData, FieldDataLength, "\r\n ", 3, MAX_HEADER_LENGTH); + } + } + ILibHashTree_DestroyEnumerator(en); + + // + // Another conservative estimate adding in the packet body length plus a padding of 3 + // for the empty line + // + BufferSize += (3 + packet->BodyLength); + + // + // Allocate the buffer + // + if ((Buffer = *RetVal = (char*)malloc(BufferSize)) == NULL) ILIBCRITICALEXIT(254); + if (packet->StatusCode != -1) + { + // + // Write the response + // + memcpy_s(Buffer, BufferSize, "HTTP/", 5); + memcpy_s(Buffer + 5, BufferSize - 5, packet->Version, packet->VersionLength); + i = 5 + packet->VersionLength; + + i += sprintf_s(Buffer + i, BufferSize - i, " %d ", packet->StatusCode); + memcpy_s(Buffer + i, BufferSize - i, packet->StatusData ,packet->StatusDataLength); + i += packet->StatusDataLength; + + memcpy_s(Buffer + i, BufferSize - i, "\r\n", 2); + i += 2; + /* HTTP/1.1 200 OK\r\n */ + } + else + { + // + // Write the Request + // + memcpy_s(Buffer, BufferSize, packet->Directive,packet->DirectiveLength); + i = packet->DirectiveLength; + memcpy_s(Buffer+i, BufferSize - i, " ",1); + i+=1; + memcpy_s(Buffer+i, BufferSize - i, packet->DirectiveObj,packet->DirectiveObjLength); + i+=packet->DirectiveObjLength; + memcpy_s(Buffer+i, BufferSize - i, " HTTP/",6); + i+=6; + memcpy_s(Buffer+i, BufferSize - i, packet->Version,packet->VersionLength); + i+=packet->VersionLength; + memcpy_s(Buffer+i, BufferSize - i, "\r\n",2); + i+=2; + /* GET / HTTP/1.1\r\n */ + } + + en = ILibHashTree_GetEnumerator(packet->HeaderTable); + while (ILibHashTree_MoveNext(en)==0) + { + ILibHashTree_GetValueEx(en,&Field,&FieldLength,(void**)&FieldData,&FieldDataLength); + if (FieldDataLength < 0) { continue; } + + // + // Write each header + // + memcpy_s(Buffer+i, BufferSize - i, Field,FieldLength); + i+=FieldLength; + memcpy_s(Buffer+i, BufferSize - i, ": ",2); + i+=2; + + if (ILibFragmentTextLength(FieldData,FieldDataLength,"\r\n ",3, MAX_HEADER_LENGTH)>FieldDataLength) + { + // Fragment this + i2 = ILibFragmentText(FieldData,FieldDataLength,"\r\n ",3, MAX_HEADER_LENGTH,&temp); + memcpy_s(Buffer+i, BufferSize - i, temp,i2); + i += i2; + free(temp); + } + else + { + // No need to fragment this + memcpy_s(Buffer+i, BufferSize - i, FieldData,FieldDataLength); + i += FieldDataLength; + } + + memcpy_s(Buffer+i, BufferSize - i, "\r\n",2); + i+=2; + } + ILibHashTree_DestroyEnumerator(en); + + // + // Write the empty line + // + memcpy_s(Buffer+i, BufferSize - i, "\r\n",2); + i+=2; + + // + // Write the body + // + memcpy_s(Buffer+i, BufferSize - i, packet->Body,packet->BodyLength); + i+=packet->BodyLength; + Buffer[i] = '\0'; + + return(i); +} + +// TCP: SOCK_STREAM, IPPROTO_TCP +// UDP: SOCK_DGRAM, IPPROTO_UDP +SOCKET ILibGetSocket(struct sockaddr *localif, int type, int protocol) +{ + int off = 0; + SOCKET sock; + if (localif->sa_family == AF_INET6 && g_ILibDetectIPv6Support == 0) { ILIBMARKPOSITION(1); return 0; } + if ((sock = socket(localif->sa_family, type, protocol)) == -1) { ILIBMARKPOSITION(2); return 0; } + if (localif->sa_family == AF_INET6) if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); +#ifdef SO_NOSIGPIPE + { + int set = 1; + setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); // Turn off SIGPIPE if writing to disconnected socket + } +#endif +#if defined(WIN32) + if (bind(sock, localif, INET_SOCKADDR_LENGTH(localif->sa_family)) != 0) { ILIBMARKPOSITION(3); closesocket(sock); return 0; } +#else + if (bind(sock, localif, INET_SOCKADDR_LENGTH(localif->sa_family)) != 0) { ILIBMARKPOSITION(4); close(sock); return 0; } +#endif + return sock; +} + + +//! Parses a URI string, into its IP Address, Port Number, and Path components +/*! +\b Note: The Address and Path components must be freed +\param URI The URI to parse +\param[out] Addr The Address component +\param Port The Port component. Default is 80 +\param[out] Path The Path component +\param[in,out] AddrStruct sockaddr_in6 structure holding the address and port [Can be NULL] +*/ +ILibParseUriResult ILibParseUri (const char* URI, char** Addr, unsigned short* Port, char** Path, struct sockaddr_in6* AddrStruct) +{ + struct parser_result *result, *result2, *result3; + char *TempString, *TempString2; + int TempStringLength, TempStringLength2; + unsigned short lport; + char* laddr = NULL; + + ILibParseUriResult retVal = ILibParseUriResult_UNKNOWN_SCHEME; + + // A scheme has the format xxx://yyy , so if we parse on ://, we can extract the path info + result = ILibParseString((char *)URI, 0, (int)strnlen_s(URI, sizeof(ILibScratchPad)), "://", 3); + + // Check the Scheme + switch (result->FirstResult->datalength) + { + case 2: + if (strncmp(result->FirstResult->data, "ws", 2) == 0 || strncmp(result->FirstResult->data, "WS", 2) == 0) { retVal = ILibParseUriResult_NO_TLS; } + break; + case 3: + if (strncmp(result->FirstResult->data, "wss", 3) == 0 || strncmp(result->FirstResult->data, "WSS", 3) == 0) { retVal = ILibParseUriResult_TLS; } + break; + case 4: + if (strncmp(result->FirstResult->data, "http", 4) == 0 || strncmp(result->FirstResult->data, "HTTP", 4) == 0) { retVal = ILibParseUriResult_NO_TLS; } + break; + case 5: + if (strncmp(result->FirstResult->data, "https", 5) == 0 || strncmp(result->FirstResult->data, "HTTPS", 5) == 0) { retVal = ILibParseUriResult_TLS; } + break; + default: + retVal = ILibParseUriResult_UNKNOWN_SCHEME; + break; + } + + TempString = result->LastResult->data; + TempStringLength = result->LastResult->datalength; + + // Parse Path. The first '/' will occur after the IPAddress:Port combination + result2 = ILibParseString(TempString, 0, TempStringLength,"/",1); + TempStringLength2 = TempStringLength-result2->FirstResult->datalength; + if (Path != NULL) + { + if ((*Path = (char*)malloc(TempStringLength2 + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(*Path, TempStringLength2 + 1, TempString + (result2->FirstResult->datalength), TempStringLength2); + (*Path)[TempStringLength2] = '\0'; + } + + // Parse Port Number + result3 = ILibParseString(result2->FirstResult->data, 0, result2->FirstResult->datalength, ":", 1); + if (result3->NumResults == 1) + { + // The default port if non is specified, assuming HTTP(s) or WS(s) + lport = (retVal == ILibParseUriResult_TLS) ? 443 : 80; + } + else + { + // If a port was specified, use that + if ((TempString2 = (char*)malloc(result3->LastResult->datalength + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(TempString2, result3->LastResult->datalength + 1, result3->LastResult->data, result3->LastResult->datalength); + TempString2[result3->LastResult->datalength] = '\0'; + lport = (unsigned short)atoi(TempString2); + free(TempString2); + } + + // Parse IP Address + if (result3->FirstResult->data[0] == '[') + { + // This is an IPv6 address + TempStringLength2 = ILibString_IndexOf(result2->FirstResult->data, result2->FirstResult->datalength, "]", 1); + if (TempStringLength2 > 0) + { + if ((laddr = (char*)malloc(TempStringLength2 + 2)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(laddr, TempStringLength2 + 2, result3->FirstResult->data, TempStringLength2 + 1); + (laddr)[TempStringLength2 + 1] = '\0'; + } + } + else + { + // This is an IPv4 address + TempStringLength2 = result3->FirstResult->datalength; + if ((laddr = (char*)malloc(TempStringLength2 + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(laddr, TempStringLength2 + 1, result3->FirstResult->data, TempStringLength2); + (laddr)[TempStringLength2] = '\0'; + } + + // Cleanup + ILibDestructParserResults(result3); + ILibDestructParserResults(result2); + ILibDestructParserResults(result); + + // Convert to sockaddr if needed + if (AddrStruct != NULL) + { + // Convert the string address into a sockaddr + memset(AddrStruct, 0, sizeof(struct sockaddr_in6)); + if (laddr != NULL && laddr[0] == '[') + { + // IPv6 + AddrStruct->sin6_family = AF_INET6; + ILibInet_pton(AF_INET6, laddr + 1, &(AddrStruct->sin6_addr)); + AddrStruct->sin6_port = (unsigned short)htons(lport); + } + else + { + // IPv4 + AddrStruct->sin6_family = AF_INET; + if (ILibInet_pton(AF_INET, laddr, &(((struct sockaddr_in*)AddrStruct)->sin_addr)) == 0) + { + // This was not an IP Address, but a DNS name + if (ILibResolveEx(laddr, lport, AddrStruct) != 0) + { + // Failed to resolve + AddrStruct->sin6_family = AF_UNSPEC; + } + } + ((struct sockaddr_in*)AddrStruct)->sin_port = (unsigned short)htons(lport); + } + } + + if (Port != NULL) *Port = lport; + if (Addr != NULL) *Addr = laddr; else if (laddr != NULL) free(laddr); + return(retVal); +} + +/*! \fn ILibCreateEmptyPacket() +\brief Creates an empty packetheader structure +\return An empty packet +*/ +struct packetheader *ILibCreateEmptyPacket() +{ + struct packetheader *RetVal; + if ((RetVal = (struct packetheader*)malloc(sizeof(struct packetheader))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct packetheader)); + + RetVal->UserAllocStrings = -1; + RetVal->StatusCode = -1; + RetVal->Version = "1.0"; + RetVal->VersionLength = 3; + RetVal->HeaderTable = ILibInitHashTree_CaseInSensitive(); + + return(RetVal); +} + +/*! \fn ILibClonePacket(struct packetheader *packet) +\brief Creates a Deep Copy of a packet structure +\par +Because ILibParsePacketHeader does not copy any data, the data will become invalid +once the data is flushed. This method is used to preserve the data. +\param packet The packet to clone +\return A cloned packet structure +*/ +struct packetheader* ILibClonePacket(struct packetheader *packet) +{ + struct packetheader *RetVal = ILibCreateEmptyPacket(); + struct packetheader_field_node *n; + + RetVal->ClonedPacket = 1; + + // Copy the addresses + memcpy_s(&(RetVal->ReceivingAddress), sizeof(RetVal->ReceivingAddress), &(packet->ReceivingAddress), INET_SOCKADDR_LENGTH(((struct sockaddr_in6*)(packet->ReceivingAddress))->sin6_family)); + memcpy_s(&(RetVal->Source), sizeof(RetVal->Source), &(packet->Source), INET_SOCKADDR_LENGTH(((struct sockaddr_in6*)(packet->ReceivingAddress))->sin6_family)); + + // These three calls will result in the fields being copied + ILibSetDirective( + RetVal, + packet->Directive, + packet->DirectiveLength, + packet->DirectiveObj, + packet->DirectiveObjLength); + + ILibSetStatusCode( + RetVal, + packet->StatusCode, + packet->StatusData, + packet->StatusDataLength); + + ILibSetVersion(RetVal,packet->Version,packet->VersionLength); + + // Iterate through each header, and copy them + n = packet->FirstField; + while (n!=NULL) + { + ILibAddHeaderLine( + RetVal, + n->Field, + n->FieldLength, + n->FieldData, + n->FieldDataLength); + n = n->NextField; + } + return(RetVal); +} + +/*! \fn ILibSetVersion(struct packetheader *packet, char* Version, int VersionLength) +\brief Sets the version of a packetheader structure. The Default version is 1.0 +\param packet The packet to modify +\param Version The version string to write. eg: 1.1 +\param VersionLength The length of the \a Version +*/ +void ILibSetVersion(struct packetheader *packet, char* Version, int VersionLength) +{ + if (packet->UserAllocVersion!=0) {free(packet->Version);} + packet->UserAllocVersion = 1; + if ((packet->Version = (char*)malloc(1+VersionLength)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->Version, 1 + VersionLength, Version, VersionLength); + packet->Version[VersionLength] = '\0'; +} + +/*! \fn ILibSetStatusCode(struct packetheader *packet, int StatusCode, char *StatusData, int StatusDataLength) +\brief Sets the status code of a packetheader structure +\param packet The packet to modify +\param StatusCode The status code, eg: 200 +\param StatusData The status string, eg: OK +\param StatusDataLength The length of \a StatusData +*/ +void ILibSetStatusCode(struct packetheader *packet, int StatusCode, char *StatusData, int StatusDataLength) +{ + if (StatusDataLength < 0) { StatusDataLength = (int)strnlen_s(StatusData, 255); } + packet->StatusCode = StatusCode; + if (packet->StatusData != NULL) { free(packet->StatusData); } + if ((packet->StatusData = (char*)malloc(StatusDataLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->StatusData, StatusDataLength + 1, StatusData, StatusDataLength); + packet->StatusData[StatusDataLength] = '\0'; + packet->StatusDataLength = StatusDataLength; +} + +/*! \fn ILibSetDirective(struct packetheader *packet, char* Directive, int DirectiveLength, char* DirectiveObj, int DirectiveObjLength) +\brief Sets the /a Method and /a Path of a packetheader structure +\param packet The packet to modify +\param Directive The Method to write, eg: \b GET +\param DirectiveLength The length of \a Directive +\param DirectiveObj The path component of the method, eg: \b /index.html +\param DirectiveObjLength The length of \a DirectiveObj +*/ +void ILibSetDirective(struct packetheader *packet, char* Directive, int DirectiveLength, char* DirectiveObj, int DirectiveObjLength) +{ + if (DirectiveLength < 0)DirectiveLength = (int)strnlen_s(Directive, 255); + if (DirectiveObjLength < 0)DirectiveObjLength = (int)strnlen_s(DirectiveObj, 255); + + if ((packet->Directive = (char*)malloc(DirectiveLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->Directive, DirectiveLength + 1, Directive,DirectiveLength); + packet->Directive[DirectiveLength] = '\0'; + packet->DirectiveLength = DirectiveLength; + + if ((packet->DirectiveObj = (char*)malloc(DirectiveObjLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(packet->DirectiveObj, DirectiveObjLength + 1, DirectiveObj, DirectiveObjLength); + packet->DirectiveObj[DirectiveObjLength] = '\0'; + packet->DirectiveObjLength = DirectiveObjLength; + packet->UserAllocStrings = -1; +} + +void ILibHTTPPacket_Stash_Put(ILibHTTPPacket *packet, char* key, int keyLen, void *data) +{ + if (keyLen < 0) { keyLen = (int)strnlen_s(key, 255); } + ILibAddEntryEx(packet->HeaderTable, key, keyLen, data, -1); +} +int ILibHTTPPacket_Stash_HasKey(ILibHTTPPacket *packet, char* key, int keyLen) +{ + return(ILibHasEntry(packet->HeaderTable, key, keyLen)); +} +void* ILibHTTPPacket_Stash_Get(ILibHTTPPacket *packet, char* key, int keyLen) +{ + if (keyLen < 0) { keyLen = (int)strnlen_s(key, 255); } + return(ILibGetEntry(packet->HeaderTable, key, keyLen)); +} + +/*! \fn void ILibDeleteHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength) +\brief Removes an HTTP header entry from a packetheader structure +\param packet The packet to modify +\param FieldName The header name, eg: \b CONTENT-TYPE +\param FieldNameLength The length of the \a FieldName +*/ +void ILibDeleteHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength) +{ + ILibDeleteEntry(packet->HeaderTable,FieldName,FieldNameLength); +} +/*! \fn ILibAddHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength, char* FieldData, int FieldDataLength) +\brief Adds an HTTP header entry into a packetheader structure +\param packet The packet to modify +\param FieldName The header name, eg: \b CONTENT-TYPE +\param FieldNameLength The length of the \a FieldName +\param FieldData The header value, eg: \b text/xml +\param FieldDataLength The length of the \a FieldData +*/ +void ILibAddHeaderLine(struct packetheader *packet, const char* FieldName, int FieldNameLength, const char* FieldData, int FieldDataLength) +{ + struct packetheader_field_node *node; + if (FieldNameLength < 0) { FieldNameLength = (int)strnlen_s(FieldName, 255); } + if (FieldDataLength < 0) { FieldDataLength = (int)strnlen_s(FieldData, 255); } + + // + // Create the Header Node + // + if ((node = (struct packetheader_field_node*)malloc(sizeof(struct packetheader_field_node))) == NULL) ILIBCRITICALEXIT(254); + node->UserAllocStrings = -1; + if ((node->Field = (char*)malloc(FieldNameLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->Field, FieldNameLength + 1, (char*)FieldName, FieldNameLength); + node->Field[FieldNameLength] = '\0'; + node->FieldLength = FieldNameLength; + + if ((node->FieldData = (char*)malloc(FieldDataLength+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(node->FieldData, FieldDataLength + 1, (char*)FieldData, FieldDataLength); + node->FieldData[FieldDataLength] = '\0'; + node->FieldDataLength = FieldDataLength; + + node->NextField = NULL; + + ILibAddEntryEx(packet->HeaderTable,node->Field,node->FieldLength,node->FieldData,node->FieldDataLength); + + // + // And attach it to the linked list + // + if (packet->LastField!=NULL) + { + packet->LastField->NextField = node; + packet->LastField = node; + } + else + { + packet->LastField = node; + packet->FirstField = node; + } +} + +char* ILibUrl_GetHost(char *url, int urlLen) +{ + int startX, endX; + char *retVal = NULL; + urlLen = urlLen >= 0 ? urlLen : (int)strnlen_s(url, sizeof(ILibScratchPad)); + + startX = ILibString_IndexOf(url, urlLen, "://", 3); + if (startX < 0) { startX = 0; } else { startX += 3; } + + endX = ILibString_IndexOf(url + startX, urlLen - startX, "/", 1); + retVal = url + startX; + retVal[endX > 0 ? endX : urlLen - startX] = 0; + + return(retVal); +} + +/*! \fn ILibGetHeaderLineEx(struct packetheader *packet, char* FieldName, int FieldNameLength, int *len) +\brief Retrieves an HTTP header value from a packet structure +\par +\param packet The packet to introspect +\param FieldName The header name to lookup +\param FieldNameLength The length of \a FieldName +\param len The length of the return value. Can be NULL +\return The header value. NULL if not found +*/ +char* ILibGetHeaderLineEx(struct packetheader *packet, char* FieldName, int FieldNameLength, int *len) +{ + void* RetVal = NULL; + int valLength; + + ILibGetEntryEx(packet->HeaderTable,FieldName,FieldNameLength,(void**)&RetVal,&valLength); + if (valLength!=0) + { + ((char*)RetVal)[valLength]=0; + } + if (len != NULL) { *len = valLength; } + return((char*)RetVal); +} +char* ILibGetHeaderLineSP_Next(char* PreviousValue, char* FieldName, int FieldNameLength) +{ + packetheader_field_node *header = (packetheader_field_node*)(PreviousValue - sizeof(void*)); + char *retVal = ILibScratchPad + sizeof(void*); + + while (header != NULL) + { + if (FieldNameLength == header->FieldLength && strncasecmp(FieldName, header->Field, FieldNameLength) == 0) + { + ((void**)ILibScratchPad)[0] = header->NextField; + memcpy_s(retVal, sizeof(ILibScratchPad) - sizeof(void*), header->FieldData, header->FieldDataLength); + retVal[header->FieldDataLength] = 0; + return(retVal); + } + header = header->NextField; + } + return(NULL); +} + +/*! \fn ILibGetHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength) +\brief Retrieves an HTTP header value from a packet structure, copied into the ScratchPad +\par +\param packet The packet to introspect +\param FieldName The header name to lookup +\param FieldNameLength The length of \a FieldName +\return The header value. NULL if not found +*/ +char* ILibGetHeaderLineSP(struct packetheader *packet, char* FieldName, int FieldNameLength) +{ + char* retVal = ILibScratchPad + sizeof(void*); + ((void**)ILibScratchPad)[0] = packet->FirstField;; + + return(ILibGetHeaderLineSP_Next(retVal, FieldName, FieldNameLength)); +} + +static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +/* encode 3 8-bit binary bytes as 4 '6-bit' characters */ +void ILibencodeblock( unsigned char in[3], unsigned char out[4], int len ) +{ + out[0] = cb64[ in[0] >> 2 ]; + out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ]; + out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '='); + out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '='); +} + +/*! \fn int ILibBase64EncodeLength(const int inputLen) +\brief Returns the length the ILibBase64Encode function would use a stream adding padding and line breaks as per spec. +\par +\param input The length of the en-encoded data +\return The length of the encoded stream +*/ +int ILibBase64EncodeLength(const int inputLen) +{ + return ((inputLen * 4) / 3) + 5; +} + +/*! \fn ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output) +\brief Base64 encode a stream adding padding and line breaks as per spec. +\par +\b Note: The encoded stream must be freed if **ouput is passed in as NULL +\param input The stream to encode +\param inputlen The length of \a input +\param[in,out] output The encoded stream. if *output is not NULL, then the base64 will be copied there, if it is NULL, then the function will malloc +\return The length of the encoded stream +*/ +int ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output) +{ + unsigned char* out; + unsigned char* in; + + if (*output == NULL) { if ((*output = (unsigned char*)malloc(((inputlen * 4) / 3) + 5)) == NULL) ILIBCRITICALEXIT(254); } + out = *output; + in = input; + + if (input == NULL || inputlen == 0) { *output = NULL; return 0; } + while ((in+3) <= (input+inputlen)) { ILibencodeblock(in, out, 3); in += 3; out += 4; } + if ((input+inputlen)-in == 1) { ILibencodeblock(in, out, 1); out += 4; } + else if ((input+inputlen)-in == 2) { ILibencodeblock(in, out, 2); out += 4; } + *out = 0; + + return (int)(out-*output); +} + +/* Decode 4 '6-bit' characters into 3 8-bit binary bytes */ +void ILibdecodeblock( unsigned char in[4], unsigned char out[3] ) +{ + out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4); + out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2); + out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]); +} + +/*! \fn int ILibBase64DecodeLength(const int inputLen) +\brief Returns the length the ILibBase64Decode function would use to store the decoded string value +\par +\param input The length of the en-encoded data +\return The length of the decoded stream +*/ +int ILibBase64DecodeLength(const int inputLen) +{ + return ((inputLen * 3) / 4) + 4; +} + +/*! \fn ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output) +\brief Decode a base64 encoded stream discarding padding, line breaks and noise +\par +\b Note: The encoded stream must be freed if **ouput is passed in as NULL +\param input The stream to decode +\param inputlen The length of \a input +\param[in,out] The encoded stream. if *output is not NULL, then the base64 will be copied there, if it is NULL, then the function will malloc +\return The length of the decoded stream +*/ +int ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output) +{ + unsigned char* inptr; + unsigned char* out; + unsigned char v; + unsigned char in[4]; + int i, len; + + if (input == NULL || inputlen == 0) + { + *output = NULL; + return 0; + } + if(*output==NULL) + if ((*output = (unsigned char*)malloc(((inputlen * 3) / 4) + 4)) == NULL) ILIBCRITICALEXIT(254); + out = *output; + inptr = input; + + memset(in, 0, 4); + while (inptr <= (input + inputlen)) + { + for(len = 0, i = 0; i < 4 && inptr <= (input + inputlen); i++) + { + v = 0; + while ( inptr <= (input+inputlen) && v == 0 ) { + v = (unsigned char) *inptr; + inptr++; + v = (unsigned char)((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]); + if (v) { + v = (unsigned char)((v == '$') ? 0 : v - 61); + } + } + if (inptr <= (input + inputlen)) { + len++; + if (v) { + in[i] = (unsigned char)(v - 1); + } + } + else { + in[i] = 0; + } + } + if (len) + { + ILibdecodeblock(in, out); + out += len - 1; + } + } + *out = 0; + return (int)(out-*output); +} + +/*! \fn ILibInPlaceXmlUnEscape(char* data) +\brief Unescapes a string according to XML parsing rules +\par +Since an escaped XML string is always larger than its unescaped form, this method +will overwrite the escaped string with the unescaped string, while decoding. +\param data The XML string to unescape +\return The length of the unescaped XML string +*/ +int ILibInPlaceXmlUnEscapeEx(char* data, size_t dataLen) +{ + char* end = data + dataLen; + char* i = data; /* src */ + char* j = data; /* dest */ + + // + // Iterate through the string to find escaped sequences + // + while (j < end) + { + if (j[0] == '&' && j[1] == 'q' && j[2] == 'u' && j[3] == 'o' && j[4] == 't' && j[5] == ';') // " + { + // Double Quote + i[0] = '"'; + j += 5; + } + else if (j[0] == '&' && j[1] == 'a' && j[2] == 'p' && j[3] == 'o' && j[4] == 's' && j[5] == ';') // ' + { + // Single Quote (apostrophe) + i[0] = '\''; + j += 5; + } + else if (j[0] == '&' && j[1] == 'a' && j[2] == 'm' && j[3] == 'p' && j[4] == ';') // & + { + // Ampersand + i[0] = '&'; + j += 4; + } + else if (j[0] == '&' && j[1] == 'l' && j[2] == 't' && j[3] == ';') // < + { + // Less Than + i[0] = '<'; + j += 3; + } + else if (j[0] == '&' && j[1] == 'g' && j[2] == 't' && j[3] == ';') // > + { + // Greater Than + i[0] = '>'; + j += 3; + } + else + { + i[0] = j[0]; + } + i++; + j++; + } + i[0] = '\0'; + return (int)(i - data); +} + +/*! \fn ILibXmlEscapeLength(const char* data) +\brief Calculates the minimum required buffer space, to escape an xml string +\par +\b Note: This calculation does not include space for a null terminator +\param data The XML string to calculate buffer requirments with +\return The minimum required buffer size +*/ +int ILibXmlEscapeLengthEx(const char* data, size_t dataLen) +{ + int i = 0, j = 0; + while (data[i] != 0 && i < (int)dataLen) + { + switch (data[i]) + { + case '"': + j += 6; + break; + case '\'': + j += 6; + break; + case '<': + j += 4; + break; + case '>': + j += 4; + break; + case '&': + j += 5; + break; + default: + j++; + } + i++; + } + return j; +} + + +/*! \fn ILibXmlEscape(char* outdata, const char* indata) +\brief Escapes a string according to XML parsing rules +\par +\b Note: \a outdata must be pre-allocated and freed +\param outdata The escaped XML string +\param indata The string to escape +\return The length of the escaped string +*/ +int ILibXmlEscapeEx(char* outdata, const char* indata, size_t indataLen) +{ + int i=0; + char* out; + + out = outdata; + + for (i=0; i < (int)indataLen; i++) + { + if (indata[i] == '"') + { + memcpy_s(out, 6, """, 6); + out = out + 6; + } + else + if (indata[i] == '\'') + { + memcpy_s(out, 6, "'", 6); + out = out + 6; + } + else + if (indata[i] == '<') + { + memcpy_s(out, 4, "<", 4); + out = out + 4; + } + else + if (indata[i] == '>') + { + memcpy_s(out, 4, ">", 4); + out = out + 4; + } + else + if (indata[i] == '&') + { + memcpy_s(out, 5, "&", 5); + out = out + 5; + } + else + { + out[0] = indata[i]; + out++; + } + } + + out[0] = 0; + + return (int)(out - outdata); +} + +// Return the number of milliseconds until trigger, -1 if not found. +long long ILibLifeTime_GetExpiration(void *LifetimeMonitorObject, void *data) +{ + void *node; + struct LifeTimeMonitorData *temp; + struct ILibLifeTime *LifeTimeMonitor = (struct ILibLifeTime*)LifetimeMonitorObject; + + node = ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList); + while (node != NULL) + { + temp = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (temp->data == data) return temp->ExpirationTick; + node = ILibLinkedList_GetNextNode(node); + } + return -1; +} + +/*! \fn ILibLifeTime_AddEx(void *LifetimeMonitorObject,void *data, int ms, void* Callback, void* Destroy) +\brief Registers a timed callback with millisecond granularity +\param LifetimeMonitorObject The \a ILibLifeTime object to add the timed callback to +\param data The data object to associate with the timed callback +\param ms The number of milliseconds for the timed callback +\param Callback The callback function pointer to trigger when the specified time elapses +\param Destroy The abort function pointer, which triggers all non-triggered timed callbacks, upon shutdown +*/ +void ILibLifeTime_AddEx(void *LifetimeMonitorObject,void *data, int ms, ILibLifeTime_OnCallback Callback, ILibLifeTime_OnCallback Destroy) +{ + struct LifeTimeMonitorData *temp; + struct LifeTimeMonitorData *ltms; + struct ILibLifeTime *LifeTimeMonitor = (struct ILibLifeTime*)LifetimeMonitorObject; + void *node; + + if ((ltms = (struct LifeTimeMonitorData*)malloc(sizeof(struct LifeTimeMonitorData))) == NULL) ILIBCRITICALEXIT(254); + memset(ltms,0,sizeof(struct LifeTimeMonitorData)); + + // + // Set the trigger time + // + ltms->data = data; + ltms->ExpirationTick = ms != 0 ? (ILibGetUptime() + (long long)(ms)) : 0; + + // + // Set the callback handlers + // + ltms->CallbackPtr = Callback; + ltms->DestroyPtr = Destroy; + + ILibLinkedList_Lock(LifeTimeMonitor->ObjectList); + + // Add the node to the list + node = ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList); + if (node == NULL) + { + ILibLinkedList_AddTail(LifeTimeMonitor->ObjectList, ltms); + ILibForceUnBlockChain(LifeTimeMonitor->ChainLink.ParentChain); + } + else + { + while (node != NULL) + { + temp = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (ltms->ExpirationTick < temp->ExpirationTick) + { + ILibLinkedList_InsertBefore(node, ltms); + break; + } + node = ILibLinkedList_GetNextNode(node); + } + if (node == NULL) + { + ILibLinkedList_AddTail(LifeTimeMonitor->ObjectList,ltms); + } + else if (ILibLinkedList_GetDataFromNode(ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList)) == ltms) + { + ILibForceUnBlockChain(LifeTimeMonitor->ChainLink.ParentChain); + } + } + + // If this notification is sooner than the existing one, replace it. + if (LifeTimeMonitor->NextTriggerTick > ltms->ExpirationTick) LifeTimeMonitor->NextTriggerTick = ltms->ExpirationTick; + + ILibLinkedList_UnLock(LifeTimeMonitor->ObjectList); +} + +// +// An internal method used by the ILibLifeTime methods +// +void ILibLifeTime_Check(void *LifeTimeMonitorObject, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + void *node; + int removed; + void *EventQueue; + long long CurrentTick; + struct ILibLinkedListNode_Root root; + struct LifeTimeMonitorData *EVT, *Temp = NULL; + struct ILibLifeTime *LifeTimeMonitor = (struct ILibLifeTime*)LifeTimeMonitorObject; + + UNREFERENCED_PARAMETER( readset ); + UNREFERENCED_PARAMETER( writeset ); + UNREFERENCED_PARAMETER( errorset ); + + + // + // Get the current tick count for reference + // + CurrentTick = ILibGetUptime(); + + // + // This will speed things up by skipping the timer check + // + if(LifeTimeMonitor->NextTriggerTick !=0 && ((LifeTimeMonitor->NextTriggerTick > CurrentTick) && (LifeTimeMonitor->NextTriggerTick != -1) && (*blocktime > (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick)))) + { + *blocktime = (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick); + return; + } + LifeTimeMonitor->NextTriggerTick = -1; + + // This is an optimization. We are going to create the root of this linked list on the stack instead of the heap. + // This also fixes a crash with malloc returns NULL if this is created in the heap - No idea why this occurs. + memset(&root, 0, sizeof(struct ILibLinkedListNode_Root)); + EventQueue = (void*)&root; + + ILibLinkedList_Lock(LifeTimeMonitor->Reserved); + ILibLinkedList_Lock(LifeTimeMonitor->ObjectList); + + while (ILibQueue_DeQueue(LifeTimeMonitor->Reserved) != NULL); + + node = ILibLinkedList_GetNode_Head(LifeTimeMonitor->ObjectList); + while (node != NULL) + { + Temp = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (Temp->ExpirationTick == 0 || Temp->ExpirationTick < CurrentTick) + { + ILibQueue_EnQueue(EventQueue, Temp); + node = ILibLinkedList_Remove(node); + } + else + { + if (LifeTimeMonitor->NextTriggerTick == -1 || Temp->ExpirationTick < LifeTimeMonitor->NextTriggerTick) LifeTimeMonitor->NextTriggerTick = Temp->ExpirationTick; // Save the next smallest value + node = ILibLinkedList_GetNextNode(node); + } + } + + ILibLinkedList_UnLock(LifeTimeMonitor->ObjectList); + ILibLinkedList_UnLock(LifeTimeMonitor->Reserved); + + // + // Iterate through all the triggers that we need to fire + // + node = ILibQueue_DeQueue(EventQueue); + while (node != NULL) + { + // + // Check to see if the item to be fired, is in the remove list. + // If it is, that means we shouldn't fire this item anymore. + // + ILibLinkedList_Lock(LifeTimeMonitor->Reserved); + removed = ILibLinkedList_Remove_ByData(LifeTimeMonitor->Reserved,node); + ILibLinkedList_UnLock(LifeTimeMonitor->Reserved); + + EVT = (struct LifeTimeMonitorData*)node; + if (removed == 0) + { + // Trigger the callback + EVT->CallbackPtr(EVT->data); + } + else + { + // + // This item was flagged for removal, so intead of triggering it, + // call the destroy pointer if it exists + // + if (EVT->DestroyPtr != NULL) { EVT->DestroyPtr(EVT->data); } + } + + free(EVT); + node = ILibQueue_DeQueue(EventQueue); + } + + // Compute how much time until next trigger + if (LifeTimeMonitor->NextTriggerTick != -1 && *blocktime > (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick)) + { + int delta = (int)(LifeTimeMonitor->NextTriggerTick - CurrentTick); + if (delta < 1000) *blocktime = 1000; else *blocktime = delta; + } +} + +/*! \fn ILibLifeTime_Remove(void *LifeTimeToken, void *data) +\brief Removes a timed callback from an \a ILibLifeTime module +\param LifeTimeToken The \a ILibLifeTime object to remove the callback from +\param data The data object to remove +*/ +void ILibLifeTime_Remove(void *LifeTimeToken, void *data) +{ + void *node; + int removed = 0; + struct LifeTimeMonitorData *evt; + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + void *EventQueue; + + if (UPnPLifeTime->ObjectList == NULL) return; + EventQueue = ILibQueue_Create(); + ILibLinkedList_Lock(UPnPLifeTime->ObjectList); + + node = ILibLinkedList_GetNode_Head(UPnPLifeTime->ObjectList); + if (node != NULL) + { + while (node != NULL) + { + evt = (struct LifeTimeMonitorData*)ILibLinkedList_GetDataFromNode(node); + if (evt->data == data) + { + ILibQueue_EnQueue(EventQueue, evt); + node = ILibLinkedList_Remove(node); + removed = 1; + } + else + { + node = ILibLinkedList_GetNextNode(node); + } + } + if (removed == 0) + { + // + // The item wasn't in the list, so maybe it is pending to be triggered + // + ILibLinkedList_Lock(UPnPLifeTime->Reserved); + ILibLinkedList_AddTail(UPnPLifeTime->Reserved, data); + ILibLinkedList_UnLock(UPnPLifeTime->Reserved); + } + } + ILibLinkedList_UnLock(UPnPLifeTime->ObjectList); + + // + // Iterate through each node that is to be removed + // + evt = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(EventQueue); + while (evt != NULL) + { + if (evt->DestroyPtr != NULL) {evt->DestroyPtr(evt->data);} + free(evt); + evt = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(EventQueue); + } + ILibQueue_Destroy(EventQueue); +} + +/*! \fn ILibLifeTime_Flush(void *LifeTimeToken) +\brief Flushes all timed callbacks from an ILibLifeTime module +\param LifeTimeToken The \a ILibLifeTime object to flush items from +*/ +void ILibLifeTime_Flush(void *LifeTimeToken) +{ + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + struct LifeTimeMonitorData *temp; + + ILibLinkedList_Lock(UPnPLifeTime->ObjectList); + + temp = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(UPnPLifeTime->ObjectList); + while (temp != NULL) + { + if (temp->DestroyPtr != NULL) temp->DestroyPtr(temp->data); + free(temp); + temp = (struct LifeTimeMonitorData*)ILibQueue_DeQueue(UPnPLifeTime->ObjectList); + } + ILibLinkedList_UnLock(UPnPLifeTime->ObjectList); +} + +// +// An internal method used by the ILibLifeTime methods +// +void ILibLifeTime_Destroy(void *LifeTimeToken) +{ + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + ILibLifeTime_Flush(LifeTimeToken); + ILibLinkedList_Destroy(UPnPLifeTime->ObjectList); + ILibQueue_Destroy(UPnPLifeTime->Reserved); + UPnPLifeTime->ObjectCount = 0; + UPnPLifeTime->ObjectList = NULL; +} + +/*! \fn ILibCreateLifeTime(void *Chain) +\brief Creates an empty ILibLifeTime container for Timed Callbacks. +\par +\b Note: All events are triggered on the MicroStack thread. Developers must \b NEVER block this thread! +\param Chain The chain to add the \a ILibLifeTime to +\return An \a ILibLifeTime token, which is used to add/remove callbacks +*/ +void *ILibCreateLifeTime(void *Chain) +{ + struct ILibLifeTime *RetVal; + if ((RetVal = (struct ILibLifeTime*)malloc(sizeof(struct ILibLifeTime))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct ILibLifeTime)); + + RetVal->ObjectList = ILibLinkedList_Create(); + RetVal->ChainLink.PreSelectHandler = &ILibLifeTime_Check; + RetVal->ChainLink.DestroyHandler = &ILibLifeTime_Destroy; + RetVal->ChainLink.ParentChain = Chain; + RetVal->Reserved = ILibQueue_Create(); + + ILibAddToChain(Chain, RetVal); + return((void*)RetVal); +} + + +long ILibLifeTime_Count(void* LifeTimeToken) +{ + struct ILibLifeTime *UPnPLifeTime = (struct ILibLifeTime*)LifeTimeToken; + return ILibQueue_GetCount(UPnPLifeTime->ObjectList); +} + +/*! \fn ILibFindEntryInTable(char *Entry, char **Table) +\brief Find the index in \a Table that contains \a Entry. +\param Entry The char* to find +\param Table Array of char*, where the last entry is NULL +\return the index into \a Table, that contains \a Entry +*/ +int ILibFindEntryInTable(char *Entry, char **Table) +{ + int i = 0; + + while (Table[i]!=NULL) + { + if (strcmp(Entry,Table[i])==0) return(i); + ++i; + } + + return(-1); +} + +void ILibLinkedList_SetData(void *LinkedList_Node, void *data) +{ + ((struct ILibLinkedListNode*)LinkedList_Node)->Data = data; +} +//! Use the given comparer to insert data into the list. +//! A Duplicate (according to comparer) will result in the chooser being dispatched to determine which value to keep +/*! + \param LinkedList ILibLinkedList to perform the insert operation on + \param comparer ILibLinkedList_Comparer handler to dispatch to perform the compare operation + \param chooser ILibLinkedList_Chooser handler to dispatch to determine which value to keep, if a duplicate entry is detected + \param data The data to insert + \param user Custom user data to pass along with dispatched calls + \return The LinkedList node that was inserted +*/ +void* ILibLinkedList_SortedInsertEx(void* LinkedList, ILibLinkedList_Comparer comparer, ILibLinkedList_Chooser chooser, void *data, void *user) +{ + void* node = ILibLinkedList_GetNode_Head(LinkedList); + if(node == NULL) + { + node = ILibLinkedList_AddHead(LinkedList, chooser(NULL, data, user)); + } + else + { + while(node != NULL) + { + if(comparer(ILibLinkedList_GetDataFromNode(node), data) == 0) + { + // Duplicate. Replace entry + ILibLinkedList_SetData(node, chooser(ILibLinkedList_GetDataFromNode(node), data, user)); + break; + } + else if(comparer(ILibLinkedList_GetDataFromNode(node), data) < 0) + { + node = ILibLinkedList_InsertBefore(node, chooser(NULL, data, user)); + break; + } + node = ILibLinkedList_GetNextNode(node); + } + if(node == NULL) + { + node = ILibLinkedList_AddTail(LinkedList, chooser(NULL, data, user)); + } + } + return(node); +} + +void* ILibLinkedList_SortedInsert_DefaultChooser(void *oldObject, void *newObject, void *user) +{ + *((void**)user) = oldObject; + return(newObject); +} + + +//! Use the given comparer to insert data into the list. +//! A Duplicate (according to comparer) will result in the value being updated, and the old value being returned +/*! + \param LinkedList ILibLinkedList to perform the insert operation on + \param comparer ILibLinkedList_Comparer handler to dispatch to perform the compare operation + \param data The data to insert + \return Duplicates are not allowed, so if the comparison resulted in a duplicate, the old value will be returned, and the new value overwritten +*/ +void* ILibLinkedList_SortedInsert(void* LinkedList, ILibLinkedList_Comparer comparer, void *data) +{ + void *retVal = NULL; + ILibLinkedList_SortedInsertEx(LinkedList, comparer, &ILibLinkedList_SortedInsert_DefaultChooser, data, &retVal); + return(retVal); +} +//! Use the given comparer to return the Linked List node that matches with the specified object +/*! + \param LinkedList The ILibLinkedList to search + \param comparer The ILibLinkedList_Comparer handler to dispatch to perform the comparison + \param matchWith object to search + \return First matching node [NULL if none found] +*/ +void* ILibLinkedList_GetNode_Search(void* LinkedList, ILibLinkedList_Comparer comparer, void *matchWith) +{ + void *retVal = NULL; + void *node = ILibLinkedList_GetNode_Head(LinkedList); + while(node != NULL) + { + if((comparer!=NULL && comparer(ILibLinkedList_GetDataFromNode(node), matchWith)==0) || (comparer == NULL && ILibLinkedList_GetDataFromNode(node) == matchWith)) + { + retVal = node; + break; + } + node = ILibLinkedList_GetNextNode(node); + } + return(retVal); +} + +/*! \fn ILibLinkedList_CreateEx(int userMemorySize) +\brief Create an empty Linked List Data Structure +\param userMemorySize Amount of memory to allocate for user data when allocating a linked list node +\return Empty Linked List +*/ +void* ILibLinkedList_CreateEx(int userMemorySize) +{ + struct ILibLinkedListNode_Root *root; + void *mem; + + ILibMemory_Allocate(sizeof(ILibLinkedListNode_Root), userMemorySize, (void**)&root, &mem); + root->ExtraMemory = mem; + + sem_init(&(root->LOCK), 0, 1); + return root; +} + + +void* ILibLinkedList_Create() +{ + return(ILibLinkedList_CreateEx(0)); +} +//! Associate custom user data with a linked list object +/*! + \param list ILibLinkedList to associate + \param tag Custom user data to associate +*/ +void ILibLinkedList_SetTag(ILibLinkedList list, void *tag) +{ + ((struct ILibLinkedListNode_Root*)list)->Tag = tag; +} +//! Fetch the associated custom user data from a linked list +/*! + \param list ILibLinkedList to query + \return Associated custom user data [NULL if none associated] +*/ +void* ILibLinkedList_GetTag(ILibLinkedList list) +{ + return(((struct ILibLinkedListNode_Root*)list)->Tag); +} + +void* ILibLinkedList_AllocateNode(void *LinkedList) +{ + void* newNode; + ILibMemory_Allocate(sizeof(ILibLinkedListNode), ILibMemory_GetExtraMemorySize(((ILibLinkedListNode_Root*)LinkedList)->ExtraMemory), &newNode, NULL); + return(newNode); +} + +void* ILibLinkedList_GetExtendedMemory(void* LinkedList_Node) +{ + if (LinkedList_Node == NULL) { return(NULL); } + return(ILibMemory_GetExtraMemory(LinkedList_Node, sizeof(ILibLinkedListNode))); +} +/*! \fn ILibLinkedList_ShallowCopy(void *LinkedList) +\brief Create a shallow copy of a linked list. That is, the structure is copied, but none of the data contents are copied. The pointer values are just copied. +\param LinkedList The linked list to copy +\return The copy of the supplied linked list +*/ +void* ILibLinkedList_ShallowCopy(void *LinkedList) +{ + void *RetVal = ILibLinkedList_Create(); + void *node = ILibLinkedList_GetNode_Head(LinkedList); + while (node != NULL) + { + ILibLinkedList_AddTail(RetVal, ILibLinkedList_GetDataFromNode(node)); + node = ILibLinkedList_GetNextNode(node); + } + return(RetVal); +} + +/*! \fn ILibLinkedList_GetNode_Head(void *LinkedList) +\brief Returns the Head node of a linked list data structure +\param LinkedList The linked list +\return The first node of the linked list +*/ +void* ILibLinkedList_GetNode_Head(void *LinkedList) +{ + return(((struct ILibLinkedListNode_Root*)LinkedList)->Head); +} + +/*! \fn ILibLinkedList_GetNode_Tail(void *LinkedList) +\brief Returns the Tail node of a linked list data structure +\param LinkedList The linked list +\return The last node of the linked list +*/ +void* ILibLinkedList_GetNode_Tail(void *LinkedList) +{ + return(((struct ILibLinkedListNode_Root*)LinkedList)->Tail); +} + +/*! \fn ILibLinkedList_GetNextNode(void *LinkedList_Node) +\brief Returns the next node, from the specified linked list node +\param LinkedList_Node The current linked list node +\return The next adjacent node of the current one +*/ +void* ILibLinkedList_GetNextNode(void *LinkedList_Node) +{ + return(((struct ILibLinkedListNode*)LinkedList_Node)->Next); +} + +/*! \fn ILibLinkedList_GetPreviousNode(void *LinkedList_Node) +\brief Returns the previous node, from the specified linked list node +\param LinkedList_Node The current linked list node +\return The previous adjacent node of the current one +*/ +void* ILibLinkedList_GetPreviousNode(void *LinkedList_Node) +{ + return(((struct ILibLinkedListNode*)LinkedList_Node)->Previous); +} + +/*! \fn ILibLinkedList_GetDataFromNode(void *LinkedList_Node) +\brief Returns the data pointed to by a linked list node +\param LinkedList_Node The current linked list node +\return The data pointer +*/ +void *ILibLinkedList_GetDataFromNode(void *LinkedList_Node) +{ + return(LinkedList_Node != NULL ? (((struct ILibLinkedListNode*)LinkedList_Node)->Data) : NULL); +} + +/*! \fn ILibLinkedList_InsertBefore(void *LinkedList_Node, void *data) +\brief Creates a new element, and inserts it before the given node +\param LinkedList_Node The linked list node +\param data The data pointer to be referenced +\return The LinkedList node that was inserted +*/ +void* ILibLinkedList_InsertBefore(void *LinkedList_Node, void *data) +{ + struct ILibLinkedListNode_Root *r = ((struct ILibLinkedListNode*)LinkedList_Node)->Root; + struct ILibLinkedListNode *n = (struct ILibLinkedListNode*) LinkedList_Node; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + + // + // Attach ourselved before the specified node + // + newNode->Next = n; + newNode->Previous = n->Previous; + if (newNode->Previous!=NULL) + { + newNode->Previous->Next = newNode; + } + n->Previous = newNode; + // + // If we are the first node, we need to adjust the head + // + if (r->Head==n) + { + r->Head = newNode; + } + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_InsertAfter(void *LinkedList_Node, void *data) +\brief Creates a new element, and appends it after the given node +\param LinkedList_Node The linked list node +\param data The data pointer to be referenced +\return The LinkedList Node that was inserted +*/ +void* ILibLinkedList_InsertAfter(void *LinkedList_Node, void *data) +{ + struct ILibLinkedListNode_Root *r = ((struct ILibLinkedListNode*)LinkedList_Node)->Root; + struct ILibLinkedListNode *n = (struct ILibLinkedListNode*) LinkedList_Node; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + + // + // Attach ourselved after the specified node + // + newNode->Next = n->Next; + n->Next = newNode; + newNode->Previous = n; + if (newNode->Next!=NULL) newNode->Next->Previous = newNode; + + // + // If we are the last node, we need to adjust the tail + // + if (r->Tail==n) r->Tail = newNode; + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_Remove(void *LinkedList_Node) +\brief Removes the given node from a linked list data structure +\param LinkedList_Node The linked list node to remove +\return The next node +*/ +void* ILibLinkedList_Remove(void *LinkedList_Node) +{ + struct ILibLinkedListNode_Root *r; + struct ILibLinkedListNode *n; + void* RetVal; + + if (LinkedList_Node == NULL) { return(NULL); } + r = ((struct ILibLinkedListNode*)LinkedList_Node)->Root; + n = (struct ILibLinkedListNode*) LinkedList_Node; + + RetVal = n->Next; + + if (n->Previous!=NULL) + { + n->Previous->Next = n->Next; + } + if (n->Next!=NULL) + { + n->Next->Previous = n->Previous; + } + if (r->Head==n) + { + r->Head = n->Next; + } + if (r->Tail==n) + { + if (n->Next==NULL) + { + r->Tail = n->Previous; + } + else + { + r->Tail = n->Next; + } + } + --r->count; + free(n); + return(RetVal); +} + +/*! \fn ILibLinkedList_Remove_ByData(void *LinkedList, void *data) +\brief Removes a node from the Linked list, via comparison +\par +Given a data pointer, will traverse the linked list data structure, deleting +elements that point to this data pointer. +\param LinkedList The linked list to traverse +\param data The data pointer to compare +\return Non-zero if an item was removed +*/ +int ILibLinkedList_Remove_ByData(void *LinkedList, void *data) +{ + void *node; + int RetVal=0; + + node = ILibLinkedList_GetNode_Head(LinkedList); + while (node!=NULL) + { + if (ILibLinkedList_GetDataFromNode(node)==data) + { + ++RetVal; + node = ILibLinkedList_Remove(node); + } + else + { + node = ILibLinkedList_GetNextNode(node); + } + } + return(RetVal); +} + +/*! \fn ILibLinkedList_AddHead(void *LinkedList, void *data) +\brief Creates a new element, and inserts it at the top of the linked list. +\param LinkedList The linked list +\param data The data pointer to reference +*/ +void* ILibLinkedList_AddHead(void *LinkedList, void *data) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + newNode->Previous = NULL; + + newNode->Next = r->Head; + if (r->Head!=NULL) r->Head->Previous = newNode; + r->Head = newNode; + if (r->Tail==NULL) r->Tail = newNode; + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_AddTail(void *LinkedList, void *data) +\brief Creates a new element, and appends it to the end of the linked list +\param LinkedList The linked list +\param data The data pointer to reference +*/ +void* ILibLinkedList_AddTail(void *LinkedList, void *data) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + struct ILibLinkedListNode *newNode; + + newNode = ILibLinkedList_AllocateNode(r); + newNode->Data = data; + newNode->Root = r; + newNode->Next = NULL; + + newNode->Previous = r->Tail; + if (r->Tail != NULL) r->Tail->Next = newNode; + r->Tail = newNode; + if (r->Head == NULL) r->Head = newNode; + ++r->count; + return(newNode); +} + +/*! \fn ILibLinkedList_Lock(void *LinkedList) +\brief Locks the linked list with a non-recursive lock +\param LinkedList The linked list +*/ +void ILibLinkedList_Lock(void *LinkedList) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + sem_wait(&(r->LOCK)); +} + +/*! \fn void ILibLinkedList_UnLock(void *LinkedList) +\brief Unlocks the linked list's non-recursive lock +\param LinkedList The linked list +*/ +void ILibLinkedList_UnLock(void *LinkedList) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + sem_post(&(r->LOCK)); +} + + +/*! \fn ILibLinkedList_Destroy(void *LinkedList) +\brief Frees the resources used by the linked list. +\par +\b Note: The data pointer referenced needs to be freed by the user if required +\param LinkedList The linked list +*/ +void ILibLinkedList_Destroy(void *LinkedList) +{ + struct ILibLinkedListNode_Root *r = (struct ILibLinkedListNode_Root*)LinkedList; + while (r->Head != NULL) ILibLinkedList_Remove(ILibLinkedList_GetNode_Head(LinkedList)); + sem_destroy(&(r->LOCK)); + free(r); +} + + +/*! \fn ILibLinkedList_GetCount(void *LinkedList) +\brief Returns the number of nodes in the linked list +\param LinkedList The linked list +\return Number of elements in the linked list +*/ +long ILibLinkedList_GetCount(void *LinkedList) +{ + return(((struct ILibLinkedListNode_Root*)LinkedList)->count); +} + +int ILibLinkedList_GetIndex(void *node) +{ + ILibLinkedListNode *current = (ILibLinkedListNode*)ILibLinkedList_GetNode_Head(((ILibLinkedListNode*)node)->Root); + int i = 0; + + while (current != NULL && current != node) + { + ++i; + current = current->Next; + } + return(current != NULL ? i : -1); +} + +typedef struct ILibHashtable_ClearCallbackStruct +{ + ILibHashtable source; + ILibHashtable_OnDestroy onClear; + void *user; +}ILibHashtable_ClearCallbackStruct; + +int ILibHashtable_DefaultBucketizer(int value) +{ + // Convert 4 bytes to 1 byte + unsigned char tmp[4]; + int retVal = 0; + + ((unsigned int*)tmp)[0] = value; + retVal = (int)(tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3]); //Klocwork is being retarded, and doesn't realize an unsigned int is 4 bytes + + return(retVal); +} +int ILibHashtable_DefaultHashFunc(void* Key1, char* Key2, int Key2Len) +{ + char tmp[4]; + int i; + int retVal = 0; + union{int i; void* p;}u; + + u.p = Key1; + if(Key1!=NULL) {retVal ^= u.i;} + if (Key2 != NULL) + { + if (Key2Len < 5) + { + ((int*)tmp)[0] = 0; + for (i = 0; i < Key2Len; ++i) + { + tmp[i] = Key2[i]; + } + retVal ^= ((int*)tmp)[0]; + } + + if (Key2Len > 4) + { + ((int*)tmp)[0] = 0; + for (i = 0; i < 4; ++i) + { + tmp[i] = Key2[Key2Len - 1 - i]; + } + retVal ^= ((int*)tmp)[0]; + + if (Key2Len > 12) + { + int x = Key2Len / 2; + ((int*)tmp)[0] = 0; + for (i = 0; i < 4; ++i) + { + tmp[i] = Key2[i + x]; + } + retVal ^= ((int*)tmp)[0]; + } + } + } + return(retVal & 0x7FFFFFFF); +} +//! Create an Advanced Hashtable using the default Hashing Function, and default SparseArray/Bucketizer +/*! + \return Hashtable +*/ +ILibHashtable ILibHashtable_Create() +{ + ILibHashtable_Root *retVal; + + if((retVal = (ILibHashtable_Root*)malloc(sizeof(ILibHashtable_Root))) == NULL) {ILIBCRITICALEXIT(254);} + memset(retVal, 0, sizeof(ILibHashtable_Root)); + + ILibHashtable_ChangeHashFunc(retVal, &ILibHashtable_DefaultHashFunc); + ILibHashtable_ChangeBucketizer(retVal, 256, &ILibHashtable_DefaultBucketizer); + + return(retVal); +} +void ILibHashtable_DestroyEx2(ILibSparseArray sender, int index, void *value, void *user) +{ + ILibHashtable_ClearCallbackStruct *state = (ILibHashtable_ClearCallbackStruct*)user; + UNREFERENCED_PARAMETER(sender); + UNREFERENCED_PARAMETER(index); + + if(value != NULL) + { + ILibHashtable_Node *tmp, *node = (ILibHashtable_Node*)value; + ILibHashtable_OnDestroy onDestroy = state->onClear; + while(node != NULL) + { + if(onDestroy!=NULL) {onDestroy(state->source, node->Key1, node->Key2, node->Key2Len, node->Data, state->user);} + tmp = node->next; + if(node->Key2 != NULL) { free(node->Key2); } + free(node); + node = tmp; + } + } +} +//! Free the resources associated with an Advanced Hashtable + +//! Dispatches event handler for each element that is destroyed/cleared +/*! + \param table Hashtable + \param onDestroy Event handler to dispatch for destroyed/cleared elements + \param user Custom User state data +*/ +void ILibHashtable_DestroyEx(ILibHashtable table, ILibHashtable_OnDestroy onDestroy, void *user) +{ + ILibHashtable_Root *root = (ILibHashtable_Root*)table; + ILibHashtable_ClearCallbackStruct state; + memset(&state, 0, sizeof(ILibHashtable_ClearCallbackStruct)); + + state.source = table; + state.onClear = onDestroy; + state.user = user; + + ILibSparseArray_DestroyEx(root->table, &ILibHashtable_DestroyEx2, &state); + free(root); +} + +void ILibHashtable_EnumerateSink(ILibSparseArray sender, int index, void *value, void *user) +{ + ILibHashtable_ClearCallbackStruct *state = (ILibHashtable_ClearCallbackStruct*)user; + UNREFERENCED_PARAMETER(sender); + UNREFERENCED_PARAMETER(index); + + if (value != NULL) + { + ILibHashtable_Node *node = (ILibHashtable_Node*)value; + ILibHashtable_OnDestroy onEnumerate = state->onClear; + while (node != NULL) + { + if (onEnumerate != NULL) { onEnumerate(state->source, node->Key1, node->Key2, node->Key2Len, node->Data, state->user); } + node = node->next; + } + } +} + +//! Enumerates the Hashtable +/*! + \param table Hashtable to enumerate + \param onEnumerate The callback to dispatch for each item + \param user User state object to pass thru to the callback +*/ +void ILibHashtable_Enumerate(ILibHashtable table, ILibHashtable_OnDestroy onEnumerate, void *user) +{ + ILibHashtable_ClearCallbackStruct state; + memset(&state, 0, sizeof(ILibHashtable_ClearCallbackStruct)); + + state.source = table; + state.onClear = onEnumerate; + state.user = user; + + ILibSparseArray_Enumerate(((ILibHashtable_Root*)table)->table, ILibHashtable_EnumerateSink, &state); +} + +//! Clear the Hashtable, with a callback for each removed item +/*! + \param table Hashtable to clear + \param onClear The callback to dispatch for each item removed + \param user User state object to pass thru to the callback +*/ +void ILibHashtable_ClearEx(ILibHashtable table, ILibHashtable_OnDestroy onClear, void *user) +{ + ILibHashtable_ClearCallbackStruct state; + memset(&state, 0, sizeof(ILibHashtable_ClearCallbackStruct)); + + state.source = table; + state.onClear = onClear; + state.user = user; + ILibSparseArray_ClearEx(((ILibHashtable_Root*)table)->table, ILibHashtable_DestroyEx2, &state); +} +//! Clear the Hashtable +/*! + \param table Hashtable to clear +*/ +void ILibHashtable_Clear(ILibHashtable table) +{ + ILibSparseArray_ClearEx(((ILibHashtable_Root*)table)->table, NULL, NULL); +} +//! Change the hashing function used by the specified hashtable +/*! + \param table Hashtable to modify + \param hashFunc Handler for the hashing function to use +*/ +void ILibHashtable_ChangeHashFunc(ILibHashtable table, ILibHashtable_Hash_Func hashFunc) +{ + ((ILibHashtable_Root*)table)->hashFunc = hashFunc; +} +//! Change the bucketizer function used by the underlying Sparse Array for the specified Hashtable +/*! + \param table Hashtable to modify + \param bucketizer Handler for the bucketizer function to use +*/ +void ILibHashtable_ChangeBucketizer(ILibHashtable table, int bucketCount, ILibSparseArray_Bucketizer bucketizer) +{ + if(((ILibHashtable_Root*)table)->table != NULL) {ILibSparseArray_Destroy(((ILibHashtable_Root*)table)->table);} + ((ILibHashtable_Root*)table)->table = ILibSparseArray_Create(bucketCount, bucketizer); +} +ILibHashtable_Node* ILibHashtable_CreateNode(void* Key1, char* Key2, int Key2Len, void* Data) +{ + ILibHashtable_Node *node; + + if((node = (ILibHashtable_Node*)malloc(sizeof(ILibHashtable_Node))) == NULL) {ILIBCRITICALEXIT(254);} + memset(node, 0, sizeof(ILibHashtable_Node)); + node->Data = Data; + node->Key1 = Key1; + node->Key2Len = Key2Len; + if(Key2Len > 0) + { + if((node->Key2 = (char*)malloc(Key2Len))==NULL) {ILIBCRITICALEXIT(254);} + memcpy_s(node->Key2, Key2Len, Key2, Key2Len); + } + return(node); +} + +ILibHashtable_Node* ILibHashtable_GetEx(ILibHashtable table, void *Key1, char* Key2, int Key2Len, ILibHashtable_Flags flags) +{ + ILibHashtable_Node *retVal = NULL; + ILibHashtable_Root* root = (ILibHashtable_Root*)table; + int hash = root->hashFunc(Key1, Key2, Key2Len); + ILibHashtable_Node *node = (ILibHashtable_Node*)ILibSparseArray_Get(root->table, hash); + + if(node == NULL) + { + // Entry doesn't exist, so create a new entry + if((flags & ILibHashtable_Flags_ADD) == ILibHashtable_Flags_ADD) + { + retVal = ILibHashtable_CreateNode(Key1, Key2, Key2Len, NULL); + ILibSparseArray_Add(root->table, hash, retVal); + } + } + else + { + // There is a hash entry, so lets enumerate to see if we really have a match + ILibHashtable_Node *prev = NULL; + while(node!=NULL) + { + if(node->Key1 == Key1 && node->Key2Len == Key2Len && memcmp(node->Key2, Key2, Key2Len)==0) + { + break; + } + prev = node; + node = node->next; + } + if(node == NULL) + { + // There were no matches in the hashes that were returned + + if((flags & ILibHashtable_Flags_ADD) == ILibHashtable_Flags_ADD) + { + // Create a new entry, and append ourself to the hash results + retVal = ILibHashtable_CreateNode(Key1, Key2, Key2Len, NULL); + prev->next = retVal; + retVal->prev = prev; + } + } + else + { + // There was a match! Update the value and return the old value + retVal = node; + if((flags & ILibHashtable_Flags_REMOVE) == ILibHashtable_Flags_REMOVE) + { + if(node->prev == NULL) + { + // This is the first entry, so we'll have to remove the entry from the SparseArray + ILibSparseArray_Remove(root->table, hash); + } + else + { + node->prev->next = node->next; + if(node->next != NULL) {node->next->prev = node->prev;} + } + } + } + } + return(retVal); +} +//! Add/Modify an entry in the Hashtable + +//! Key1 and Key2 can both be NULL, but not at the same time. +/*! + \param table Hashtable to perform the operation on + \param Key1 Address Key [Can be NULL] + \param Key2 String Key [Can be NULL] + \param Key2Len String Key LEngth + \param Data New Element Value + \return Old Element Value [NULL if it didn't exist] +*/ +void* ILibHashtable_Put(ILibHashtable table, void *Key1, char* Key2, int Key2Len, void* Data) +{ + ILibHashtable_Node *node = ILibHashtable_GetEx(table, Key1, Key2, Key2Len, ILibHashtable_Flags_ADD); + void *retVal = node->Data; + node->Data = Data; + return(retVal); +} +//! Get the specified Element Value associated with the specified Key(s). + +//! Key1 and Key2 can both be NULL, but not at the same time. +/*! + \param table Hashtable to fetch the value from + \param Key1 Address Key [Can be NULL] + \param Key2 String Key [Can be NULL] + \param Key2Len String Key Length + \return Element Value [NULL if it doesn't exist] +*/ +void* ILibHashtable_Get(ILibHashtable table, void *Key1, char* Key2, int Key2Len) +{ + ILibHashtable_Node *node = ILibHashtable_GetEx(table, Key1, Key2, Key2Len, ILibHashtable_Flags_NONE); + return(node != NULL ? node->Data : NULL); +} +//! Remove an entry from the hashtable + +//! Both Key1 and Key can be NULL, but not at the same time +/*! + \param table Hashtable to remove the entry from + \param Key1 Address Key [Can be NULL] + \param Key2 String Key [Can be NULL] + \param Key2Len String Key Length + \return Element Value that was removed [NULL if it didn't exist] +*/ +void* ILibHashtable_Remove(ILibHashtable table, void *Key1, char* Key2, int Key2Len) +{ + ILibHashtable_Node *node = ILibHashtable_GetEx(table, Key1, Key2, Key2Len, ILibHashtable_Flags_REMOVE); + void *retVal = node != NULL ? node->Data : NULL; + if(node != NULL) + { + if(node->Key2 != NULL) {free(node->Key2);} + free(node); + } + return(retVal); +} +//! Use the specified hashtable as a synchronization lock, and acquire it +/*! + \param table Hashtable to use as a synchronization lock +*/ +void ILibHashtable_Lock(ILibHashtable table) +{ + ILibSparseArray_Lock(((ILibHashtable_Root*)table)->table); +} +//! Use the specified hashtable as a synchronization lock, and release it +/*! + \param table Hashtable to use as a synchronization lock +*/ +void ILibHashtable_UnLock(ILibHashtable table) +{ + ILibSparseArray_UnLock(((ILibHashtable_Root*)table)->table); +} + + +//! Allocates a new SparseArray using the specified number of buckets and the given bucketizer method, which maps indexes to buckets. +/*! + \param numberOfBuckets Number of buckets to initialize + \param bucketizer Event handler to be triggered whenever an index value needs to be hashed into a bucket value + \param userMemorySize Size of extra memory to allocate for user state + \return ILibSparseArray object +*/ +ILibSparseArray ILibSparseArray_CreateWithUserMemory(int numberOfBuckets, ILibSparseArray_Bucketizer bucketizer, int userMemorySize) +{ + ILibSparseArray_Root *retVal = (ILibSparseArray_Root*)ILibMemory_Allocate(sizeof(ILibSparseArray_Root), userMemorySize, NULL, NULL); + + sem_init(&(retVal->LOCK), 0, 1); + retVal->bucketSize = numberOfBuckets; + retVal->bucketizer = bucketizer; + retVal->bucket = (ILibSparseArray_Node*)malloc(numberOfBuckets * sizeof(ILibSparseArray_Node)); + retVal->userMemorySize = userMemorySize; + memset(retVal->bucket, 0, numberOfBuckets * sizeof(ILibSparseArray_Node)); + + return(retVal); +} + +//! Allocates a new SparseArray using the same parameters as an existing SparseArray +/*! + \param source The ILibSparseArray to duplicate parameters from + \return New ILibSparseArray object +*/ +ILibSparseArray ILibSparseArray_CreateEx(ILibSparseArray source) +{ + ILibSparseArray retVal = ILibSparseArray_CreateWithUserMemory(((ILibSparseArray_Root*)source)->bucketSize, ((ILibSparseArray_Root*)source)->bucketizer, ((ILibSparseArray_Root*)source)->userMemorySize); + return(retVal); +} + + +int ILibSparseArray_Comparer(void *obj1, void *obj2) +{ + if(((ILibSparseArray_Node*)obj1)->index == ((ILibSparseArray_Node*)obj2)->index) + { + return(0); + } + else + { + return(((ILibSparseArray_Node*)obj1)->index < ((ILibSparseArray_Node*)obj2)->index ? -1 : 1); + } +} +//! Populates the "index" in the SparseArray. If that index is already defined, the new value is saved, and the old value is returned. +/*! + \param sarray Sparse Array Object + \param index Index to initialize/set + \param data Value to set to the index + \return Old value of index if it was initialized, NULL otherwise +*/ +void* ILibSparseArray_Add(ILibSparseArray sarray, int index, void *data) +{ + void* retVal = NULL; + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + int i = root->bucketizer(index); + + if(root->bucket[i].index == 0 && root->bucket[i].ptr == NULL) + { + // No Entry Exists + root->bucket[i].index = index; + root->bucket[i].ptr = data; + } + else if(root->bucket[i].index < 0) + { + // Need to use Linked List + ILibSparseArray_Node* n = (ILibSparseArray_Node*)malloc(sizeof(ILibSparseArray_Node)); + if (n == NULL) ILIBCRITICALEXIT(254); + n->index = index; + n->ptr = data; + n = (ILibSparseArray_Node*)ILibLinkedList_SortedInsert(root->bucket[i].ptr, &ILibSparseArray_Comparer, n); + if(n!=NULL) + { + // This duplicates an entry already in the list... Updated with new value, pass back the old + retVal = n->ptr; + free(n); + } + } + else + { + // No Linked List... We either need to create one, or check to see if this is possibly a duplicate entry + if(root->bucket[i].index == index) + { + // Today's our lucky day! We can just replace the value! (No return value) + retVal = root->bucket[i].ptr; + root->bucket[i].ptr = data; + } + else + { + // We need to create a linked list, add the old value, then insert our new value (No return value) + ILibSparseArray_Node* n = (ILibSparseArray_Node*)malloc(sizeof(ILibSparseArray_Node)); + if (n == NULL) ILIBCRITICALEXIT(254); + n->index = root->bucket[i].index; + n->ptr = root->bucket[i].ptr; + + root->bucket[i].index = -1; + root->bucket[i].ptr = ILibLinkedList_Create(); + ILibLinkedList_AddHead(root->bucket[i].ptr, n); + + n = (ILibSparseArray_Node*)malloc(sizeof(ILibSparseArray_Node)); + if (n == NULL) ILIBCRITICALEXIT(254); + n->index = index; + n->ptr = data; + ILibLinkedList_SortedInsert(root->bucket[i].ptr, &ILibSparseArray_Comparer, n); + } + } + return(retVal); +} + +void* ILibSparseArray_GetEx(ILibSparseArray sarray, int index, int remove) +{ + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + void *retVal = NULL; + int i = root->bucketizer(index); + + if(root->bucket[i].index == index) + { + // Direct Match + retVal = root->bucket[i].ptr; + if(remove!=0) + { + root->bucket[i].ptr = NULL; + root->bucket[i].index = 0; + } + } + else if(root->bucket[i].index < 0) + { + // Need to check the Linked List + void *listNode = ILibLinkedList_GetNode_Search(root->bucket[i].ptr, &ILibSparseArray_Comparer, (void*)&index); + retVal = listNode != NULL ? ((ILibSparseArray_Node*)ILibLinkedList_GetDataFromNode(listNode))->ptr : NULL; + if(remove!=0 && listNode!=NULL) + { + free(ILibLinkedList_GetDataFromNode(listNode)); + ILibLinkedList_Remove(listNode); + if(ILibLinkedList_GetCount(root->bucket[i].ptr)==0) + { + ILibLinkedList_Destroy(root->bucket[i].ptr); + root->bucket[i].ptr = NULL; + root->bucket[i].index = 0; + } + } + } + else + { + // Whatever is in this bucket doesn't match what we're looking for + retVal = NULL; + } + return(retVal); +} +//! Fetches the value at the index. NULL if the index is not defined +/*! + \param sarray Sparse Array Object + \param index Index to fetch + \return Value at specified index, NULL if it was not defined +*/ +void* ILibSparseArray_Get(ILibSparseArray sarray, int index) +{ + return(ILibSparseArray_GetEx(sarray, index, 0)); +} +//! Removes an index from the SparseArray (if it exists), and returns the associated value if it does exist. +/*! + \param sarray Sparse Array Object + \param index Index value to remove + \return Value defined at Index, NULL if it was not defined +*/ +void* ILibSparseArray_Remove(ILibSparseArray sarray, int index) +{ + return(ILibSparseArray_GetEx(sarray, index, 1)); +} +//! Clones the contents of the given Sparse Array into a new Sparse Array +/*! + \param sarray The Sparse Array to clone + \return Cloned Sparse Array +*/ +ILibSparseArray ILibSparseArray_Move(ILibSparseArray sarray) +{ + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + ILibSparseArray_Root *retVal = (ILibSparseArray_Root*)ILibSparseArray_CreateEx(sarray); + memcpy_s(retVal->bucket, root->bucketSize * sizeof(ILibSparseArray_Node), root->bucket, root->bucketSize * sizeof(ILibSparseArray_Node)); + return(retVal); +} +//! Clears the SparseArray, and dispatches an event for each defined index +/*! + \param sarray Sparse Array Object + \param onClear Event to dispatch for defined indexes + \param user Custom user state data + \param nonZeroWillDelete If this is zero, will just enumerato, otherwise will delete +*/ +void ILibSparseArray_ClearEx2(ILibSparseArray sarray, ILibSparseArray_OnValue onClear, void *user, int nonZeroWillDelete) +{ + ILibSparseArray_Root *root = (ILibSparseArray_Root*)sarray; + int i; + + for(i=0;ibucketSize;++i) + { + if(root->bucket[i].ptr != NULL) + { + if(root->bucket[i].index < 0) + { + // There is a linked list here + void *node = ILibLinkedList_GetNode_Head(root->bucket[i].ptr); + while(node != NULL) + { + ILibSparseArray_Node *sn = (ILibSparseArray_Node*)ILibLinkedList_GetDataFromNode(node); + if(onClear!=NULL) { onClear(sarray, sn->index, sn->ptr, user);} + if (nonZeroWillDelete != 0) { free(sn); } + node = ILibLinkedList_GetNextNode(node); + } + if (nonZeroWillDelete != 0) { ILibLinkedList_Destroy(root->bucket[i].ptr); } + } + else + { + // No Linked List, just a direct map + if(onClear!=NULL) + { + onClear(sarray, root->bucket[i].index, root->bucket[i].ptr, user); + } + } + if (nonZeroWillDelete != 0) + { + root->bucket[i].index = 0; + root->bucket[i].ptr = NULL; + } + } + } +} +//! Frees the resources used by an ILibSparseArray, and dispatches an event for each defined index +/*! + \param sarray Sparse Array Object + \param onDestroy Event handler to dispatch for each defined entry + \param user Custom user state data +*/ +void ILibSparseArray_DestroyEx(ILibSparseArray sarray, ILibSparseArray_OnValue onDestroy, void *user) +{ + ILibSparseArray_ClearEx(sarray, onDestroy, user); + sem_destroy(&((ILibSparseArray_Root*)sarray)->LOCK); + free(((ILibSparseArray_Root*)sarray)->bucket); + free(sarray); +} +//! Frees the resources used by an ILibSparseArray +/*! + \param sarray Sparse Array Object to free +*/ +void ILibSparseArray_Destroy(ILibSparseArray sarray) +{ + ILibSparseArray_DestroyEx(sarray, NULL, NULL); +} +//! Use the Sparse Array as a synchronization lock, and acquire it +/*! + \param sarray Sparse Array to use as a synchronization lock +*/ +void ILibSparseArray_Lock(ILibSparseArray sarray) +{ + sem_wait(&(((ILibSparseArray_Root*)sarray)->LOCK)); +} +//! Use the Sparse Array as a synchronization lock, and release it +/*! + \param sarray Sparse Array to use as a synchronization lock +*/ +void ILibSparseArray_UnLock(ILibSparseArray sarray) +{ + sem_post(&(((ILibSparseArray_Root*)sarray)->LOCK)); +} + +int ILibString_IndexOfFirstWhiteSpace(const char *inString, int inStringLength) +{ + //CR, LF, space, tab + int i = 0; + for(i = 0;i < inStringLength; ++i) + { + if (inString[i] == 13 || inString[i] == 10 || inString[i] == 9 || inString[i] == 32) return(i); + } + return(-1); +} + +/*! \fn ILibString_EndsWithEx(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength, int caseSensitive) +\brief Determines if a string ends with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param endWithString The substring to match +\param endWithStringLength The length of \a startsWithString +\param caseSensitive 0 if the matching is case-insensitive +\return Non-zero if the string starts with the substring +*/ +int ILibString_EndsWithEx(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength, int caseSensitive) +{ + int RetVal = 0; + if (inStringLength < 0) { inStringLength = (int)strnlen_s(inString, sizeof(ILibScratchPad)); } + if (endWithStringLength < 0) { endWithStringLength = (int)strnlen_s(endWithString, sizeof(ILibScratchPad)); } + + if (inStringLength>=endWithStringLength) + { + if (caseSensitive!=0 && memcmp(inString+inStringLength-endWithStringLength,endWithString,endWithStringLength)==0) RetVal = 1; + else if (caseSensitive==0 && strncasecmp(inString+inStringLength-endWithStringLength,endWithString,endWithStringLength)==0) RetVal = 1; + } + return(RetVal); +} +/*! \fn ILibString_EndsWith(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength) +\brief Determines if a string ends with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param endWithString The substring to match +\param endWithStringLength The length of \a startsWithString +\return Non-zero if the string starts with the substring +*/ +int ILibString_EndsWith(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength) +{ + return(ILibString_EndsWithEx(inString,inStringLength,endWithString,endWithStringLength,1)); +} +/*! \fn ILibString_StartsWithEx(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength, int caseSensitive) +\brief Determines if a string starts with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param startsWithString The substring to match +\param startsWithStringLength The length of \a startsWithString +\param caseSensitive Non-zero if match is to be case sensitive +\return Non-zero if the string starts with the substring +*/ +int ILibString_StartsWithEx(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength, int caseSensitive) +{ + int RetVal = 0; + if (inStringLength>=startsWithStringLength) + { + if (caseSensitive!=0 && memcmp(inString,startsWithString,startsWithStringLength)==0) RetVal = 1; + else if (caseSensitive==0 && strncasecmp(inString,startsWithString,startsWithStringLength)==0) RetVal = 1; + } + return(RetVal); +} +/*! \fn ILibString_StartsWith(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength) +\brief Determines if a string starts with a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param startsWithString The substring to match +\param startsWithStringLength The length of \a startsWithString +\return Non-zero if the string starts with the substring +*/ +int ILibString_StartsWith(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength) +{ + return(ILibString_StartsWithEx(inString,inStringLength,startsWithString,startsWithStringLength,1)); +} +/*! \fn ILibString_IndexOfEx(const char *inString, int inStringLength, const char *indexOf, int indexOfLength, int caseSensitive) +\brief Returns the position index of the first occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param indexOf The substring to search for +\param indexOfLength The length of \a lastIndexOf +\param caseSensitive Non-zero if the match is case sensitive +\return Position index of first occurance. -1 if the substring is not found +*/ +int ILibString_IndexOfEx(const char *inString, int inStringLength, const char *indexOf, int indexOfLength, int caseSensitive) +{ + int RetVal = -1; + int index = 0; + + while (inStringLength-index >= indexOfLength) + { + if (caseSensitive!=0 && memcmp(inString+index,indexOf,indexOfLength)==0) + { + RetVal = index; + break; + } + else if (caseSensitive==0 && strncasecmp(inString+index,indexOf,indexOfLength)==0) + { + RetVal = index; + break; + } + ++index; + } + return(RetVal); +} +/*! \fn ILibString_IndexOf(const char *inString, int inStringLength, const char *indexOf, int indexOfLength) +\brief Returns the position index of the first occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param indexOf The substring to search for +\param indexOfLength The length of \a lastIndexOf +\return Position index of first occurance. -1 if the substring is not found +*/ +int ILibString_IndexOf(const char *inString, int inStringLength, const char *indexOf, int indexOfLength) +{ + return(ILibString_IndexOfEx(inString,inStringLength,indexOf,indexOfLength,1)); +} +/*! \fn ILibString_LastIndexOfEx(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength, int caseSensitive) +\brief Returns the position index of the last occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param lastIndexOf The substring to search for +\param lastIndexOfLength The length of \a lastIndexOf +\param caseSensitive 0 for case insensitive matching, non-zero for case-sensitive matching +\return Position index of last occurance. -1 if the substring is not found +*/ +int ILibString_LastIndexOfEx(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength, int caseSensitive) +{ + int RetVal = -1; + int index = (inStringLength < 0 ? (int)strnlen_s(inString, sizeof(ILibScratchPad)) : inStringLength) - (lastIndexOfLength < 0 ? (int)strnlen_s(lastIndexOf, sizeof(ILibScratchPad)) : lastIndexOfLength); + + while (index >= 0) + { + if (caseSensitive!=0 && memcmp(inString+index,lastIndexOf,lastIndexOfLength)==0) + { + RetVal = index; + break; + } + else if (caseSensitive==0 && strncasecmp(inString+index,lastIndexOf,lastIndexOfLength)==0) + { + RetVal = index; + break; + } + --index; + } + return(RetVal); +} +/*! \fn ILibString_LastIndexOf(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength) +\brief Returns the position index of the last occurance of a given substring +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param lastIndexOf The substring to search for +\param lastIndexOfLength The length of \a lastIndexOf +\return Position index of last occurance. -1 if the substring is not found +*/ +int ILibString_LastIndexOf(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength) +{ + return(ILibString_LastIndexOfEx(inString,inStringLength,lastIndexOf,lastIndexOfLength,1)); +} +/*! \fn ILibString_Replace(const char *inString, int inStringLength, const char *replaceThis, int replaceThisLength, const char *replaceWithThis, int replaceWithThisLength) +\brief Replaces all occurances of a given substring that occur in the input string with another substring +\par +\b Note: \a Returned string must be freed +\param inString Pointer to char* to process +\param inStringLength The length of \a inString +\param replaceThis The substring to replace +\param replaceThisLength The length of \a replaceThis +\param replaceWithThis The substring to substitute for \a replaceThis +\param replaceWithThisLength The length of \a replaceWithThis +\return New string with replaced values +*/ +char *ILibString_Replace(const char *inString, int inStringLength, const char *replaceThis, int replaceThisLength, const char *replaceWithThis, int replaceWithThisLength) +{ + char *RetVal; + int RetValLength; + struct parser_result *pr; + struct parser_result_field *prf; + int mallocSize; + + pr = ILibParseString((char*)inString, 0, inStringLength,(char*)replaceThis,replaceThisLength); + RetValLength = (pr->NumResults-1) * replaceWithThisLength; // string that will be inserted + RetValLength += (inStringLength - ((pr->NumResults-1) * replaceThisLength)); // Add the length of the rest of the string + mallocSize = RetValLength + 1; + + if ((RetVal = (char*)malloc(mallocSize)) == NULL) ILIBCRITICALEXIT(254); + RetVal[RetValLength]=0; + + RetValLength = 0; + prf = pr->FirstResult; + while (prf != NULL) + { + memcpy_s(RetVal + RetValLength, mallocSize - RetValLength, prf->data, prf->datalength); + RetValLength += prf->datalength; + + if (prf->NextResult!=NULL) + { + memcpy_s(RetVal + RetValLength, mallocSize - RetValLength, (char*)replaceWithThis, replaceWithThisLength); + RetValLength += replaceWithThisLength; + } + prf = prf->NextResult; + } + ILibDestructParserResults(pr); + return(RetVal); +} +char* ILibString_Cat_s(char *destination, size_t destinationSize, char *source) +{ + size_t sourceLen = strnlen_s(source, destinationSize); + int i; + int x = -1; + for (i = 0; i < (int)destinationSize - 1; ++i) + { + if (destination[i] == 0) { x = i; break; } + } + if (x < 0 || ((x + sourceLen + 1 )> destinationSize)) { ILIBCRITICALEXIT(254); } + memcpy_s(destination + x, destinationSize - x, source, sourceLen); + destination[x + sourceLen] = 0; + return(destination); +} +#ifndef WIN32 +#ifdef ILib_need_sprintf_s +int sprintf_s(void *dest, size_t destSize, const char *format, ...) +{ + int len = 0; + va_list argptr; + + va_start(argptr, format); + len += vsnprintf(dest + len, destSize - len, format, argptr); + va_end(argptr); + + return(len < destSize ? len : -1); +} +#endif +int ILibMemory_Copy_s(void *destination, size_t destinationSize, const void *source, size_t sourceLength) +{ + if (destinationSize >= sourceLength) + { + memcpy(destination, source, sourceLength); + } + else + { + ILIBCRITICALEXIT(254); + } + return(0); +} +int ILibMemory_Move_s(void *destination, size_t destinationSize, void *source, size_t sourceLength) +{ + if (destinationSize >= sourceLength) + { + memmove(destination, source, sourceLength); + } + else + { + ILIBCRITICALEXIT(254); + } + return(0); +} +#endif +int ILibString_n_Copy_s(char *destination, size_t destinationSize, char *source, size_t maxCount) +{ + size_t count = strnlen_s(source, maxCount == (size_t)-1 ? (destinationSize-1) : maxCount); + if ((count + 1) > destinationSize) + { + ILIBCRITICALEXIT(254); + } + memcpy_s(destination, destinationSize, source, count); + destination[count] = 0; + return(0); +} +int ILibString_Copy_s(char *destination, size_t destinationSize, char *source) +{ + size_t sourceLen = strnlen_s(source, destinationSize); + + if ((sourceLen + 1) > destinationSize) { ILIBCRITICALEXIT(254); } + memcpy_s(destination, destinationSize, source, sourceLen); + destination[sourceLen] = 0; + return(0); +} + +char* ILibString_Cat(const char *inString1, int inString1Len, const char *inString2, int inString2Len) +{ + char *RetVal; + if (inString1Len < 0) { inString1Len = (int)strnlen_s(inString1, sizeof(ILibScratchPad)); } + if (inString2Len < 0) { inString2Len = (int)strnlen_s(inString2, sizeof(ILibScratchPad)); } + if ((RetVal = (char*)malloc(inString1Len + inString2Len+1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(RetVal, inString1Len + inString2Len + 1, (char*)inString1, inString1Len); + memcpy_s(RetVal + inString1Len, inString2Len + 1, (char*)inString2, inString2Len); + RetVal[inString1Len + inString2Len]=0; + return(RetVal); +} +char* ILibString_Copy(const char *inString, int length) +{ + char *RetVal; + if (length<0) length = (int)strnlen_s(inString, sizeof(ILibScratchPad)); + if ((RetVal = (char*)malloc(length + 1)) == NULL) ILIBCRITICALEXIT(254); + memcpy_s(RetVal, length + 1, (char*)inString, length); + RetVal[length] = 0; + return(RetVal); +} +/*! \fn ILibString_ToUpper(const char *inString, int length) +\brief Coverts the given string to upper case +\par +\b Note: \a Returned string must be freed +\param inString Pointer to char* to convert to upper case +\param length The length of \a inString +\return Converted string +*/ +char *ILibString_ToUpper(const char *inString, int length) +{ + char *RetVal; + if ((RetVal = (char*)malloc(length + 1)) == NULL) ILIBCRITICALEXIT(254); + RetVal[length]=0; + ILibToUpper(inString, length, RetVal); + return(RetVal); +} +/*! \fn ILibString_ToLower(const char *inString, int length) +\brief Coverts the given string to lower case +\par +\b Note: \a Returned string must be freed +\param inString Pointer to char* to convert to lower case +\param length The length of \a inString +\return Converted string +*/ +char *ILibString_ToLower(const char *inString, int length) +{ + char *RetVal; + if ((RetVal = (char*)malloc(length + 1)) == NULL) ILIBCRITICALEXIT(254); + RetVal[length] = 0; + ILibToLower(inString, length, RetVal); + return(RetVal); +} +/*! \fn ILibReadFileFromDiskEx(char **Target, char *FileName) +\brief Reads a file into a char * +\par +\b Note: \a Target must be freed +\param Target Pointer to char* that will contain the data +\param FileName Filename of the file to read +\return length of the data read +*/ +int ILibReadFileFromDiskEx(char **Target, char *FileName) +{ + char *buffer; + int SourceFileLength; + FILE *SourceFile = NULL; + +#ifdef WIN32 + fopen_s(&SourceFile, FileName, "rb"); +#else + SourceFile = fopen(FileName, "rb"); +#endif + if (SourceFile == NULL) return(0); + + fseek(SourceFile, 0, SEEK_END); + SourceFileLength = (int)ftell(SourceFile); + fseek(SourceFile, 0, SEEK_SET); + if ((buffer = (char*)malloc(SourceFileLength)) == NULL) ILIBCRITICALEXIT(254); + SourceFileLength = (int)fread(buffer, sizeof(char), (size_t)SourceFileLength,SourceFile); + fclose(SourceFile); + + *Target = buffer; + return(SourceFileLength); +} + + +/*! \fn ILibReadFileFromDisk(char *FileName) +\brief Reads a file into a char * +\par +\b Note: Data must be null terminated +\param FileName Filename of the file to read +\return data read +*/ +char *ILibReadFileFromDisk(char *FileName) +{ + char *RetVal = NULL; + ILibReadFileFromDiskEx(&RetVal,FileName); + return(RetVal); +} + +/*! \fn ILibWriteStringToDisk(char *FileName, char *data) +\brief Writes a null terminated string to disk +\par +\b Note: Files that already exist will be overwritten +\param FileName Filename of the file to write +\param data data to write +*/ +void ILibWriteStringToDisk(char *FileName, char *data) +{ + ILibWriteStringToDiskEx(FileName, data, (int)strnlen_s(data, 65535)); +} +void ILibWriteStringToDiskEx(char *FileName, char *data, int dataLen) +{ + FILE *SourceFile = NULL; + +#ifdef WIN32 + fopen_s(&SourceFile, FileName, "wb"); +#else + SourceFile = fopen(FileName, "wb"); +#endif + + if (SourceFile != NULL) + { + fwrite(data, sizeof(char), dataLen, SourceFile); + fclose(SourceFile); + } +} +void ILibAppendStringToDiskEx(char *FileName, char *data, int dataLen) +{ + FILE *SourceFile = NULL; + +#ifdef WIN32 + fopen_s(&SourceFile, FileName, "ab"); +#else + SourceFile = fopen(FileName, "ab"); +#endif + + if (SourceFile != NULL) + { + fwrite(data, sizeof(char), dataLen, SourceFile); + fclose(SourceFile); + } +} +void ILibDeleteFileFromDisk(char *FileName) +{ +#if defined(WIN32) + DeleteFile((LPCTSTR)FileName); +#elif defined(_POSIX) + remove(FileName); +#endif +} + +void ILibGetDiskFreeSpace(void *i64FreeBytesToCaller, void *i64TotalBytes) +{ +#if defined (WIN32) + GetDiskFreeSpaceEx (NULL, (PULARGE_INTEGER)i64FreeBytesToCaller, (PULARGE_INTEGER)i64TotalBytes, NULL); +#elif defined (_POSIX) + struct statfs stfs; + statfs(".", &stfs); + *((uint64_t *)i64FreeBytesToCaller) = (uint64_t)stfs.f_bavail * stfs.f_bsize; + *((uint64_t *)i64TotalBytes)= (uint64_t)stfs.f_blocks * stfs.f_bsize; +#endif +} +long ILibGetTimeStamp() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +} +int ILibIsLittleEndian() { int v = 1; return (((char*)&v)[0] == 1 ? 1 : 0); } +int ILibGetCurrentTimezoneOffset_Minutes() +{ +#if defined(_WIN32) + int offset; + int dl = 0; + long tz = 0; + + tzset(); + _get_timezone(&tz); + offset = -1 * ((int)tz / 60); + _get_daylight(&dl); + offset += (dl * 60); + return offset; +#else + int offset; + + tzset(); + offset = -1 * ((int)timezone / 60); + offset += (daylight * 60); + return offset; +#endif +} +int ILibIsDaylightSavingsTime() +{ +#if defined(_WIN32) + int dl = 0; + tzset(); + _get_daylight(&dl); + return dl; +#else + tzset(); + return daylight; +#endif +} + +int ILibGetLocalTime(char *dest, int destLen) +{ + int retVal; + struct timeval tv; +#ifdef WIN32 + struct tm t; +#endif + gettimeofday(&tv, NULL); +#ifdef WIN32 + localtime_s(&t, (time_t*)&(tv.tv_sec)); + retVal = (int)strftime(dest, destLen, "%Y-%m-%d %I:%M:%S %p", &t); +#else + retVal = (int)strftime(dest, destLen, "%F %r", localtime((time_t*)&(tv.tv_sec))); +#endif + return(retVal); +} +#if defined(WIN32) +void ILibGetTimeOfDay(struct timeval *tp) +{ + __time64_t t; + _time64(&t); + tp->tv_usec = 0; + tp->tv_sec = (long)t; +} +#endif + +#ifdef _MINCORE +static long long ILibGetUptimeUpperEmulation1; +static long long ILibGetUptimeUpperEmulation2; +long long ILibGetUptime() +{ + // Windows XP with rollover prevention + long long r; + r = (long long)GetTickCount(); + if (r < ILibGetUptimeUpperEmulation1) ILibGetUptimeUpperEmulation2 += ((long long)1) << 32; + ILibGetUptimeUpperEmulation1 = r; + r += ILibGetUptimeUpperEmulation2; + return r; +} +#elif WIN32 +static int ILibGetUptimeFirst = 1; +static ULONGLONG (*pILibGetUptimeGetTickCount64)() = NULL; +static long long ILibGetUptimeUpperEmulation1; +static long long ILibGetUptimeUpperEmulation2; +long long ILibGetUptime() +{ + long long r; + + // Windows 7 & Vista + if (ILibGetUptimeFirst) { + + wchar_t w_dllPath[MAX_PATH]; + if (GetWindowsDllPath("\\KERNEL32.DLL", w_dllPath) == FALSE) return 0; + + HMODULE hlib = LoadLibrary(w_dllPath); + if (hlib == NULL) return 0; + pILibGetUptimeGetTickCount64 = (ULONGLONG(*)())GetProcAddress(hlib, "GetTickCount64"); + ILibGetUptimeFirst = 0; + } + if (pILibGetUptimeGetTickCount64 != NULL) return pILibGetUptimeGetTickCount64(); + + // Windows XP with rollover prevention + r = (long long)GetTickCount(); // Static Analyser reports this could roll over, but that's why this block is doing rollover prevention + if (r < ILibGetUptimeUpperEmulation1) ILibGetUptimeUpperEmulation2 += ((long long)1) << 32; + ILibGetUptimeUpperEmulation1 = r; + r += ILibGetUptimeUpperEmulation2; + return r; +} +#elif _MIPS +long long ILibGetUptime() +{ + // On MIPS routers, we just used the current clock. This could messup if the router's clock is changed. + time_t t; + time(&t); + return (t * 1000); +} +#else +long long ILibGetUptime() +{ + struct timespec ts; + memset(&ts, 0, sizeof ts); + clock_gettime(CLOCK_MONOTONIC, &ts); + return (((long long)ts.tv_sec) * 1000) + ((((long long)ts.tv_nsec) / 1000) % 1000); +} +#endif + +int ILibReaderWriterLock_ReadLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int BlockRead = 0; + int RetVal = 0; + + int AlreadyExiting = 0; + + sem_wait(&(rw->CounterLock)); + AlreadyExiting = rw->Exit; + if (rw->Exit==0) + { + BlockRead = rw->PendingWriters>0; + if (BlockRead==0) + { + ++rw->ActiveReaders; + } + else + { + ++rw->WaitingReaders; + } + } + sem_post(&(rw->CounterLock)); + + if (BlockRead!=0) + { + sem_wait(&(rw->ReadLock)); + } + + // + // We made it through! + // + RetVal = rw->Exit; + if (AlreadyExiting == 0) { sem_post(&(rw->ExitLock)); } + return(RetVal); +} +int ILibReaderWriterLock_ReadUnLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int SignalWriter = 0; + + sem_wait(&(rw->CounterLock)); + + if (rw->Exit!=0) + { + // + // Unlock, and just abort + // + sem_post(&(rw->CounterLock)); + return(1); + } + + + --rw->ActiveReaders; + SignalWriter = (rw->ActiveReaders==0 && rw->PendingWriters>0); + sem_post(&(rw->CounterLock)); + + if (SignalWriter) { sem_post(&(rw->WriteLock)); } + + sem_wait(&(rw->ExitLock)); // This won't block. We're just keeping the semaphore value stable. + return(0); +} +int ILibReaderWriterLock_WriteLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int BlockWrite = 0; + int RetVal = 0; + int AlreadyExiting = 0; + + sem_wait(&(rw->CounterLock)); + AlreadyExiting = rw->Exit; + if (rw->Exit==0) + { + ++rw->PendingWriters; + BlockWrite = (rw->ActiveReaders>0 || rw->PendingWriters>1); + } + sem_post(&(rw->CounterLock)); + + if (BlockWrite) + { + sem_wait(&(rw->WriteLock)); + } + + // + // We made it through! + // + RetVal = rw->Exit; + if (AlreadyExiting==0) + { + sem_post(&(rw->ExitLock)); + } + return(RetVal); +} +int ILibReaderWriterLock_WriteUnLock(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + int SignalAnotherWriter = 0; + int SignalReaders = 0; + + sem_wait(&(rw->CounterLock)); + + if (rw->Exit!=0) + { + // + // Unlock, and just abort + // + sem_post(&(rw->CounterLock)); + return(1); + } + + --rw->PendingWriters; + if (rw->PendingWriters>0) + { + // + // Still more Writers, so flag it. + // + SignalAnotherWriter=1; + } + else + { + // + // No more writers, so we can flag the waiting readers + // + while (rw->WaitingReaders>0) + { + --rw->WaitingReaders; + ++rw->ActiveReaders; + ++SignalReaders; + } + } + sem_post(&(rw->CounterLock)); + + if (SignalAnotherWriter) + { + sem_post(&(rw->WriteLock)); + } + else + { + while (SignalReaders>0) + { + --SignalReaders; + sem_post(&(rw->ReadLock)); + } + } + + sem_wait(&(rw->ExitLock)); // This won't block. We're just keeping the semaphore value stable. + return(0); +} +void ILibReaderWriterLock_Destroy2(ILibReaderWriterLock rwLock) +{ + struct ILibReaderWriterLock_Data *rw = (struct ILibReaderWriterLock_Data*)rwLock; + + int NumberOfWaitingLocks = 0; + + sem_wait(&(rw->CounterLock)); + rw->Exit = 1; + + NumberOfWaitingLocks += rw->WaitingReaders; + if (rw->PendingWriters>1) + { + NumberOfWaitingLocks += (rw->PendingWriters-1); + } + + while (rw->WaitingReaders>0) + { + sem_post(&(rw->ReadLock)); + --rw->WaitingReaders; + } + while (rw->PendingWriters>1) + { + sem_post(&(rw->WriteLock)); + --rw->PendingWriters; + } + sem_post(&(rw->CounterLock)); + + + while (NumberOfWaitingLocks>0) + { + sem_wait(&(rw->ExitLock)); + --NumberOfWaitingLocks; + } + + sem_destroy(&(rw->ReadLock)); + sem_destroy(&(rw->WriteLock)); + sem_destroy(&(rw->CounterLock)); + sem_destroy(&(rw->ExitLock)); +} +void ILibReaderWriterLock_Destroy(ILibReaderWriterLock rwLock) +{ + ILibReaderWriterLock_Destroy2(rwLock); + free(rwLock); +} +ILibReaderWriterLock ILibReaderWriterLock_Create() +{ + struct ILibReaderWriterLock_Data *RetVal; + if ((RetVal = (struct ILibReaderWriterLock_Data*)malloc(sizeof(struct ILibReaderWriterLock_Data))) == NULL) ILIBCRITICALEXIT(254); + memset(RetVal,0,sizeof(struct ILibReaderWriterLock_Data)); + + sem_init(&(RetVal->CounterLock),0,1); + sem_init(&(RetVal->ReadLock),0,0); + sem_init(&(RetVal->WriteLock),0,0); + sem_init(&(RetVal->ExitLock),0,0); + RetVal->Destroy = &ILibReaderWriterLock_Destroy2; + return((ILibReaderWriterLock)RetVal); +} +ILibReaderWriterLock ILibReaderWriterLock_CreateEx(void *chain) +{ + struct ILibReaderWriterLock_Data *RetVal = (struct ILibReaderWriterLock_Data*)ILibReaderWriterLock_Create(); + ILibChain_SafeAdd(chain,RetVal); + return(RetVal); +} + + +char* ILibTime_Serialize(time_t timeVal) +{ +#if defined(_WIN32) + struct tm T; + char *RetVal; + if ((RetVal = (char*)malloc(20)) == NULL) ILIBCRITICALEXIT(254); + if (localtime_s(&T, &timeVal) != 0) ILIBCRITICALEXIT(253); + sprintf_s(RetVal, 20, "%04d-%02d-%02dT%02d:%02d:%02d", + T.tm_year + 1900, + T.tm_mon + 1, + T.tm_mday, + T.tm_hour, T.tm_min, T.tm_sec); +#else + struct tm *T; + char *RetVal; + if ((RetVal = (char*)malloc(20)) == NULL) ILIBCRITICALEXIT(254); + T = localtime(&timeVal); + if (T == NULL) ILIBCRITICALEXIT(253); + sprintf_s(RetVal, 20, "%04d-%02d-%02dT%02d:%02d:%02d", + T->tm_year + 1900, + T->tm_mon + 1, + T->tm_mday, + T->tm_hour, T->tm_min, T->tm_sec); +#endif + + return(RetVal); +} +int ILibTime_ValidateTimePortion(char *timeString) +{ + // + // Verify the format is hh:mm:ss+hh:mm + // hh:mm:ss-hh:mm + // hh:mm:ssZ + // hh:mm:ss + // + char *temp; + struct parser_result *pr; + int RetVal = 0; + int length = (int)strnlen_s(timeString, 20); + if (length==8 || length==9 || length==12 || length==13 || length==14 || length==18) + { + pr = ILibParseString(timeString,0,length,":",1); + if (pr->NumResults==3 || pr->NumResults==4) + { + if (pr->FirstResult->datalength==2 && pr->FirstResult->NextResult->datalength==2) // Klockwork says this could be NULL, but that is not possible because NumResults is 3 or 4 + { + temp = ILibString_Copy(pr->FirstResult->data,pr->FirstResult->datalength); + if (atoi(temp)<24 && atoi(temp)>=0) + { + // + // hh is correct + // + free(temp); + temp = ILibString_Copy(pr->FirstResult->NextResult->data,pr->FirstResult->NextResult->datalength); + if (atoi(temp)>=0 && atoi(temp)<60) + { + // + // mm is correct + // + free(temp); + temp = ILibString_Copy(pr->FirstResult->NextResult->NextResult->data,length-6); + switch((int)strnlen_s(temp, length-6)) + { + case 2: // ss + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal=1; + } + break; + case 3: // ssZ + if (temp[2]=='Z') + { + temp[2]=0; + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal=1; + } + } + else + { + RetVal=1; + } + break; + case 6: // ss.sss + case 7: // ss.sssZ + if (temp[2]=='.') + { + temp[2]=0; + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal = 1; + } + else if (temp[6]!= 'Z' && temp[6]!=0) + { + RetVal = 1; + } + } + else + { + RetVal = 1; + } + break; + case 8: // ss+hh:mm || ss-hh:mm + if (temp[2]=='-' || temp[2]=='+') + { + temp[2] = 0; + if (!(atoi(temp)>=0 && atoi(temp)<60 && atoi(temp+3)>=0 && atoi(temp+3)<24)) + { + RetVal=1; + } + } + else + { + RetVal=1; + } + break; + case 12: // ss.sss+hh:mm || ss.sss-hh:mm + if (temp[2]=='.' && temp[9]==':' && (temp[6]=='+' || temp[6]=='-')) + { + temp[2]=0; + if (!(atoi(temp)>=0 && atoi(temp)<60)) + { + RetVal = 1; + } + } + else + { + RetVal = 1; + } + break; + default: + RetVal=1; + break; + } + if (RetVal==0 && pr->NumResults==4) + { + // + // Check the last mm component + // + if (!(atoi(pr->LastResult->data)>=0 && atoi(pr->LastResult->data)<60)) + { + RetVal=1; + } + } + } + else + { + RetVal=1; + } + } + else + { + RetVal=1; + } + free(temp); + } + else + { + RetVal=1; + } + } + else + { + RetVal=1; + } + + ILibDestructParserResults(pr); + } + else + { + RetVal=1; + } + return(RetVal); +} +char* ILibTime_ValidateDatePortion(char *timeString) +{ + struct parser_result *pr; + char *startTime; + int errCode = 1; + int timeStringLen = (int)strnlen_s(timeString, 255); + char *RetVal = NULL; + int t; + + if (timeStringLen == 10) + { + pr = ILibParseString(timeString,0, timeStringLen,"-",1); + if (pr->NumResults==3) + { + // This means there it is in x-y-z format + if (pr->FirstResult->datalength==4) + { + // This means it is in yyyy-x-z format + if (pr->FirstResult->NextResult->datalength==2 && pr->LastResult->datalength==2) // Klockwork says this could be NULL, but that's not possible, as numresults is 3 + { + // This means it is in yyyy-xx-zz format + startTime = ILibString_Copy(pr->FirstResult->NextResult->data,pr->FirstResult->NextResult->datalength); + if (atoi(startTime)<=12 && atoi(startTime)>0) + { + // This means it is in yyyy-mm-xx format + free(startTime); + startTime = ILibString_Copy(pr->LastResult->data,pr->LastResult->datalength); + if (atoi(startTime)<=31 && atoi(startTime)>0) + { + // Everything in correct format + errCode = 0; + t = timeStringLen + 10; + if ((RetVal = (char*)malloc(t)) == NULL) ILIBCRITICALEXIT(254); + sprintf_s(RetVal, t, "%sT00:00:00", timeString); + } + } + free(startTime); + } + } + } + ILibDestructParserResults(pr); + } + if (errCode==0) + { + return(RetVal); + } + else + { + return(NULL); + } +} +int ILibTime_ParseEx(char *timeString, time_t *val) +{ + int errCode = 0; + time_t RetVal = 0; + struct parser_result *pr,*pr2; + struct tm t; + char *startTime; + int timeStringLen = (int)strnlen_s(timeString, 255); + int i; + + char *year=NULL,*month=NULL,*day=NULL; + char *hour=NULL,*minute=NULL,*second=NULL; + + char *tempString; + + if (ILibString_IndexOf(timeString, timeStringLen,"T",1)==-1) + { + // + // Doesn't have time Component, so format must be: yyyy-mm-dd + // + startTime = ILibTime_ValidateDatePortion(timeString); + if (startTime==NULL) + { + errCode = 1; + } + } + else + { + // + // Verify the format is yyyy-mm-ddThh:mm:ss+hh:mm + // yyyy-mm-ddThh:mm:ss-hh:mm + // yyyy-mm-ddThh:mm:ssZ + // yyyy-mm-ddThh:mm:ss + // + i = ILibString_IndexOf(timeString, timeStringLen,"T",1); + tempString = ILibString_Copy(timeString,i); + startTime = ILibTime_ValidateDatePortion(tempString); + free(tempString); + if (startTime!=NULL) + { + free(startTime); + // + // Valid Date Portion, now check the time portion + // + if (ILibTime_ValidateTimePortion(timeString+i+1)!=0) + { + errCode = 1; + } + } + else + { + free(startTime); + // + // Invalid Date portion + // + errCode = 1; + } + if (errCode==0) + { + startTime = ILibString_Copy(timeString, timeStringLen); + } + } + + + // + // If we got this far and errCode==0, then we're golden + // + if (errCode!=0) + { + return(1); + } + + memset(&t, 0, sizeof(struct tm)); + t.tm_isdst = -1; + + pr = ILibParseString(startTime, 0, (int)strnlen_s(startTime, timeStringLen), "T", 1); + if (pr->NumResults == 2) + { + pr2 = ILibParseString(pr->FirstResult->data, 0, pr->FirstResult->datalength,"-", 1); + + if (pr2->NumResults == 3) + { + year = pr2->FirstResult->data; + year[pr2->FirstResult->datalength]=0; + + month = pr2->FirstResult->NextResult->data; // Klockwork reports this could be NULL, but that's not possible becuase NumResults is 3 + month[pr2->FirstResult->NextResult->datalength]=0; + + day = pr2->LastResult->data; + day[pr2->LastResult->datalength]=0; + + t.tm_year = atoi(year)-1900; + t.tm_mon = atoi(month)-1; + t.tm_mday = atoi(day); + + ILibDestructParserResults(pr2); + } + } + pr2 = ILibParseString(pr->LastResult->data, 0, pr->LastResult->datalength, ":", 1); + switch(pr2->NumResults) + { + case 4: + // yyyy-mm-ddThh:mm:ss+hh:mm + // yyyy-mm-ddThh:mm:ss-hh:mm + hour = pr2->FirstResult->data; + hour[pr2->FirstResult->datalength]=0; + minute = pr2->FirstResult->NextResult->data; // Klockwork reports this could be NULL, but that's not possible because NumResults is 4 + minute[pr2->FirstResult->NextResult->datalength]=0; + second = pr2->FirstResult->NextResult->NextResult->data; + second[2]=0; + break; + case 3: + // yyyy-mm-ddThh:mm:ssZ + hour = pr2->FirstResult->data; + hour[pr2->FirstResult->datalength]=0; + minute = pr2->FirstResult->NextResult->data; // Klocwork reports this could be NULL, but that's not possible becuase NumResults is 3 + minute[pr2->FirstResult->NextResult->datalength]=0; + second = pr2->FirstResult->NextResult->NextResult->data; + second[2]=0; + break; + } + if (hour!=NULL && minute!=NULL && second!=NULL) + { + t.tm_hour = atoi(hour); + t.tm_min = atoi(minute); + t.tm_sec = atoi(second); + + RetVal = mktime(&t); + } + + ILibDestructParserResults(pr2); + ILibDestructParserResults(pr); + free(startTime); + *val = RetVal; + return(errCode); +} +time_t ILibTime_Parse(char *timeString) +{ + time_t retval; + ILibTime_ParseEx(timeString, &retval); + return(retval); +} + + +//! Copy an IPv4 address into an IPv6 mapped address +/*! + \param[in] addr4 IPv4 Address + \param[in,out] addr6 IPv6 Address +*/ +void ILibMakeIPv6Addr(struct sockaddr *addr4, struct sockaddr_in6 *addr6) +{ + if (addr4->sa_family == AF_INET6 || !g_ILibDetectIPv6Support) + { + // Direct copy + memcpy_s(addr6, sizeof(struct sockaddr_in6), addr4, sizeof(struct sockaddr_in6)); + } + else + { + // Conversion + memset(addr6, 0, sizeof(struct sockaddr_in6)); + addr6->sin6_family = AF_INET6; + addr6->sin6_port = ((struct sockaddr_in*)addr4)->sin_port; + (((int*)&(addr6->sin6_addr)))[2] = 0xFFFF0000; + (((int*)&(addr6->sin6_addr)))[3] = ((int*)addr4)[1]; + } +} + +char IPv4MappedPrefix[12] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF}; +char IPv4MappedLoopback[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x7F,0x00,0x00,0x01}; +char IPv4Loopback[4] = {0x7F,0x00,0x00,0x01}; + +//! Copy the IP Address into an HTTP Header style string +/*! + \param[in] addr IP Address + \param[out] str HTTP Header Style String + \return Length of str +*/ +int ILibMakeHttpHeaderAddr(struct sockaddr *addr, char** str) +{ + int len; + if ((*str = (char *)malloc(256)) == NULL) ILIBCRITICALEXIT(254); + + if (addr->sa_family == AF_INET6) + { + // Check if this IPv6 addresses starts with the IPv4 mapped prefix + if (memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, IPv4MappedPrefix, 12) == 0) + { + // IPv4 translation conversion + ILibInet_ntop(AF_INET, &(((int*)&((struct sockaddr_in6*)addr)->sin6_addr)[3]), *str, 256); + len = (int)strnlen_s(*str, 256); + } + else + { + // IPv6 conversion + *str[0] = '['; + ILibInet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, *str + 1, 254); + len = (int)strnlen_s(*str, 256); + (*str)[len++] = ']'; + (*str)[len++] = 0; + } + } + else + { + // IPv4 conversion + ILibInet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), *str, 256); + len = (int)strnlen_s(*str, 256); + } + return len; +} + +//! Return true if the given address is an IPv4 mapped address in an IPv6 container +/*! + \param addr IP Address + \return [ZERO = FALSE, NON-ZERO = TRUE] +*/ +int ILibIsIPv4MappedAddr(struct sockaddr *addr) +{ + return (addr->sa_family == AF_INET6 && memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, IPv4MappedPrefix, 12) == 0); +} + +//! Checks if the given address is a loopback address (IPv4 or IPv6) +/*! + \param addr + \return [0 = NOT-LOOPBACK, 1 = LOOPBACK] +*/ +int ILibIsLoopback(struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) + { + if (memcmp(&((struct sockaddr_in*)addr)->sin_addr, IPv4Loopback, 4) == 0) return 1; else return 0; + } + else if (addr->sa_family == AF_INET6) + { + if (memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, &in6addr_loopback, 16) == 0) return 1; + if (memcmp(&((struct sockaddr_in6*)addr)->sin6_addr, IPv4MappedLoopback, 16) == 0) return 1; + } + return 0; +} + +int ILibGetAddrBlob(struct sockaddr *addr, char** ptr) +{ + if (addr->sa_family == AF_INET) + { + // IPv4 + *ptr = (char*)&(((struct sockaddr_in*)addr)->sin_addr); + return 4; + } + if (addr->sa_family == AF_INET6) + { + // IPv6, check if this is a IPv4 mapped address + if (ILibIsIPv4MappedAddr(addr)) + { + // This is a IPv4 mapped address, only take 4 last bytes + *ptr = (char*)&(((struct sockaddr_in6*)addr)->sin6_addr) + 12; + return 4; + } + else + { + // This is a real IPv6 address + *ptr = (char*)&(((struct sockaddr_in6*)addr)->sin6_addr); + + // If the scope is not null, we need to store the address & scope (4 bytes more) + return (((struct sockaddr_in6*)addr)->sin6_scope_id == 0)?16:20; + } + } + return 0; +} + +void ILibGetAddrFromBlob(char* ptr, int len, unsigned short port, struct sockaddr_in6 *addr) +{ + // Fetch the IP address + memset(addr, 0, sizeof(struct sockaddr_in6)); + if (len == 4) + { + // IPv4 address + addr->sin6_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_port = port; + memcpy_s(&(((struct sockaddr_in*)addr)->sin_addr), sizeof(struct in_addr), ptr, 4); + } + else if (len == 16 || len == 20) + { + // IPv6 address, or IPv6 + Scope + addr->sin6_family = AF_INET6; + addr->sin6_port = port; + // memcpy(&(addr->sin6_addr), ptr, len); // Fast version (not klocworks friendly); + memcpy_s(&(addr->sin6_addr), sizeof(struct in6_addr), ptr, 16); + if (len == 20) memcpy_s(&(addr->sin6_scope_id), 4, ptr + 16, 4); + } + else ILIBCRITICALEXIT(253) +} + +int g_ILibDetectIPv6Support = -1; + +//! Determine if IPv6 is supported +/*! + \return [ZERO = NOT SUPPORTED, NON-ZERO = SUPPORTED] +*/ +int ILibDetectIPv6Support() +{ + SOCKET sock; + struct sockaddr_in6 addr; + + if (g_ILibDetectIPv6Support >= 0) return g_ILibDetectIPv6Support; + + memset(&addr, 0, sizeof(struct sockaddr_in6)); + addr.sin6_family = AF_INET6; + + // Get our listening socket + sock = ILibGetSocket((struct sockaddr*)&addr, SOCK_DGRAM, IPPROTO_UDP); + if (sock == 0) + { + g_ILibDetectIPv6Support = 0; + } + else + { + g_ILibDetectIPv6Support = 1; +#if defined(WIN32) + closesocket(sock); +#else + close(sock); +#endif + } + + return g_ILibDetectIPv6Support; // Klocwork is being dumb. This can't be lost, because sock is getting closed if it's valid... So Klocwork is saying an invalid resource is getting lost. But we can't close an invalid handle +} + +char* ILibInet_ntop2(struct sockaddr* addr, char *dst, size_t dstsize) +{ + if (addr != NULL && addr->sa_family == AF_INET) { ILibInet_ntop(AF_INET, &(((int*)&((struct sockaddr_in*)addr)->sin_addr)[0]), dst, dstsize); return dst; } + if (addr != NULL && addr->sa_family == AF_INET6) { ILibInet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, dst, dstsize); return dst; } + dst[0] = 0; + return NULL; +} + +#if defined(WIN32) + +typedef PCTSTR (WSAAPI *inet_ntop_type)(__in INT Family, __in PVOID pAddr, __out PTSTR pStringBuf, __in size_t StringBufSize); +typedef INT (WSAAPI *inet_pton_type)(__in INT Family, __in PCTSTR pszAddrString, __out PVOID pAddrBuf); +inet_ntop_type inet_ntop_ptr = NULL; +inet_pton_type inet_pton_ptr = NULL; +int inet_pton_setup_done = 0; + +void inet_pton_setup() +{ + HMODULE h = NULL; + if (inet_pton_setup_done == 1) return; + inet_pton_setup_done = 1; + h = GetModuleHandle(TEXT("Ws2_32.dll")); + if (h == NULL) return; + inet_ntop_ptr = (inet_ntop_type)GetProcAddress(h, "inet_ntop"); + inet_pton_ptr = (inet_pton_type)GetProcAddress(h, "inet_pton"); + if (inet_ntop_ptr == NULL || inet_pton_ptr == NULL) inet_pton_setup_done = 2; +} + +char* ILibInet_ntop(int af, const char *src, char *dst, size_t dstsize) +{ + if (inet_pton_setup_done == 0) inet_pton_setup(); + + if (inet_pton_setup_done == 1) + { + // Use the real IPv6 version + return (char*)((inet_ntop_ptr)(af, (char*)src, (PTSTR)dst, dstsize)); + } + else + { + // Use the alternate IPv4 only version + if (af != AF_INET) return NULL; + _snprintf_s(dst, dstsize, dstsize, "%u.%u.%u.%u", (unsigned char)src[0], (unsigned char)src[1], (unsigned char)src[2], (unsigned char)src[3]); + return dst; + } +} + +int ILibInet_pton(int af, const char *src, char *dst) +{ + if (inet_pton_setup_done == 0) inet_pton_setup(); + + if (inet_pton_setup_done == 1) + { + // Use the real IPv6 version + return (int)(inet_pton_ptr)(af, (PCTSTR)src, dst); + } + else + { + // Use the alternate IPv4 only version + char dst2[8]; + char dst3[4]; + int i; + + if (af != AF_INET) return 0; + sscanf_s(src, "%hu.%hu.%hu.%hu", (unsigned short*)(dst2), (unsigned short*)(dst2 + 2), (unsigned short*)(dst2 + 4), (unsigned short*)(dst2 + 6)); + + for (i = 0; i < 4; ++i) { ((unsigned char*)dst3)[i] = (unsigned char)((unsigned short*)dst2)[i]; } + + ((int*)dst)[0] = ((int*)dst3)[0]; + return 1; + } +} +#else +char* ILibInet_ntop(int af, const void *src, char *dst, size_t dstsize) +{ + return (char*)inet_ntop(af, src, dst, (socklen_t)dstsize); +} +int ILibInet_pton(int af, const char *src, void *dst) +{ + return inet_pton(af, src, dst); +} +#endif + +// Compare two IP addresses. Compare: 0 already true, 1 family only, 2 family and address, 3 familly, address and port. Return 0 if different, 1 if same. +int ILibInetCompare(struct sockaddr* addr1, struct sockaddr* addr2, int compare) +{ + if (addr1 == NULL || addr2 == NULL) return 0; + if (addr1 == addr2 || compare == 0) return 1; + if (addr1->sa_family != addr2->sa_family) return 0; + if (compare == 1) return 1; + if (addr1->sa_family != AF_INET && addr1->sa_family != AF_INET6) return 0; + if (addr1->sa_family == AF_INET) + { +#ifdef WIN32 + if (((struct sockaddr_in*)addr1)->sin_addr.S_un.S_addr != ((struct sockaddr_in*)addr2)->sin_addr.S_un.S_addr) return 0; +#else + if (((struct sockaddr_in*)addr1)->sin_addr.s_addr != ((struct sockaddr_in*)addr2)->sin_addr.s_addr) return 0; +#endif + } + else + { + if (memcmp(&((struct sockaddr_in6*)addr1)->sin6_addr, &((struct sockaddr_in6*)addr2)->sin6_addr, 16) != 0) return 0; + } + if (compare == 2) return 1; + if (addr1->sa_family == AF_INET) + { + if (((struct sockaddr_in*)addr1)->sin_port != ((struct sockaddr_in*)addr2)->sin_port) return 0; + } + else + { + if (((struct sockaddr_in6*)addr1)->sin6_port != ((struct sockaddr_in6*)addr2)->sin6_port) return 0; + } + return 1; +} + +int ILibResolve(char* hostname, char* service, struct sockaddr_in6* addr6) +{ + int r; + struct addrinfo *result = NULL; + struct addrinfo hints; + + memset(addr6, 0, sizeof(struct sockaddr_in6)); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + r = getaddrinfo(hostname, service, &hints, &result); + if (r != 0) { if (result != NULL) { freeaddrinfo(result); } return r; } + if (result == NULL) return -1; + if (result->ai_family == AF_INET) { memcpy_s(addr6, sizeof(struct sockaddr_in6), result->ai_addr, sizeof(struct sockaddr_in)); } + if (result->ai_family == AF_INET6) { memcpy_s(addr6, sizeof(struct sockaddr_in6), result->ai_addr, sizeof(struct sockaddr_in6)); } + freeaddrinfo(result); + return 0; +} +int ILibResolveEx(char* hostname, unsigned short port, struct sockaddr_in6* addr6) +{ + char service[16]; + if (sprintf_s(service, sizeof(service), "%u", port) > 0) + { + return(ILibResolve(hostname, service, addr6)); + } + else + { + return(1); + } +} +unsigned char ILib6to4Header[12]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; +void ILib6to4(struct sockaddr* addr) +{ + struct sockaddr_in* addr4 = (struct sockaddr_in*)addr; + struct sockaddr_in6* addr6 = (struct sockaddr_in6*)addr; + +#ifdef WIN32 + if (addr->sa_family != AF_INET6 || memcmp(addr6->sin6_addr.u.Byte, ILib6to4Header, 12)) return; + addr4->sin_addr.s_addr = ntohl(((int*)(addr6->sin6_addr.u.Byte + 12))[0]); + addr4->sin_family = AF_INET; +#else + if (addr->sa_family != AF_INET6 || memcmp(&(addr6->sin6_addr), ILib6to4Header, 12)) return; + addr4->sin_addr.s_addr = ntohl(((int*)(&(addr6->sin6_addr) + 12))[0]); + addr4->sin_family = AF_INET; +#endif +} + +// Log a critical error to file +char* ILibCriticalLog (const char* msg, const char* file, int line, int user1, int user2) +{ + char timeStamp[32]; + int len = ILibGetLocalTime((char*)timeStamp, (int)sizeof(timeStamp)); + if (file != NULL) + { + len = sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "\r\n[%s] %s:%d (%d,%d) %s", timeStamp, file, line, user1, user2, msg); + } + else + { + len = sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "\r\n[%s] %s", timeStamp, msg); + } + if (len > 0 && len < (int)sizeof(ILibScratchPad) && ILibCriticalLogFilename != NULL) ILibAppendStringToDiskEx(ILibCriticalLogFilename, ILibScratchPad, len); + if (file != NULL) + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s:%d (%d,%d) %s", file, line, user1, user2, msg); + } + else + { + ILibRemoteLogging_printf(ILibChainGetLogger(gILibChain), ILibRemoteLogging_Modules_Microstack_Generic, ILibRemoteLogging_Flags_VerbosityLevel_1, "%s", msg); + } + return(ILibScratchPad); +} +//! Platform Agnostic method to Spawn a detached worker thread with normal priority/affinity +/*! + \param method Handler to dispatch on the new thread + \param arg Optional Parameter to dispatch [Can be NULL] + \return Thread Handle +*/ +void* ILibSpawnNormalThread(voidfp1 method, void* arg) +{ +#if defined (_POSIX) + intptr_t result; + void* (*fptr) (void* a); + pthread_t newThread; + fptr = (void*(*)(void*))method; + result = (intptr_t)pthread_create(&newThread, NULL, fptr, arg); + pthread_detach(newThread); + return (void*)result; +#endif +#ifdef WIN32 + return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)method, arg, 0, NULL ); +#endif + +#ifdef UNDER_CE + return CreateThread(NULL, 0, method, arg, 0, NULL ); +#endif +} +//! Platform Agnostic to end the currently executing thread +void ILibEndThisThread() +{ +#if defined (_POSIX) + pthread_exit(NULL); +#endif +#ifdef WIN32 + ExitThread(0); +#endif + +#ifdef UNDER_CE + ExitThread(0); +#endif +} +#ifdef WIN32 +void ILibHandle_DisableInherit(HANDLE * h) +{ + HANDLE tmpRead = *h; + if (tmpRead != NULL) + { + DuplicateHandle(GetCurrentProcess(), tmpRead, GetCurrentProcess(), h, 0, FALSE, DUPLICATE_SAME_ACCESS); + CloseHandle(tmpRead); + } +} +#endif +// Convert a block of data to HEX +// The "out" must have (len*2)+1 free space. +char ILibHexTable[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; +//! Convert a block of data to HEX +/*! + \param data Block of data to convert + \param len Data Length + \param[in,out] out Hex Result. Length must be at least (len*2) + 1 +*/ +char* ILibToHex(char* data, int len, char* out) +{ + int i; + char *p = out; + if (data == NULL || len == 0) { *p = 0; return NULL; } + for(i = 0; i < len; i++) + { + *(p++) = ILibHexTable[((unsigned char)data[i]) >> 4]; + *(p++) = ILibHexTable[((unsigned char)data[i]) & 0x0F]; + } + *p = 0; + return out; +} +//! Determine if the current thread is the Microstack thread +/*! + \param chain Microstack Thread to compare + \return 0 = NO, 1 = YES +*/ +int ILibIsRunningOnChainThread(void* chain) +{ + struct ILibBaseChain* c = (struct ILibBaseChain*)chain; + +#if defined(WIN32) + return(c->ChainThreadID == 0 || c->ChainThreadID == GetCurrentThreadId()); +#else + return(pthread_equal(c->ChainThreadID, pthread_self())); +#endif +} + +#define ILibLinkedList_FileBacked_Root_Raw_HEAD_OFFSET(root) ((unsigned int)((char*)&(root->head) - (char*)root)) +#define ILibLinkedList_FileBacked_Root_Raw_TAIL_OFFSET(root) ((unsigned int)((char*)&(root->tail) - (char*)root)) + +void ILibLinkedList_FileBacked_Close(ILibLinkedList_FileBacked_Root *root) +{ + FILE *source = ((FILE**)ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)))[0]; + if (source != NULL) { fclose(source); } + free(root); +} + +int ILibLinkedList_FileBacked_IsEmpty(ILibLinkedList_FileBacked_Root *root) +{ + return(root->head == 0 ? 1 : 0); +} + +void ILibLinkedList_FileBacked_Reset(ILibLinkedList_FileBacked_Root *root) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + fclose(source); + source = NULL; +#ifdef WIN32 + fopen_s(&source, (char*)ptr[1], "wb+N"); +#else + source = fopen((char*)ptr[1], "wb+"); +#endif + ptr[0] = source; + root->head = root->tail = 0; + ILibLinkedList_FileBacked_SaveRoot(root); +} +int ILibLinkedList_FileBacked_ReloadRoot(ILibLinkedList_FileBacked_Root* root) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + + fseek(source, 0, SEEK_SET); + return(fread((void*)root, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source) > 0 ? 0 : 1); +} +void ILibLinkedList_FileBacked_SaveRoot(ILibLinkedList_FileBacked_Root* root) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + + fseek(source, 0, SEEK_SET); + fwrite(root, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source); + fflush(source); +} +ILibLinkedList_FileBacked_Node* ILibLinkedList_FileBacked_ReadNext(ILibLinkedList_FileBacked_Root* root, ILibLinkedList_FileBacked_Node* current) +{ + void **ptr = ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + FILE* source = (FILE*)ptr[0]; + char* buffer = (char*)&ptr[2]; + ILibLinkedList_FileBacked_Node *retVal = NULL; + size_t br; + + if (current == NULL) + { + if (root->head > 0) + { + retVal = (ILibLinkedList_FileBacked_Node*)(buffer); + fseek(source, root->head, SEEK_SET); + br = fread(buffer, sizeof(char), 2 * sizeof(unsigned int), source); + if (fread(buffer + br, sizeof(char), retVal->dataLen, source) != retVal->dataLen) { retVal = NULL; } + } + } + else + { + if (current->next > 0) + { + retVal = (ILibLinkedList_FileBacked_Node*)(buffer); + fseek(source, current->next, SEEK_SET); + br = fread(buffer, sizeof(char), 2 * sizeof(unsigned int), source); + if (fread(buffer + br, sizeof(char), retVal->dataLen, source) != retVal->dataLen) { retVal = NULL; } + } + } + + return(retVal); +} + +void ILibLinkedList_FileBacked_CopyPath(ILibLinkedList_FileBacked_Root *root, char* path) +{ + char *extraMemory = (char*)ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)); + int pathLen = (int)strnlen_s(path, _MAX_PATH); + int offset = ILibMemory_GetExtraMemorySize(extraMemory) - pathLen - 1; + + memcpy_s(extraMemory + offset, pathLen, path, pathLen); + (extraMemory + offset)[pathLen] = 0; + + ((void**)extraMemory)[1] = &extraMemory[offset]; +} +ILibLinkedList_FileBacked_Root* ILibLinkedList_FileBacked_Create(char* path, unsigned int maxFileSize, int maxRecordSize) +{ + FILE* source; + ILibLinkedList_FileBacked_Root *retVal = NULL; + +#ifdef WIN32 + if (fopen_s(&source, path, "rb+N") != 0) + { + fopen_s(&source, path, "wb+N"); + } +#else + if ((source = fopen(path, "rb+")) == NULL) + { + source = fopen(path, "wb+"); + } +#endif + + if (source == NULL) { return(NULL); } + + fseek(source, 0, SEEK_END); + if ((int)ftell(source) < (int)sizeof(ILibLinkedList_FileBacked_Root)) + { + // New File + retVal = (ILibLinkedList_FileBacked_Root*)ILibMemory_Allocate(sizeof(ILibLinkedList_FileBacked_Root), maxRecordSize + (int)strnlen_s(path, _MAX_PATH) + 1 + (int)(2*sizeof(void*)), NULL, NULL); + retVal->maxSize = maxFileSize; + ((FILE**)ILibMemory_GetExtraMemory(retVal, sizeof(ILibLinkedList_FileBacked_Root)))[0] = source; + ILibLinkedList_FileBacked_CopyPath(retVal, path); + fwrite((void*)retVal, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source); + fflush(source); + } + else + { + // File Exists + fseek(source, 0, SEEK_SET); + retVal = (ILibLinkedList_FileBacked_Root*)ILibMemory_Allocate(sizeof(ILibLinkedList_FileBacked_Root), maxRecordSize + (int)strnlen_s(path, _MAX_PATH) + 1 + (int)(2 * sizeof(void*)), NULL, NULL); + if (fread((void*)retVal, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source) != sizeof(ILibLinkedList_FileBacked_Root)) + { + free(retVal); + retVal = NULL; + fclose(source); + } + else + { + ((FILE**)ILibMemory_GetExtraMemory(retVal, sizeof(ILibLinkedList_FileBacked_Root)))[0] = source; + ILibLinkedList_FileBacked_CopyPath(retVal, path); + } + } + + return(retVal); +} + +int ILibLinkedList_FileBacked_AddTail(ILibLinkedList_FileBacked_Root* root, char* data, unsigned int dataLen) +{ + unsigned int sizeNeeded = dataLen + 8; + unsigned int i; + unsigned int tmp; + int headUpdated = 0; + + FILE* source = ((FILE**)ILibMemory_GetExtraMemory(root, sizeof(ILibLinkedList_FileBacked_Root)))[0]; + + fseek(source, 0, SEEK_SET); + if (fread((void*)root, sizeof(char), sizeof(ILibLinkedList_FileBacked_Root), source) != sizeof(ILibLinkedList_FileBacked_Root)) { return(1); } + + // Grab a memory pointer that we can write to + if (root->tail == 0) + { + i = (unsigned int)sizeof(ILibLinkedList_FileBacked_Root); + } + else + { + // Need to calculate some things + fseek(source, root->tail + (unsigned int)sizeof(unsigned int), SEEK_SET); + if (fread(&i, sizeof(char), sizeof(unsigned int), source) != sizeof(unsigned int)) { return(1); } + i += ((unsigned int)2*sizeof(unsigned int) + root->tail); + + if (i + sizeNeeded > root->maxSize) { i = root->head; } + } + + // Check to see if we write to this memory location, do we need to move the head pointer + while (root->head != 0 && i <= root->head && (i + sizeNeeded > root->head)) + { + fseek(source, root->head, SEEK_SET); + if (fread(&(root->head), sizeof(char), sizeof(unsigned int), source) != sizeof(unsigned int)) { return(1); } + headUpdated = 1; + } + + if (root->head == 0) + { + root->head = i; + headUpdated = 1; + } + + // Write the new Head Pointer + if (headUpdated != 0) + { + fseek(source, ILibLinkedList_FileBacked_Root_Raw_HEAD_OFFSET(root), SEEK_SET); + fwrite(&(root->head), sizeof(char), sizeof(unsigned int), source); + fflush(source); + } + + // Write Record + fseek(source, i, SEEK_SET); + tmp = 0; + fwrite(&tmp, sizeof(char), sizeof(unsigned int), source); // Next + fwrite(&dataLen, sizeof(char), sizeof(unsigned int), source); // Data Length + if (dataLen > 0) + { + fwrite(data, sizeof(char), dataLen, source); // Data + } + fflush(source); + + // Update 'Next' of old Tail + if (root->tail != 0) + { + fseek(source, root->tail, SEEK_SET); + fwrite(&i, sizeof(char), sizeof(unsigned int), source); // New NEXT value + fflush(source); + } + + // Write new Tail Pointer + fseek(source, ILibLinkedList_FileBacked_Root_Raw_TAIL_OFFSET(root), SEEK_SET); + fwrite(&i, sizeof(char), sizeof(unsigned int), source); // NEXT + fflush(source); + return(0); +} diff --git a/MicroLMS/microstack/ILibParsers.h b/MicroLMS/microstack/ILibParsers.h new file mode 100644 index 0000000..60dee22 --- /dev/null +++ b/MicroLMS/microstack/ILibParsers.h @@ -0,0 +1,1302 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +/*! \file ILibParsers.h +\brief MicroStack APIs for various functions and tasks +*/ + +#ifndef __ILibParsers__ +#define __ILibParsers__ + +#ifdef __cplusplus +extern "C" { +#endif + + /*! \defgroup ILibParsers ILibParser Modules + * @{ + * @} + */ + + /*! \def MAX_HEADER_LENGTH + Specifies the maximum allowed length for an HTTP Header + */ +#define MAX_HEADER_LENGTH 800 + +#ifdef MEMORY_CHECK +#include +#define MEMCHECK(x) x +#else +#define MEMCHECK(x) +#endif + +#if !defined(WIN32) +#define HANDLE int +#define SOCKET int +#endif + +extern char* ILibCriticalLogFilename; + + +#ifndef WIN32 +#define REQUIRES_MEMORY_ALIGNMENT +#endif + +#if defined(WIN32) +#ifndef MICROSTACK_NO_STDAFX +#include "stdafx.h" +#endif +struct sockaddr_in6; +#elif defined(_POSIX) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(WIN32) && defined (_DEBUG) && !defined(_MINCORE) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif +#ifdef _POSIX +#include +#endif + +#include +#include +#include +#include + + +#if defined(WIN32) +#include +#include +#include +#include +#include +#endif + +#if defined(WIN32) +#include +#include +#endif + +#ifndef _MAX_PATH +#define _MAX_PATH PATH_MAX +#endif + +#include +static inline void ignore_result(uint64_t result) { (void)result; } + +int ILibGetLocalTime(char *dest, int destLen); +long ILibGetTimeStamp(); + +#ifdef ILibEXPORT +#ifdef WIN32 +#define ILibExportMethod __declspec(dllexport) +#else +#define ILibExportMethod +#endif +#else +#define ILibExportMethod +#endif + + +#if defined(WIN32) +#ifndef FD_SETSIZE +#define SEM_MAX_COUNT 64 +#else +#define SEM_MAX_COUNT FD_SETSIZE +#endif + void ILibGetTimeOfDay(struct timeval *tp); + +/* +// Implemented in Windows using events. +#define sem_t HANDLE +#define sem_init(x,pShared,InitValue) *x=CreateSemaphore(NULL,InitValue,SEM_MAX_COUNT,NULL) +#define sem_destroy(x) (CloseHandle(*x)==0?1:0) +#define sem_wait(x) WaitForSingleObject(*x,INFINITE) +#define sem_trywait(x) ((WaitForSingleObject(*x,0)==WAIT_OBJECT_0)?0:1) +#define sem_post(x) ReleaseSemaphore(*x,1,NULL) +*/ + +// Implemented in Windows using critical section. +#define sem_t CRITICAL_SECTION +#define sem_init(x,pShared,InitValue) InitializeCriticalSection(x); +#define sem_destroy(x) (DeleteCriticalSection(x)) +#define sem_wait(x) EnterCriticalSection(x) +#define sem_trywait(x) TryEnterCriticalSection(x) +#define sem_post(x) LeaveCriticalSection(x) + +#define strncasecmp(x,y,z) _strnicmp(x,y,z) +#define strcasecmp(x,y) _stricmp(x,y) +#define gettimeofday(tp,tzp) ILibGetTimeOfDay(tp) + +#define tzset() _tzset() +#define daylight _daylight +#define timezone _timezone + +#ifndef stricmp +#define stricmp(x,y) _stricmp(x,y) +#endif + +#ifndef strnicmp +#define strnicmp(x,y,z) _strnicmp(x,y,z) +#endif + +#ifndef strcmpi +#define strcmpi(x,y) _stricmp(x,y) +#endif +#endif + + +#ifndef WIN32 + +// Polyfills +#ifndef memcpy_s + int ILibMemory_Copy_s(void *destination, size_t destinationSize, const void *source, size_t sourceLength); + #define memcpy_s(dest, destSize, source, sourceLen) ILibMemory_Copy_s(dest, destSize, source, sourceLen) +#endif + +#ifndef memmove_s + int ILibMemory_Move_s(void *destination, size_t destinationSize, void *source, size_t sourceLength); + #define memmove_s(dest, destSize, source, sourceLen) ILibMemory_Move_s(dest, destSize, source, sourceLen) +#endif + +#ifndef strncpy_s +#define strncpy_s(dest, destSize, source, count) ILibString_n_Copy_s(dest, destSize, source, count) +#endif + +#ifndef strcpy_s +#define strcpy_s(dest, destSize, source) ILibString_Copy_s(dest, destSize, source) +#endif + +#ifndef strnlen_s +#define strnlen_s(source, maxCount) (strlen(source) < (maxCount) ? strlen(source) : (maxCount)) +#endif + +#ifndef sprintf_s +#define ILib_need_sprintf_s +#include + int sprintf_s(void *dest, size_t destSize, const char *format, ...); +#endif + +#ifndef strnlen_s +#define strnlen_s(source, maxLen) strnlen(source, maxLen) +#endif + +#endif + + + +#if !defined(WIN32) +#define __fastcall +#endif + + typedef void (*voidfp)(void); // Generic function pointer + typedef void(*voidfp1)(void*); // Generic function pointer + extern char ILibScratchPad[4096]; // General buffer + extern char ILibScratchPad2[65536]; // Often used for UDP packet processing + extern char ILibScratchPad_WWWAuth1[1024]; // Used to construct WWW auth digest request + extern char ILibScratchPad_WWWAuth2[4096]; // Used to construct WWW auth digest request + extern void* gILibChain; // Global Chain used for Remote Logging when a chain instance is not otherwise exposed + + /*! \def UPnPMIN(a,b) + Returns the minimum of \a a and \a b. + */ +#define UPnPMIN(a,b) (((a)<(b))?(a):(b)) + +/*! +\ingroup ChainGroup +*@{ +*/ + extern const int ILibMemory_CHAIN_CONTAINERSIZE; + +//! Determines if the specified chain is in the process of being disposed. +/*! + \param Chain Microstack Chain to query + \return 0 = NO, 1 = YES +*/ +#define ILibIsChainBeingDestroyed(Chain) (*((int*)Chain)) +//! Determines if the specified chain is running +/*! + \param Chain Microstack Chain to query + \return 0 = NO, 1 = YES +*/ +#define ILibIsChainRunning(Chain) (((int*)Chain)[1]) +int ILibIsRunningOnChainThread(void* chain); +/*! @} */ + + typedef enum ILibServerScope + { + ILibServerScope_All=0, + ILibServerScope_LocalLoopback=1, + ILibServerScope_LocalSegment=2 + }ILibServerScope; + + + typedef void(*ILibChain_PreSelect)(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime); + typedef void(*ILibChain_PostSelect)(void* object, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset); + typedef void(*ILibChain_Destroy)(void* object); + typedef void(*ILibChain_DestroyEvent)(void *chain, void *user); + typedef void(*ILibChain_StartEvent)(void *chain, void *user); + typedef void*(*ILibChain_Link_GetUserMemory)(void *ChainLinkObject, int *len); + typedef void* ILibChain_EventHookToken; + typedef void(*ILibChain_EventHookHandler)(void *hookedObject, ILibChain_EventHookToken token); + + typedef struct ILibChain_Link + { + ILibChain_PreSelect PreSelectHandler; + ILibChain_PostSelect PostSelectHandler; + ILibChain_Destroy DestroyHandler; + void* ParentChain; + void* ExtraMemoryPtr; + }ILibChain_Link; + + ILibChain_Link* ILibChain_Link_Allocate(int structSize, int extraMemorySize); + int ILibChain_Link_GetExtraMemorySize(ILibChain_Link* link); + + void* ILibMemory_Allocate(int containerSize, int extraMemorySize, void** allocatedContainer, void **extraMemory); + int ILibMemory_GetExtraMemorySize(void* extraMemory); + ILibExportMethod void* ILibMemory_GetExtraMemory(void *container, int containerSize); + ILibChain_EventHookToken ILibChain_SetEventHook(void* chainLinkObject, int maxTimeout, ILibChain_EventHookHandler handler); + void ILibChain_UpdateEventHook(ILibChain_EventHookToken token, int maxTimeout); + + /*! + \defgroup LoggingMethods Logging Methods + \ingroup ILibParsers + @{ + */ +#ifdef _REMOTELOGGING + //! Associate an ILibRemoteLogging module with a microstack Chain + /*! + \param chain Microstack Chain to associate + \param logger ILibRemoteLogging module to associate + */ + #define ILibChainSetLogger(chain, logger) ((void**)&((int*)chain)[2])[0] = logger + //! Gets the associated ILibRemoteLogging module from a microstack chain + /*! + \paran chain Microstack chain to get the association from + \return ILibRemoteLogging module associated (NULL if none associated) + */ + #define ILibChainGetLogger(chain) (chain!=NULL?(((void**)&((int*)chain)[2])[0]):NULL) + #ifdef _REMOTELOGGINGSERVER + ILibExportMethod unsigned short ILibStartDefaultLoggerEx(void* chain, unsigned short portNumber, char *path); + #define ILibStartDefaultLogger(chain, portNumber) ILibStartDefaultLoggerEx(chain, portNumber, NULL) + #else + #define ILibStartDefaultLogger(chain, portNumber) 0 + #define ILibStartDefaultLoggerEx(chain, portNumber, path) 0 + #endif +#else + #define ILibChainSetLogger(chain, logger) ; + #define ILibChainGetLogger(chain) NULL + #define ILibStartDefaultLogger(chain, portNumber) 0 + #define ILibStartDefaultLoggerEx(chain, portNumber, path) 0 +#endif + /*! @} */ + + /*! + \ingroup ChainGroup + *@{ + */ + void ILibChain_OnDestroyEvent_AddHandler(void *chain, ILibChain_DestroyEvent sink, void *user); + void ILibChain_OnStartEvent_AddHandler(void *chain, ILibChain_StartEvent sink, void *user); + /*! @} */ + +#ifdef MICROSTACK_NOTLS + #define NONCE_SIZE 32 + #define HALF_NONCE_SIZE 16 + + char* __fastcall util_tohex(char* data, int len, char* out); + int __fastcall util_hexToint(char *hexString, int hexStringLength); + + // Convert hex string to int + int __fastcall util_hexToBuf(char *hexString, int hexStringLength, char* output); +#endif + int ILibIsLittleEndian(); + int ILibGetCurrentTimezoneOffset_Minutes(); + int ILibIsDaylightSavingsTime(); + int ILibTime_ParseEx(char *timeString, time_t *val); + time_t ILibTime_Parse(char *timeString); + char* ILibTime_Serialize(time_t timeVal); + long long ILibGetUptime(); + + unsigned long long ILibHTONLL(unsigned long long v); + unsigned long long ILibNTOHLL(unsigned long long v); + + typedef void* ILibReaderWriterLock; + ILibReaderWriterLock ILibReaderWriterLock_Create(); + ILibReaderWriterLock ILibReaderWriterLock_CreateEx(void *chain); + int ILibReaderWriterLock_ReadLock(ILibReaderWriterLock rwLock); + int ILibReaderWriterLock_ReadUnLock(ILibReaderWriterLock rwLock); + int ILibReaderWriterLock_WriteLock(ILibReaderWriterLock rwLock); + int ILibReaderWriterLock_WriteUnLock(ILibReaderWriterLock rwLock); + void ILibReaderWriterLock_Destroy(ILibReaderWriterLock rwLock); + + /*! \defgroup ILibTransport ILibTransport Abstraction + \ingroup ILibParsers + * @{ + */ + //! Fetch the underlying chain from an ILibTransport object + /*! + \param transport ILibTransport object to query + \return Microstack Chain + */ + #define ILibTransportChain(transport) ((ILibTransport*)transport)->ChainLink.ParentChain + //! Memory Ownership of ILibTransport Data + typedef enum ILibTransport_MemoryOwnership + { + ILibTransport_MemoryOwnership_CHAIN = 0, /*!< The Microstack will own this memory, and free it when it is done with it */ + ILibTransport_MemoryOwnership_STATIC = 1, /*!< This memory is static, so the Microstack will not free it, and assume it will not go away, so it won't copy it either */ + ILibTransport_MemoryOwnership_USER = 2 /*!< The Microstack doesn't own this memory, so if necessary the memory will be copied */ + }ILibTransport_MemoryOwnership; + //! Operation Status + typedef enum ILibTransport_DoneState + { + ILibTransport_DoneState_INCOMPLETE = 0, /*!< Operation not complete [PENDING] */ + ILibTransport_DoneState_COMPLETE = 1, /*!< Operation complete */ + ILibTransport_DoneState_ERROR = -4 /*!< Operation failed */ + }ILibTransport_DoneState; + + typedef ILibTransport_DoneState(*ILibTransport_SendPtr)(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done); + typedef void(*ILibTransport_ClosePtr)(void *transport); + typedef unsigned int(*ILibTransport_PendingBytesToSendPtr)(void *transport); + + ILibTransport_DoneState ILibTransport_Send(void *transport, char* buffer, int bufferLength, ILibTransport_MemoryOwnership ownership, ILibTransport_DoneState done); + void ILibTransport_Close(void *transport); + unsigned int ILibTransport_PendingBytesToSend(void *transport); + + //! ILibTransport Abstraction [DO NOT MODIFY STRUCT DEFINITION] + typedef void(*ILibTransport_OnSendOK)(void* sender); + typedef struct ILibTransport + { + ILibChain_Link ChainLink; + ILibTransport_SendPtr SendPtr; /*!< [RESERVED: Encapsulated Send Handler] */ + ILibTransport_ClosePtr ClosePtr; /*!< [RESERVED: Encapsulated Close Handler] */ + ILibTransport_PendingBytesToSendPtr PendingBytesPtr; /*!< [RESERVED: Encapsulated Pending Handler] */ + ILibTransport_OnSendOK SendOkPtr; /*!< [RESERVED: Encapsulated SendOK Callback] */ + unsigned int IdentifierFlags; /*!< ILibTransport Type Identifier */ + }ILibTransport; + /*! @} */ + + + /*! \defgroup DataStructures Data Structures + \ingroup ILibParsers + \{ + \} + */ + + + /*! \struct parser_result_field ILibParsers.h + \brief Data Elements of \a parser_result + \par + This structure represents individual tokens, resulting from a call to + \a ILibParseString and \a ILibParseStringAdv + */ + typedef struct parser_result_field + { + /*! \var data + \brief Pointer to string + */ + char* data; + + /*! \var datalength + \brief Length of \a data + */ + int datalength; + + /*! \var NextResult + \brief Pointer to next token + */ + struct parser_result_field *NextResult; + }parser_result_field; + + /*! \struct parser_result ILibParsers.h + \brief String Parsing Result Index + \par + This is returned from a successfull call to either \a ILibParseString or + \a ILibParseStringAdv. + */ + typedef struct parser_result + { + /*! \var FirstResult + \brief Pointer to the first token + */ + struct parser_result_field *FirstResult; + /*! \var LastResult + \brief Pointer to the last token + */ + struct parser_result_field *LastResult; + /*! \var NumResults + \brief The total number of tokens + */ + int NumResults; + }parser_result; + + /*! \struct packetheader_field_node ILibParsers.h + \brief HTTP Headers + \par + This structure represents an individual header element. A list of these + is referenced from a \a packetheader_field_node. + */ + typedef struct packetheader_field_node + { + /*! \var Field + \brief Header Name + */ + char* Field; + /*! \var FieldLength + \brief Length of \a Field + */ + int FieldLength; + /*! \var FieldData + \brief Header Value + */ + char* FieldData; + /*! \var FieldDataLength + \brief Length of \a FieldData + */ + int FieldDataLength; + /*! \var UserAllocStrings + \brief Boolean indicating if the above strings are non-static memory + */ + int UserAllocStrings; + /*! \var NextField + \brief Pointer to the Next Header entry. NULL if this is the last one + */ + struct packetheader_field_node* NextField; + }packetheader_field_node; + + /*! \struct packetheader ILibParsers.h + \brief Structure representing a packet formatted according to HTTP encoding rules + \par + This can be created manually by calling \a ILibCreateEmptyPacket(), or automatically via a call to \a ILibParsePacketHeader(...) + */ + typedef struct packetheader + { + /*! \var Directive + \brief HTTP Method + \par + eg: \b GET /index.html HTTP/1.1 + */ + char* Directive; + /*! \var DirectiveLength + \brief Length of \a Directive + */ + int DirectiveLength; + /*! \var DirectiveObj + \brief HTTP Method Path + \par + eg: GET \b /index.html HTTP/1.1 + */ + char* DirectiveObj; + /*! \var DirectiveObjLength + \brief Length of \a DirectiveObj + */ + + void *Reserved; + + int DirectiveObjLength; + /*! \var StatusCode + \brief HTTP Response Code + \par + eg: HTTP/1.1 \b 200 OK + */ + int StatusCode; + /* \var StatusData + \brief Status Meta Data + \par + eg: HTTP/1.1 200 \b OK + */ + char* StatusData; + /*! \var StatusDataLength + \brief Length of \a StatusData + */ + int StatusDataLength; + /*! \var Version + \brief HTTP Version + \par + eg: 1.1 + */ + char* Version; + /*! \var VersionLength + \brief Length of \a Version + */ + int VersionLength; + /*! \var Body + \brief Pointer to HTTP Body + */ + char* Body; + /*! \var BodyLength + \brief Length of \a Body + */ + int BodyLength; + /*! \var UserAllocStrings + \brief Boolean indicating if Directive/Obj are non-static + \par + This only needs to be set, if you manually populate \a Directive and \a DirectiveObj.
+ It is \b recommended that you use \a ILibSetDirective + */ + int UserAllocStrings; // Set flag if you allocate memory pointed to by Directive/Obj + /*! \var UserAllocVersion + \brief Boolean indicating if Version string is non-static + \par + This only needs to be set, if you manually populate \a Version.
+ It is \b recommended that you use \a ILibSetVersion + */ + int UserAllocVersion; // Set flag if you allocate memory pointed to by Version + int ClonedPacket; + + /*! \var FirstField + \brief Pointer to the first Header field + */ + struct packetheader_field_node* FirstField; + /*! \var LastField + \brief Pointer to the last Header field + */ + struct packetheader_field_node* LastField; + + /*! \var Source + \brief The origin of this packet + \par + This is only populated if you obtain this structure from either \a ILibWebServer or + \a ILibWebClient. + */ + char Source[30]; + /*! \var ReceivingAddress + \brief IP address that this packet was received on + \par + This is only populated if you obtain this structure from either \a ILibWebServer or + \a ILibWebClient. + */ + char ReceivingAddress[30]; + + void *HeaderTable; + }ILibHTTPPacket; + + /*! \struct ILibXMLNode + \brief An XML Tree + \par + This is obtained via a call to \a ILibParseXML. It is \b highly \b recommended + that you call \a ILibProcessXMLNodeList, so that the node pointers at the end of this + structure will be populated. That will greatly simplify node traversal.

+ In order for namespaces to be resolved, you must call \a ILibXML_BuildNamespaceLookupTable(...) + with root-most node that you would like to resolve namespaces for. It is recommended that you always use + the root node, obtained from the initial call to \a ILibParseXML.

+ For most intents and purposes, you only need to work with the "StartElements" + */ + struct ILibXMLNode + { + /*! \var Name + \brief Local Name of the current element + */ + char* Name; // Element Name + /*! \var NameLength + \brief Length of \a Name + */ + int NameLength; + + /*! \var NSTag + \brief Namespace Prefix of the current element + \par + This can be resolved using a call to \a ILibXML_LookupNamespace(...) + */ + char* NSTag; // Element Prefix + /*! \var NSLength + \brief Length of \a NSTag + */ + int NSLength; + + /*! \var StartTag + \brief boolean indicating if the current element is a start element + */ + int StartTag; // Non zero if this is a StartElement + /*! \var EmptyTag + \brief boolean indicating if this element is an empty element + */ + int EmptyTag; // Non zero if this is an EmptyElement + + void *Reserved; // DO NOT TOUCH + void *Reserved2; // DO NOT TOUCH + + /*! \var Next + \brief Pointer to the child of the current node + */ + struct ILibXMLNode *Next; // Next Node + /*! \var Parent + \brief Pointer to the Parent of the current node + */ + struct ILibXMLNode *Parent; // Parent Node + /*! \var Peer + \brief Pointer to the sibling of the current node + */ + struct ILibXMLNode *Peer; // Sibling Node + struct ILibXMLNode *ClosingTag; // Pointer to closing node of this element + struct ILibXMLNode *StartingTag; // Pointer to start node of this element + }; + + /*! \struct ILibXMLAttribute + \brief A list of XML Attributes for a specified XML node + \par + This can be obtained via a call to \a ILibGetXMLAttributes(...) + */ + struct ILibXMLAttribute + { + /*! \var Name + \brief Local name of Attribute + */ + char* Name; // Attribute Name + /*! \var NameLength + \brief Length of \a Name + */ + int NameLength; + + /*! \var Prefix + \brief Namespace Prefix of this attribute + \par + This can be resolved by calling \a ILibXML_LookupNamespace(...) and passing in \a Parent as the current node + */ + char* Prefix; // Attribute Namespace Prefix + /*! \var PrefixLength + \brief Lenth of \a Prefix + */ + int PrefixLength; + + /*! \var Parent + \brief Pointer to the XML Node that contains this attribute + */ + struct ILibXMLNode *Parent; // The XML Node this attribute belongs to + + /*! \var Value + \brief Attribute Value + */ + char* Value; // Attribute Value + /*! \var ValueLength + \brief Length of \a Value + */ + int ValueLength; + /*! \var Next + \brief Pointer to the next attribute + */ + struct ILibXMLAttribute *Next; // Next Attribute + }; + + char *ILibReadFileFromDisk(char *FileName); + int ILibReadFileFromDiskEx(char **Target, char *FileName); + void ILibWriteStringToDisk(char *FileName, char *data); + void ILibAppendStringToDiskEx(char *FileName, char *data, int dataLen); + void ILibWriteStringToDiskEx(char *FileName, char *data, int dataLen); + void ILibDeleteFileFromDisk(char *FileName); + void ILibGetDiskFreeSpace(void *i64FreeBytesToCaller, void *i64TotalBytes); + + + /*! \defgroup StackGroup Stack + \ingroup DataStructures + Stack Methods + \{ + */ + void ILibCreateStack(void **TheStack); + void ILibPushStack(void **TheStack, void *data); + void *ILibPopStack(void **TheStack); + void *ILibPeekStack(void **TheStack); + void ILibClearStack(void **TheStack); + /*! \} */ + + /*! \defgroup QueueGroup Queue + \ingroup DataStructures + Queue Methods + \{ + */ + typedef void* ILibQueue; + ILibQueue ILibQueue_Create(); + void ILibQueue_Destroy(ILibQueue q); + int ILibQueue_IsEmpty(ILibQueue q); + void ILibQueue_EnQueue(ILibQueue q, void *data); + void *ILibQueue_DeQueue(ILibQueue q); + #define ILibQueue_PeekTail(q) ILibLinkedList_GetDataFromNode(ILibLinkedList_GetNode_Tail(q)) + void *ILibQueue_PeekQueue(ILibQueue q); + void ILibQueue_Lock(ILibQueue q); + void ILibQueue_UnLock(ILibQueue q); + long ILibQueue_GetCount(ILibQueue q); + /* \} */ + + + /*! \defgroup XML XML Parsing Methods + \ingroup ILibParsers + MicroStack supplied XML Parsing Methods + \par + \b Note: None of the XML Parsing Methods copy or allocate memory + The lifetime of any/all strings is bound to the underlying string that was + parsed using ILibParseXML. If you wish to keep any of these strings for longer + then the lifetime of the underlying string, you must copy the string. + \{ + */ + + + // + // Parses an XML string. Returns a tree of ILibXMLNode elements. + // + struct ILibXMLNode *ILibParseXML(char *buffer, int offset, int length); + + // + // Preprocesses the tree of ILibXMLNode elements returned by ILibParseXML. + // This method populates all the node pointers in each node for easy traversal. + // In addition, this method will also determine if the given XML document was well formed. + // Returns 0 if processing succeeded. Specific Error Codes are returned otherwise. + // + int ILibProcessXMLNodeList(struct ILibXMLNode *nodeList); + + // + // Initalizes a namespace lookup table for a given parent node. + // If you want to resolve namespaces, you must call this method exactly once + // + void ILibXML_BuildNamespaceLookupTable(struct ILibXMLNode *node); + + // + // Resolves a namespace prefix. + // + char* ILibXML_LookupNamespace(struct ILibXMLNode *currentLocation, char *prefix, int prefixLength); + + // + // Fetches all the data for an element. Returns the length of the populated data + // + int ILibReadInnerXML(struct ILibXMLNode *node, char **RetVal); + + // + // Returns the attributes of an XML element. Returned as a linked list of ILibXMLAttribute. + // + struct ILibXMLAttribute *ILibGetXMLAttributes(struct ILibXMLNode *node); + + void ILibDestructXMLNodeList(struct ILibXMLNode *node); + void ILibDestructXMLAttributeList(struct ILibXMLAttribute *attribute); + + // + // Escapes an XML string. + // indata must be pre-allocated. + // + int ILibXmlEscapeEx(char* outdata, const char* indata, size_t indataLen); + #define ILibXmlEscape(outdata, indata) ILibXmlEscapeEx(outdata, indata, strnlen_s(indata, sizeof(ILibScratchPad2))) + // + // Returns the required size string necessary to escape this XML string + // + int ILibXmlEscapeLengthEx(const char* data, size_t dataLen); + #define ILibXmlEscapeLength(data) ILibXmlEscapeLengthEx(data, strnlen_s(data, sizeof(ILibScratchPad2))) + // + // Unescapes an XML string. + // Since Unescaped strings are always shorter than escaped strings, + // the resultant string will overwrite the supplied string, to save memory + // + int ILibInPlaceXmlUnEscapeEx(char* data, size_t dataLen); + #define ILibInPlaceXmlUnEscape(data) ILibInPlaceXmlUnEscapeEx(data, strnlen_s(data, sizeof(ILibScratchPad2))) + /*! \} */ + + /*! \defgroup ChainGroup Chain Methods + \ingroup ILibParsers + \brief Chaining Methods + \{ + */ + ILibExportMethod void *ILibCreateChain(); + void *ILibCreateChainEx(int extraMemorySize); + void ILibAddToChain(void *chain, void *object); + void *ILibGetBaseTimer(void *chain); + void ILibChain_SafeAdd(void *chain, void *object); + void ILibChain_SafeRemove(void *chain, void *object); + void ILibChain_SafeRemoveEx(void *chain, void *object); + void ILibChain_DestroyEx(void *chain); + ILibExportMethod void ILibStartChain(void *chain); + ILibExportMethod void ILibStopChain(void *chain); + ILibExportMethod void ILibChain_Continue(void *chain, ILibChain_Link **modules, int moduleCount, int maxTimeout); + ILibExportMethod void ILibChain_EndContinue(void *chain); + + void ILibForceUnBlockChain(void *Chain); + void ILibChain_RunOnMicrostackThreadEx(void *chain, ILibChain_StartEvent handler, void *user); + #define ILibChain_RunOnMicrostackThread(chain, handler, user) if(ILibIsRunningOnChainThread(chain)==0){ILibChain_RunOnMicrostackThreadEx(chain, handler, user);}else{handler(chain,user);} + /* \} */ + + /*! \defgroup SparseArrayGroup Sparse Array + \ingroup DataStructures + Sparse Array is a compact array where most of the elements will have the default value [NULL]. + *@{ + */ + + typedef void* ILibSparseArray; + //! Event handler triggered when an index needs to be hashed into a bucket value + /*! + \param value Index value to be hashed + \return Hashed Bucket value + */ + typedef int(*ILibSparseArray_Bucketizer)(int value); + //! Generic Event handler + /*! + \param sender The Sparse Array that generated the event + \param index Index Value + \param value Data Value + \param user Custom user state object + */ + typedef void(*ILibSparseArray_OnValue)(ILibSparseArray sender, int index, void *value, void *user); + extern const int ILibMemory_SparseArray_CONTAINERSIZE; + ILibSparseArray ILibSparseArray_CreateWithUserMemory(int numberOfBuckets, ILibSparseArray_Bucketizer bucketizer, int userMemorySize); + #define ILibSparseArray_Create(numberOfBuckets, bucketizer) ILibSparseArray_CreateWithUserMemory(numberOfBuckets, bucketizer, 0) + ILibSparseArray ILibSparseArray_CreateEx(ILibSparseArray source); + void* ILibSparseArray_Add(ILibSparseArray sarray, int index, void *data); + void* ILibSparseArray_Get(ILibSparseArray sarray, int index); + void* ILibSparseArray_Remove(ILibSparseArray sarray, int index); + void ILibSparseArray_Destroy(ILibSparseArray sarray); + void ILibSparseArray_DestroyEx(ILibSparseArray sarray, ILibSparseArray_OnValue onDestroy, void *user); + void ILibSparseArray_ClearEx2(ILibSparseArray sarray, ILibSparseArray_OnValue onClear, void *user, int nonZeroWillDelete); + #define ILibSparseArray_ClearEx(sarray, onClear, user) ILibSparseArray_ClearEx2(sarray, onClear, user, 1) + #define ILibSparseArray_Enumerate(sarray, onEnumerate, user) ILibSparseArray_ClearEx2(sarray, onEnumerate, user, 0) + ILibSparseArray ILibSparseArray_Move(ILibSparseArray sarray); + + void ILibSparseArray_Lock(ILibSparseArray sarray); + void ILibSparseArray_UnLock(ILibSparseArray sarray); + + /*! + *@} + */ + + /*! \defgroup AdvHashtableGroup Advanced Hashtable + \ingroup DataStructures + Advanced Hashtable is a customizable Hashtable data structure, that can be optimized as necessary + *@{ + */ + + typedef void* ILibHashtable; + //! Event handler for hash requests [Key1 or Key2 or Both] + /*! + \param Key1 Address Key + \param Key2 String Key + \param Key2Len String Key Length + \return Hash result + */ + typedef int(*ILibHashtable_Hash_Func)(void* Key1, char* Key2, int Key2Len); + //! Event handler for when elements in the hashtable are freed + /*! + \param sender Hashtable that generated the request + \param Key1 Address Key + \param Key2 String Key + \param Key2Len String Key Length + \param Data Element Value + \param user Custom User State data + */ + typedef void(*ILibHashtable_OnDestroy)(ILibHashtable sender, void *Key1, char* Key2, int Key2Len, void *Data, void *user); + + ILibHashtable ILibHashtable_Create(); + + void ILibHashtable_DestroyEx(ILibHashtable table, ILibHashtable_OnDestroy onDestroy, void *user); + //! Free resources associated with Advanced Hashtable + /*! + \param hashtable Hashtable + */ + #define ILibHashtable_Destroy(hashtable) ILibHashtable_DestroyEx(hashtable, NULL, NULL) + void ILibHashtable_ChangeHashFunc(ILibHashtable table, ILibHashtable_Hash_Func hashFunc); + void ILibHashtable_ChangeBucketizer(ILibHashtable table, int bucketCount, ILibSparseArray_Bucketizer bucketizer); + void* ILibHashtable_Put(ILibHashtable table, void *Key1, char* Key2, int Key2Len, void* Data); + void* ILibHashtable_Get(ILibHashtable table, void *Key1, char* Key2, int Key2Len); + void* ILibHashtable_Remove(ILibHashtable table, void *Key1, char* Key2, int Key2Len); + void ILibHashtable_Clear(ILibHashtable table); + void ILibHashtable_ClearEx(ILibHashtable table, ILibHashtable_OnDestroy onClear, void *user); + void ILibHashtable_Enumerate(ILibHashtable table, ILibHashtable_OnDestroy onEnumerate, void *user); + void ILibHashtable_Lock(ILibHashtable table); + void ILibHashtable_UnLock(ILibHashtable table); + /*! + *@} + */ + + /*! \defgroup LinkedListGroup Linked List + \ingroup DataStructures + *@{ + */ + + typedef void* ILibLinkedList; + + /*! + \ingroup ChainGroup + *@{ + */ + + typedef struct ILibLinkedList_FileBacked_Root + { + unsigned int flags; + unsigned int maxSize; + unsigned int head; + unsigned int tail; + }ILibLinkedList_FileBacked_Root; +#ifdef WIN32 +#pragma warning( push ) +#pragma warning( disable : 4200 ) +#endif + typedef struct ILibLinkedList_FileBacked_Node + { + unsigned int next; + unsigned int dataLen; + char data[]; + }ILibLinkedList_FileBacked_Node; +#ifdef WIN32 +#pragma warning( pop ) +#endif + void ILibLinkedList_FileBacked_Close(ILibLinkedList_FileBacked_Root *root); + int ILibLinkedList_FileBacked_IsEmpty(ILibLinkedList_FileBacked_Root *root); + ILibLinkedList_FileBacked_Node* ILibLinkedList_FileBacked_ReadNext(ILibLinkedList_FileBacked_Root* root, ILibLinkedList_FileBacked_Node* current); + ILibLinkedList_FileBacked_Root* ILibLinkedList_FileBacked_Create(char* path, unsigned int maxFileSize, int maxRecordSize); + void ILibLinkedList_FileBacked_Reset(ILibLinkedList_FileBacked_Root *root); + int ILibLinkedList_FileBacked_AddTail(ILibLinkedList_FileBacked_Root* root, char* data, unsigned int dataLen); + int ILibLinkedList_FileBacked_ReloadRoot(ILibLinkedList_FileBacked_Root* root); + void ILibLinkedList_FileBacked_SaveRoot(ILibLinkedList_FileBacked_Root* root); + + + ILibLinkedList ILibChain_GetLinks(void *chain); + ILibHashtable ILibChain_GetBaseHashtable(void* chain); + #define ILibChain_Stash_Put(chain, key, value) ILibHashtable_Put(ILibChain_GetBaseHashtable(chain), NULL, key, strnlen_s(key, 255), value) + #define ILibChain_Stash_Get(chain, key) ILibHashtable_Get(ILibChain_GetBaseHashtable(chain), NULL, key, strnlen_s(key, 255)) + /*! @} */ + + + void* ILibLinkedList_Create(); + void* ILibLinkedList_CreateEx(int userMemorySize); + void ILibLinkedList_SetTag(ILibLinkedList list, void *tag); + void* ILibLinkedList_GetTag(ILibLinkedList list); + int ILibLinkedList_GetIndex(void *node); + + //! Comparer delegate is called to compare two values. Mimics behavior of .NET IComparer.. + //! obj2 == obj1 : 0 + //! obj2 < obj1 : -1 + //! obj2 > obj1 : +1 + /*! + \param obj1 Compare this object + \param obj2 with this object + \return Result [0: EQ, -1: LT, +1: GT] + */ + typedef int(*ILibLinkedList_Comparer)(void* obj1, void *obj2); + + //! Chooser handler is called when a new item is to be added to Linked List. The old value is passed in as well as the new value... The return value will be the designated added node + /*! + \param oldObject Current Element Value [NULL if it doesn't exist] + \param newObject Proposed Element Value + \param user Custom user state data + \return Element Value to be kept + */ + typedef void*(*ILibLinkedList_Chooser)(void* oldObject, void *newObject, void *user); + + void* ILibLinkedList_SortedInsert(void* LinkedList, ILibLinkedList_Comparer comparer, void *data); + void* ILibLinkedList_SortedInsertEx(void* LinkedList, ILibLinkedList_Comparer comparer, ILibLinkedList_Chooser chooser, void *data, void *user); + void* ILibLinkedList_GetNode_Search(void* LinkedList, ILibLinkedList_Comparer comparer, void *matchWith); + + void* ILibLinkedList_GetNode_Head(void *LinkedList); // Returns Node + void* ILibLinkedList_GetNode_Tail(void *LinkedList); // Returns Node + void* ILibLinkedList_GetNextNode(void *LinkedList_Node); // Returns Node + void* ILibLinkedList_GetPreviousNode(void *LinkedList_Node);// Returns Node + long ILibLinkedList_GetCount(void *LinkedList); + void* ILibLinkedList_ShallowCopy(void *LinkedList); + void *ILibLinkedList_GetDataFromNode(void *LinkedList_Node); + void* ILibLinkedList_InsertBefore(void *LinkedList_Node, void *data); + void* ILibLinkedList_InsertAfter(void *LinkedList_Node, void *data); + void* ILibLinkedList_Remove(void *LinkedList_Node); + int ILibLinkedList_Remove_ByData(void *LinkedList, void *data); + void* ILibLinkedList_AddHead(void *LinkedList, void *data); + void* ILibLinkedList_AddTail(void *LinkedList, void *data); + #define ILibLinkedList_Clear(LinkedList) while(ILibLinkedList_GetNode_Head(LinkedList)!=NULL){ILibLinkedList_Remove(ILibLinkedList_GetNode_Head(LinkedList));} + + void ILibLinkedList_Lock(void *LinkedList); + void ILibLinkedList_UnLock(void *LinkedList); + void ILibLinkedList_Destroy(void *LinkedList); + void* ILibLinkedList_GetExtendedMemory(void* LinkedList_Node); + /*! + *@} + */ + + + + /*! \defgroup HashTreeGroup Hash Table + \ingroup DataStructures + \b Note: Duplicate key entries will be overwritten. + *@{ + */ + + void* ILibInitHashTree(); + void* ILibInitHashTree_CaseInSensitive(); + void ILibDestroyHashTree(void *tree); + int ILibHasEntry(void *hashtree, char* key, int keylength); + void ILibAddEntry(void* hashtree, char* key, int keylength, void *value); + void ILibAddEntryEx(void* hashtree, char* key, int keylength, void *value, int valueEx); + void* ILibGetEntry(void *hashtree, char* key, int keylength); + ILibExportMethod void ILibGetEntryEx(void *hashtree, char* key, int keylength, void **value, int *valueEx); + void ILibDeleteEntry(void *hashtree, char* key, int keylength); + + void *ILibHashTree_GetEnumerator(void *tree); + void ILibHashTree_DestroyEnumerator(void *tree_enumerator); + int ILibHashTree_MoveNext(void *tree_enumerator); + + void ILibHashTree_GetValue(void *tree_enumerator, char **key, int *keyLength, void **data); + void ILibHashTree_GetValueEx(void *tree_enumerator, char **key, int *keyLength, void **data, int *dataEx); + void ILibHashTree_Lock(void *hashtree); + void ILibHashTree_UnLock(void *hashtree); + + /*! + *@} + */ + + /*! \defgroup LifeTimeMonitor LifeTimeMonitor + \ingroup ILibParsers + \brief Timed Callback Service + \par + These callbacks will always be triggered on the thread that calls ILibStartChain(). + \{ + */ + + typedef void(*ILibLifeTime_OnCallback)(void *obj); + + // + // Adds an event trigger to be called after the specified time elapses, with the + // specified data object + // +#define ILibLifeTime_Add(LifetimeMonitorObject, data, seconds, Callback, Destroy) ILibLifeTime_AddEx(LifetimeMonitorObject, data, seconds * 1000, Callback, Destroy) + void ILibLifeTime_AddEx(void *LifetimeMonitorObject,void *data, int milliseconds, ILibLifeTime_OnCallback Callback, ILibLifeTime_OnCallback Destroy); + + // + // Removes all event triggers that contain the specified data object. + // + void ILibLifeTime_Remove(void *LifeTimeToken, void *data); + + // + // Return the expiration time for an event + // + long long ILibLifeTime_GetExpiration(void *LifetimeMonitorObject, void *data); + + // + // Removes all events triggers + // + void ILibLifeTime_Flush(void *LifeTimeToken); + void *ILibCreateLifeTime(void *Chain); + long ILibLifeTime_Count(void* LifeTimeToken); + + /* \} */ + + + /*! \defgroup StringParsing String Parsing + \ingroup ILibParsers + \{ + */ + + int ILibFindEntryInTable(char *Entry, char **Table); + + + int ILibTrimString(char **theString, int length); + int ILibString_IndexOfFirstWhiteSpace(const char *inString, int inStringLength); + char* ILibString_Cat(const char *inString1, int inString1Len, const char *inString2, int inString2Len); + char* ILibString_Cat_s(char *destination, size_t destinationSize, char *source); + char *ILibString_Copy(const char *inString, int length); + int ILibString_Copy_s(char *destination, size_t destinationSize, char *source); + int ILibString_n_Copy_s(char *destination, size_t destinationSize, char *source, size_t count); + int ILibString_EndsWith(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength); + int ILibString_EndsWithEx(const char *inString, int inStringLength, const char *endWithString, int endWithStringLength, int caseSensitive); + int ILibString_StartsWith(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength); + int ILibString_StartsWithEx(const char *inString, int inStringLength, const char *startsWithString, int startsWithStringLength, int caseSensitive); + int ILibString_IndexOfEx(const char *inString, int inStringLength, const char *indexOf, int indexOfLength, int caseSensitive); + int ILibString_IndexOf(const char *inString, int inStringLength, const char *indexOf, int indexOfLength); + int ILibString_LastIndexOf(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength); + int ILibString_LastIndexOfEx(const char *inString, int inStringLength, const char *lastIndexOf, int lastIndexOfLength, int caseSensitive); + char *ILibString_Replace(const char *inString, int inStringLength, const char *replaceThis, int replaceThisLength, const char *replaceWithThis, int replaceWithThisLength); + char *ILibString_ToUpper(const char *inString, int length); + char *ILibString_ToLower(const char *inString, int length); + void ILibToUpper(const char *in, int inLength, char *out); + void ILibToLower(const char *in, int inLength, char *out); +#if !defined(WIN32) + #ifndef strcat_s + #define strcat_s(destination, destinationSize, source) ILibString_Cat_s(destination, destinationSize, source) + #endif + #ifndef strcpy_s + #define strcpy_s(destination, destinationSize, source) ILibString_Copy_s(destination, destinationSize, source); + #endif +#endif + + struct parser_result* ILibParseString (char* buffer, int offset, int length, const char* Delimiter, int DelimiterLength); + struct parser_result* ILibParseStringAdv (char* buffer, int offset, int length, const char* Delimiter, int DelimiterLength); + parser_result_field* ILibParseString_GetResultIndex(parser_result* r, int index); + void ILibDestructParserResults(struct parser_result *result); + + typedef enum ILibParseUriResult + { + ILibParseUriResult_UNKNOWN_SCHEME = 0, + ILibParseUriResult_NO_TLS = 1, + ILibParseUriResult_TLS = 2 + }ILibParseUriResult; + + ILibParseUriResult ILibParseUri(const char* URI, char** Addr, unsigned short* Port, char** Path, struct sockaddr_in6* AddrStruct); + + int ILibGetLong(char *TestValue, int TestValueLength, long* NumericValue); + int ILibGetULong(const char *TestValue, const int TestValueLength, unsigned long* NumericValue); + int ILibFragmentText(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength, char **RetVal); + int ILibFragmentTextLength(char *text, int textLength, char *delimiter, int delimiterLength, int tokenLength); + + + /* Base64 handling methods */ + int ILibBase64EncodeLength(const int inputLen); + int ILibBase64DecodeLength(const int inputLen); + int ILibBase64Encode(unsigned char* input, const int inputlen, unsigned char** output); + int ILibBase64Decode(unsigned char* input, const int inputlen, unsigned char** output); + + /* Compression Handling Methods */ + char* ILibDecompressString(unsigned char* CurrentCompressed, const int bufferLength, const int DecompressedLength); + + /* \} */ + + + /*! \defgroup PacketParsing Packet Parsing + \ingroup ILibParsers + \{ + */ + + /* Packet Methods */ + + struct packetheader *ILibCreateEmptyPacket(); + void ILibAddHeaderLine(struct packetheader *packet, const char* FieldName, int FieldNameLength, const char* FieldData, int FieldDataLength); + void ILibDeleteHeaderLine(struct packetheader *packet, char* FieldName, int FieldNameLength); + void ILibHTTPPacket_Stash_Put(ILibHTTPPacket *packet, char* key, int keyLen, void *data); + int ILibHTTPPacket_Stash_HasKey(ILibHTTPPacket *packet, char* key, int keyLen); + void* ILibHTTPPacket_Stash_Get(ILibHTTPPacket *packet, char* key, int keyLen); + + char* ILibUrl_GetHost(char *url, int urlLen); + #define ILibGetHeaderLine(packet, FieldName, FieldNameLength) ILibGetHeaderLineEx(packet, FieldName, FieldNameLength, NULL) + char* ILibGetHeaderLineEx(struct packetheader *packet, char* FieldName, int FieldNameLength, int *len); + char* ILibGetHeaderLineSP(struct packetheader *packet, char* FieldName, int FieldNameLength); + char* ILibGetHeaderLineSP_Next(char* PreviousValue, char* FieldName, int FieldNameLength); + void ILibSetVersion(struct packetheader *packet, char* Version, int VersionLength); + void ILibSetStatusCode(struct packetheader *packet, int StatusCode, char* StatusData, int StatusDataLength); + void ILibSetDirective(struct packetheader *packet, char* Directive, int DirectiveLength, char* DirectiveObj, int DirectiveObjLength); + void ILibDestructPacket(struct packetheader *packet); + struct packetheader* ILibParsePacketHeader(char* buffer, int offset, int length); + int ILibGetRawPacket(struct packetheader *packet,char **buffer); + struct packetheader* ILibClonePacket(struct packetheader *packet); + + int ILibHTTPEscapeEx(char* outdata, const char* indata, size_t indataLen); + int ILibHTTPEscapeLengthEx(const char* data, size_t dataLen); + #define ILibHTTPEscape(outdata, indata) ILibHTTPEscapeEx(outdata, indata, strnlen_s(indata, sizeof(ILibScratchPad))) + #define ILibHTTPEscapeLength(data) ILibHTTPEscapeLengthEx(data, strnlen_s(data, sizeof(ILibScratchPad))) + + int ILibInPlaceHTTPUnEscapeEx(char* data, int length); + #define ILibInPlaceHTTPUnEscape(data) ILibInPlaceHTTPUnEscapeEx(data, (int)strnlen_s(data, sizeof(ILibScratchPad))) + /* \} */ + + void* dbg_malloc(int sz); + void dbg_free(void* ptr); + int dbg_GetCount(); + + /*! \defgroup NetworkHelper Network Helper + \ingroup ILibParsers + *@{ + */ + + int ILibGetLocalIPv6IndexList(int** indexlist); + int ILibGetLocalIPv6List(struct sockaddr_in6** list); + int ILibGetLocalIPAddressList(int** pp_int); + int ILibGetLocalIPv4AddressList(struct sockaddr_in** addresslist, int includeloopback); + //int ILibGetLocalIPv6AddressList(struct sockaddr_in6** addresslist); + +#if defined(WINSOCK2) + int ILibGetLocalIPAddressNetMask(unsigned int address); +#endif + + SOCKET ILibGetSocket(struct sockaddr *localif, int type, int protocol); + + // + // IPv6 helper methods + // + void ILibMakeIPv6Addr(struct sockaddr *addr4, struct sockaddr_in6* addr6); + int ILibMakeHttpHeaderAddr(struct sockaddr *addr, char** str); + int ILibIsIPv4MappedAddr(struct sockaddr *addr); + int ILibIsLoopback(struct sockaddr *addr); + int ILibGetAddrBlob(struct sockaddr *addr, char** ptr); + void ILibGetAddrFromBlob(char* ptr, int len, unsigned short port, struct sockaddr_in6 *addr); + int ILibDetectIPv6Support(); + extern int g_ILibDetectIPv6Support; + char* ILibInet_ntop2(struct sockaddr* addr, char *dst, size_t dstsize); + char* ILibInet_ntop(int af, const void *src, char *dst, size_t dstsize); + int ILibInet_pton(int af, const char *src, void *dst); + int ILibInetCompare(struct sockaddr* addr1, struct sockaddr* addr2, int compare); // Compare=1 family only, 2 family and address, 3 familly, address and port. + int ILibResolve(char* hostname, char* service, struct sockaddr_in6* addr6); + int ILibResolveEx(char* hostname, unsigned short port, struct sockaddr_in6* addr6); + void ILib6to4(struct sockaddr* addr); + #define ILibInet_StructSize(addr) ((((struct sockaddr*)(addr))->sa_family == AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)) + /*! @} */ + // + // Used to log critical problems + // + char* ILibChain_Debug(void *chain, char* buffer, int bufferLen); + void ILibChain_DebugOffset(char *buffer, int bufferLen, uint64_t addrOffset); + +#if defined(WIN32) + int ILib_WindowsExceptionFilter(DWORD exceptionCode, void *exceptionInfo, CONTEXT *exceptionContext); + void ILib_WindowsExceptionDebug(CONTEXT *exceptionContext); +#elif defined(_POSIX) + char* ILib_POSIX_InstallCrashHandler(char *exename); +#endif + +#define ILIBCRITICALEXITMSG(code, msg) {printf("%s", ILibCriticalLog(msg, NULL, 0, 0, 0)); exit(code);} +#if defined(WIN32) +#define ILIBCRITICALERREXIT(ex) { ILibCriticalLog(NULL, __FILE__, __LINE__, GetLastError(), 0); exit(ex); } +#define ILIBCRITICALEXIT(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, GetLastError());printf("CRITICALEXIT, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__); exit(ex);} +#define ILIBCRITICALEXIT2(ex,u) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT2, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__); exit(ex);} +#define ILIBCRITICALEXIT3(ex,m,u) {ILibCriticalLog(m, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT3, FILE: %s, LINE: %d\r\nMessage: %s\r\n", __FILE__, __LINE__, m); exit(ex);} +#define ILIBMARKPOSITION(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, GetLastError());} +#define ILIBMESSAGE(m) {ILibCriticalLog(m, __FILE__, __LINE__, 0, GetLastError());printf("ILIBMSG: %s (%d).\r\n", m, (int)GetLastError());} +#define ILIBMESSAGE2(m,u) {ILibCriticalLog(m, __FILE__, __LINE__, u, GetLastError());printf("ILIBMSG: %s (%d,%d).\r\n", m, (int)u, (int)GetLastError());} +#else +extern void ILib_POSIX_CrashHandler(int code); +#define ILIBCRITICALERREXIT(ex) { ILibCriticalLog(NULL, __FILE__, __LINE__, errno, 0); fflush(stdout); exit(ex); } +#ifdef _POSIX +#define ILIBCRITICALEXIT(ex) ILib_POSIX_CrashHandler(ex); +#else +#define ILIBCRITICALEXIT(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, errno); printf("CRITICALEXIT, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__); fflush(stdout); exit(ex); } +#endif +#define ILIBCRITICALEXIT2(ex,u) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT2, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__);fflush(stdout); exit(ex);} +#define ILIBCRITICALEXIT3(ex,m,u) {ILibCriticalLog(m, __FILE__, __LINE__, ex, u); printf("CRITICALEXIT3, FILE: %s, LINE: %d\r\n", __FILE__, __LINE__);fflush(stdout); exit(ex);} +#define ILIBMARKPOSITION(ex) {ILibCriticalLog(NULL, __FILE__, __LINE__, ex, errno);} +#define ILIBMESSAGE(m) {ILibCriticalLog(m, __FILE__, __LINE__, 0, errno);printf("ILIBMSG: %s\r\n", m);fflush(stdout);} +#define ILIBMESSAGE2(m,u) {ILibCriticalLog(m, __FILE__, __LINE__, u, errno);printf("ILIBMSG: %s (%d)\r\n", m, u);fflush(stdout);} +#endif + + char* ILibCriticalLog(const char* msg, const char* file, int line, int user1, int user2); + + /*! \defgroup ThreadingHelper Threading Helper + \ingroup ILibParsers + *@{ + */ + void* ILibSpawnNormalThread(voidfp1 method, void* arg); + void ILibEndThisThread(); +#ifdef WIN32 + void ILibHandle_DisableInherit(HANDLE *h); +#endif + /*! @} */ + + + /*! \defgroup ConversionGroup Conversion Utilities + \ingroup ILibParsers + *@{ + */ + char* ILibToHex(char* data, int len, char* out); + int ILibWhichPowerOfTwo(int number); +#define ILibPowerOfTwo(exponent) (0x01 << exponent) + /*! @} */ +#ifdef __cplusplus +} +#endif + +/* \} */ // End of ILibParser Group +#endif + diff --git a/MicroLMS/microstack/ILibRemoteLogging.h b/MicroLMS/microstack/ILibRemoteLogging.h new file mode 100644 index 0000000..9092f05 --- /dev/null +++ b/MicroLMS/microstack/ILibRemoteLogging.h @@ -0,0 +1,95 @@ +/* INTEL CONFIDENTIAL + * Copyright 2011 - 2019 Intel Corporation. + * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. + * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. + */ + +#ifndef __ILIBREMOTELOGGING__ +#define __ILIBREMOTELOGGING__ + +#include "ILibParsers.h" + +/*! \defgroup ILibRemoteLogging ILibRemoteLogging Module +@{ +*/ +//! Module Types +typedef enum ILibRemoteLogging_Modules +{ + ILibRemoteLogging_Modules_UNKNOWN = 0x00, //!< UNKNOWN Module + ILibRemoteLogging_Modules_Logger = 0x01, //!< RESERVED: Logger + ILibRemoteLogging_Modules_WebRTC_STUN_ICE = 0x02, //!< WebRTC: STUN/ICE + ILibRemoteLogging_Modules_WebRTC_DTLS = 0x04, //!< WebRTC: DTLS + ILibRemoteLogging_Modules_WebRTC_SCTP = 0x08, //!< WebRTC: SCTP + ILibRemoteLogging_Modules_Agent_GuardPost = 0x10, //!< Mesh Agent: Guard Post + ILibRemoteLogging_Modules_Agent_P2P = 0x20, //!< Mesh Agent: Peer to Peer + ILibRemoteLogging_Modules_Agent_KVM = 0x200, //!< Mesh AGent: KVM + ILibRemoteLogging_Modules_Microstack_AsyncSocket = 0x40, //!< Microstack: AsyncSocket, AsyncServerSocket, AsyncUDPSocket + ILibRemoteLogging_Modules_Microstack_Web = 0x80, //!< Microstack: WebServer, WebSocket, WebClient + ILibRemoteLogging_Modules_Microstack_Pipe = 0x400,//!< Microstack: Pipe + ILibRemoteLogging_Modules_Microstack_Generic = 0x100,//!< Microstack: Generic + ILibRemoteLogging_Modules_ConsolePrint = 0x4000 +}ILibRemoteLogging_Modules; +//! Logging Flags +typedef enum ILibRemoteLogging_Flags +{ + ILibRemoteLogging_Flags_NONE = 0x00, //!< NONE + ILibRemoteLogging_Flags_DisableLogging = 0x01, //!< DISABLED + ILibRemoteLogging_Flags_VerbosityLevel_1 = 0x02, //!< Verbosity Level 1 + ILibRemoteLogging_Flags_VerbosityLevel_2 = 0x04, //!< Verbosity Level 2 + ILibRemoteLogging_Flags_VerbosityLevel_3 = 0x08, //!< Verbosity Level 3 + ILibRemoteLogging_Flags_VerbosityLevel_4 = 0x10, //!< Verbosity Level 4 + ILibRemoteLogging_Flags_VerbosityLevel_5 = 0x20, //!< Verbosity Level 5 +}ILibRemoteLogging_Flags; + +typedef enum ILibRemoteLogging_Command_Logger_Flags +{ + ILibRemoteLogging_Command_Logger_Flags_ENABLE = 0x100, //!< Enables/Disables File Logging + ILibRemoteLogging_Command_Logger_Flags_RESET_FILE = 0x200, //!< Erases the log file + ILibRemoteLogging_Command_Logger_Flags_READ_FILE = 0x400, //!< Reads the log file + ILibRemoteLogging_Command_Logger_Flags_RESET_FLAGS = 0x800, //!< Sets the Module/Flags to log to file +}ILibRemoteLogging_Command_Logger_Flags; + +#define ILibTransports_RemoteLogging_FileTransport 0x70 +typedef void* ILibRemoteLogging; +typedef void (*ILibRemoteLogging_OnWrite)(ILibRemoteLogging module, char* data, int dataLen, void *userContext); +typedef void (*ILibRemoteLogging_OnCommand)(ILibRemoteLogging sender, ILibRemoteLogging_Modules module, unsigned short flags, char* data, int dataLen, void *userContext); +typedef void(*ILibRemoteLogging_OnRawForward)(ILibRemoteLogging sender, ILibRemoteLogging_Modules module, ILibRemoteLogging_Flags flags, char *buffer, int bufferLen); + +#ifdef _REMOTELOGGING + char* ILibRemoteLogging_ConvertAddress(struct sockaddr* addr); + char* ILibRemoteLogging_ConvertToHex(char* inVal, int inValLength); + void ILibRemoteLogging_printf(ILibRemoteLogging loggingModule, ILibRemoteLogging_Modules module, ILibRemoteLogging_Flags flags, char* format, ...); + + ILibRemoteLogging ILibRemoteLogging_Create(ILibRemoteLogging_OnWrite onOutput); + ILibTransport* ILibRemoteLogging_CreateFileTransport(ILibRemoteLogging loggingModule, ILibRemoteLogging_Modules modules, ILibRemoteLogging_Flags flags, char* path, int pathLen); + void ILibRemoteLogging_Destroy(ILibRemoteLogging logger); + void ILibRemoteLogging_SetRawForward(ILibRemoteLogging logger, int bufferOffset, ILibRemoteLogging_OnRawForward onRawForward); + + void ILibRemoteLogging_DeleteUserContext(ILibRemoteLogging logger, void *userContext); + void ILibRemoteLogging_RegisterCommandSink(ILibRemoteLogging logger, ILibRemoteLogging_Modules module, ILibRemoteLogging_OnCommand sink); + int ILibRemoteLogging_Dispatch(ILibRemoteLogging loggingModule, char* data, int dataLen, void *userContext); + #define ILibRemoteLogging_ReadModuleType(data) ((ILibRemoteLogging_Modules)ntohs(((unsigned short*)data)[0])) + #define ILibRemoteLogging_ReadFlags(data) ((ILibRemoteLogging_Flags)ntohs(((unsigned short*)data)[1])) + int ILibRemoteLogging_IsModuleSet(ILibRemoteLogging loggingModule, ILibRemoteLogging_Modules module); + void ILibRemoteLogging_Forward(ILibRemoteLogging loggingModule, char* data, int dataLen); +#else + #define ILibRemoteLogging_ConvertAddress(...) ; + #define ILibRemoteLogging_ConvertToHex(...) ; + #define ILibRemoteLogging_printf(...) ; + #define ILibRemoteLogging_Create(...) NULL; + #define ILibRemoteLogging_SetRawForward(...) ; + #define ILibRemoteLogging_CreateFileTransport(...) NULL; + #define ILibRemoteLogging_Destroy(...) ; + #define ILibRemoteLogging_DeleteUserContext(...) ; + #define ILibRemoteLogging_RegisterCommandSink(...) ; + #define ILibRemoteLogging_Dispatch(...) ; + #define ILibRemoteLogging_ReadModuleType(data) ILibRemoteLogging_Modules_UNKNOWN + #define ILibRemoteLogging_ReadFlags(data) ILibRemoteLogging_Flags_NONE + #define ILibRemoteLogging_IsModuleSet(...) 0 + #define ILibRemoteLogging_Forward(...) ; +#endif + +/*! @} */ + +#endif + diff --git a/README.md b/README.md index e9cc75c..8400006 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +The Default ("master") branch is our release branch that is for production use. All other branches are pre-production and should not be used for production deployments. + # Remote Provisioning Client (RPC) RPC is an application which enables remote capabilities for AMT, such as as device activation. To accomplish this, RPC communicates with the RPS (Remote Provisioning Server). @@ -9,67 +11,77 @@ As a prerequisite, a Local Management Service (LMS) must be installed and runnin Steps below are for Ubuntu 18.04. ### Dependencies - -- sudo apt install git cmake build-essential libboost-system-dev libboost-thread-dev libboost-random-dev libboost-regex-dev libboost-filesystem-dev libssl-dev zlib1g-dev -- RPC requires CMake version 3.17. - - Please use "cmake --version" to check version. - - CMake be downloaded from . +``` +sudo apt install git cmake build-essential libboost-system-dev libboost-thread-dev libboost-random-dev libboost-regex-dev libboost-filesystem-dev libssl-dev zlib1g-dev +``` ### Build -- mkdir build -- cd build -- cmake -DCMAKE_BUILD_TYPE=Release .. - - Build debug: cmake -DCMAKE_BUILD_TYPE=Debug .. -- cmake --build . +``` +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +cmake --build . +``` + +To build debug: +``` +cmake -DCMAKE_BUILD_TYPE=Debug .. +``` ### Run -- See ./rpc --help for details. -- Example - - sudo ./rpc --url wss://localhost:8080 --profile profile1 +``` +sudo ./rpc --url wss://localhost:8080 --cmd "-t activate --profile profile1" +``` + +Use --help for more options. ## Windows Steps below are for Windows 10 and Visual Studio 2019 Professional. -### Dependencies - -- RPC requires CMake version 3.17, which is included with Visual Studio 2019. - - Please use "cmake --version" to check version. - - CMake can be downloaded from . - ### Build VCPKG Open an x64 native command prompt for Visual Studio 2019 as Administrator. -- git clone --branch 2020.01 https://github.com/microsoft/vcpkg.git -- cd vcpkg -- bootstrap-vcpkg.bat -- vcpkg integrate install +``` +git clone --branch 2020.01 https://github.com/microsoft/vcpkg.git +cd vcpkg +bootstrap-vcpkg.bat +vcpkg integrate install +``` ### Build C++ REST SDK Open an x64 native tools command prompt for Visual Studio 2019. - -- cd vcpkg -- vcpkg install cpprestsdk:x64-windows-static +``` +cd vcpkg +vcpkg install cpprestsdk:x64-windows-static +``` ### Build Open an x64 native tools command prompt for Visual Studio 2019. +``` +mkdir build +cd build +cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake +cmake --build . --config Release +``` -- mkdir build -- cd build -- cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -- cmake --build . --config Release - - Build debug: cmake --build . --config Debug +To build debug: +``` +cmake --build . --config Debug +``` ### Run Open a command prompt as Administrator. -- See rpc.exe --help for details. -- Example - - cd build\Release - - rpc.exe --url wss://localhost:8080 --profile profile1 +``` +cd build\Release +rpc.exe --url wss://localhost:8080 --cmd "-t activate --profile profile1" +``` + +Use --help for more options. diff --git a/activation.cpp b/activation.cpp new file mode 100644 index 0000000..59eed9d --- /dev/null +++ b/activation.cpp @@ -0,0 +1,321 @@ +/* +Copyright 2019 Intel Corporation + +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 "activation.h" +#include +#include +#include +#include +#include +#include "version.h" +#include "commands.h" +#include "network.h" +#include "utils.h" + +bool get_certificate_hashes(web::json::value& hashes) +{ + std::vector hashValues; + std::vector certHashes; + if (!cmd_get_certificate_hashes(certHashes)) + { + return false; + } + + for (std::string hashString : certHashes) + { + hashValues.push_back(web::json::value::string(utility::conversions::convertstring(hashString))); + } + + hashes = web::json::value::array(hashValues); + + return true; +} + +bool get_uuid(web::json::value& value) +{ + int i = 0; + std::vector uuidValue; + std::vector uuid; + + if (!cmd_get_uuid(uuid)) return false; + + for (unsigned char value : uuid) + { + uuidValue.push_back(web::json::value(uuid[i++])); + } + + value = web::json::value::array(uuidValue); + + return true; +} + +std::string get_dns_info() +{ + std::string dnsSuffix; + + // get DNS according to AMT + cmd_get_dns_suffix(dnsSuffix); + + if (!dnsSuffix.length()) + { + std::vector address; + cmd_get_wired_mac_address(address); + + if (address.size() == 6) + { + char macAddress[6]; + macAddress[0] = address[0]; + macAddress[1] = address[1]; + macAddress[2] = address[2]; + macAddress[3] = address[3]; + macAddress[4] = address[4]; + macAddress[5] = address[5]; + + // get DNS from OS + dnsSuffix = net_get_dns(macAddress); + } + } + + return dnsSuffix; +} + +web::json::value get_dns() +{ + utility::string_t tmp; + + std::string dnsSuffix = get_dns_info(); + tmp = utility::conversions::convertstring(dnsSuffix); + + return web::json::value::string(tmp); +} + +bool getVersion(web::json::value& value) +{ + std::string version; + utility::string_t tmp; + + if (!cmd_get_version(version)) return false; + + tmp = utility::conversions::convertstring(version); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_sku(web::json::value& value) +{ + std::string version; + utility::string_t tmp; + + if (!cmd_get_sku(version)) return false; + tmp = utility::conversions::convertstring(version); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_build_number(web::json::value& value) +{ + std::string version; + utility::string_t tmp; + + if (!cmd_get_build_number(version)) return false; + tmp = utility::conversions::convertstring(version); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_local_system_account_username(web::json::value& value) +{ + std::string username; + std::string password; + utility::string_t tmp; + + if (!cmd_get_local_system_account(username, password)) return false; + tmp = utility::conversions::convertstring(username); + + value = web::json::value::string(tmp); + + return true; +} + + bool get_local_system_account_password(web::json::value& value) +{ + std::string username; + std::string password; + utility::string_t tmp; + + if (!cmd_get_local_system_account(username, password)) return false; + tmp = utility::conversions::convertstring(password); + + value = web::json::value::string(tmp); + + return true; +} + +bool get_control_mode(web::json::value& value) +{ + int controlMode; + utility::string_t tmp; + + if (!cmd_get_control_mode(controlMode)) return false; + + value = web::json::value::number(controlMode); + + return true; +} + +bool get_client_string(web::json::value& value) +{ + int controlMode; + utility::string_t tmp; + + tmp = utility::conversions::convertstring("PPC"); + value = web::json::value::string(tmp); + + return true; +} + +bool get_activation_payload(web::json::value& payload) +{ + web::json::value value; + utility::string_t tmp; + web::json::value activationParams; + + // get code version + if (!getVersion(value)) return false; + activationParams[U("ver")] = value; + + if (!get_build_number(value)) return false; + activationParams[U("build")] = value; + + if (!get_sku(value)) return false; + activationParams[U("sku")] = value; + + // get UUID + if (!get_uuid(value)) return false; + activationParams[U("uuid")] = value; + + // get local system account + if (!get_local_system_account_username(value)) return false; + activationParams[U("username")] = value; + + if (!get_local_system_account_password(value)) return false; + activationParams[U("password")] = value; + + // get Control Mode + if (!get_control_mode(value)) return false; + activationParams[U("currentMode")] = value; + + // get DNS Info + activationParams[U("fqdn")] = get_dns(); + + // get client string + if (!get_client_string(value)) return false; + activationParams[U("client")] = value; + + // get certificate hashes + if (!get_certificate_hashes(value)) return false; + activationParams[U("certHashes")] = value; + + payload = activationParams; + + return true; +} + +bool act_create_request(std::string commands, std::string dns_suffix, std::string& request) +{ + web::json::value msg; + + // get the activation info + utility::string_t tmp = utility::conversions::convertstring(commands); + msg[U("method")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("key"); + msg[U("apiKey")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROJECT_VER); + msg[U("appVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROTOCOL_VERSION); + msg[U("protocolVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("status")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("message")] = web::json::value::string(tmp); + + // get the activation payload + web::json::value activationPayload; + if (!get_activation_payload(activationPayload)) return false; + + // override dns value if passed in + if (!dns_suffix.empty()) + { + utility::string_t tmp = utility::conversions::convertstring(dns_suffix); + activationPayload[U("fqdn")] = web::json::value::string(tmp); + } + + // serialize payload + std::string serializedPayload = utility::conversions::to_utf8string(activationPayload.serialize()); + std::string encodedPayload = util_encode_base64(serializedPayload); + utility::string_t payload = utility::conversions::to_string_t(encodedPayload); + msg[U("payload")] = web::json::value::string(payload); + +#ifdef DEBUG + std::cout << "Activation info payload:" << serializedPayload << std::endl; +#endif + + // serialize the entire message + request = utility::conversions::to_utf8string(msg.serialize()); + + return true; +} + +bool act_create_response(std::string payload, std::string& response) +{ + web::json::value msg; + + utility::string_t tmp = utility::conversions::convertstring("response"); + msg[U("method")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("key"); + msg[U("apiKey")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROJECT_VER); + msg[U("appVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(PROTOCOL_VERSION); + msg[U("protocolVersion")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("status")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring("ok"); + msg[U("message")] = web::json::value::string(tmp); + + tmp = utility::conversions::convertstring(util_encode_base64(payload)); + msg[U("payload")] = web::json::value::string(tmp); + + response = utility::conversions::to_utf8string(msg.serialize()); + + return true; +} diff --git a/activation.h b/activation.h new file mode 100644 index 0000000..86006dd --- /dev/null +++ b/activation.h @@ -0,0 +1,33 @@ +/* +Copyright 2019 Intel Corporation + +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 __ACTIVATION_H__ +#define __ACTIVATION_H__ + +#include + +#define PROTOCOL_VERSION "3.0.0" + +#ifdef _WIN32 +#define convertstring to_utf16string +#else +#define convertstring to_utf8string +#endif + +bool act_create_request(std::string commands, std::string dns_suffix, std::string& request); +bool act_create_response(std::string payload, std::string& response); + +#endif \ No newline at end of file diff --git a/args.cpp b/args.cpp new file mode 100644 index 0000000..bdf3d0f --- /dev/null +++ b/args.cpp @@ -0,0 +1,95 @@ +/* +Copyright 2019 Intel Corporation + +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 "args.h" +#include +#include "port.h" +#include "utils.h" + +bool get_arg_exists(int argc, char* argv[], const char* long_opt, const char* short_opt) +{ + for (int i = 1; i < argc; i++) + { + if ((strcasecmp(argv[i], long_opt) == 0) || (strcasecmp(argv[i], short_opt) == 0)) + { + return true; + } + } + + return false; +} + +bool get_arg_string(int argc, char* argv[], const char* long_opt, const char* short_opt, std::string& value) +{ + value = ""; + + for (int i = 1; i < argc; i++) + { + if ((strcasecmp(argv[i], long_opt) == 0) || (strcasecmp(argv[i], short_opt) == 0)) + { + if (i + 1 < argc) + { + std::string tmp = argv[++i]; + + if (!util_is_printable(tmp)) + { + return false; + } + + value = tmp; + + return true; + } + } + } + + return false; +} + +bool args_get_help(int argc, char* argv[]) +{ + return get_arg_exists(argc, argv, "--help", "-h"); +} + +bool args_get_version(int argc, char* argv[]) +{ + return get_arg_exists(argc, argv, "--version", "-v"); +} + +bool args_get_url(int argc, char* argv[], std::string& url) +{ + return get_arg_string(argc, argv, "--url", "-u", url); +} + +bool args_get_proxy(int argc, char* argv[], std::string& proxy) +{ + return get_arg_string(argc, argv, "--proxy", "-x", proxy); +} + +bool args_get_cmd(int argc, char* argv[], std::string& cmd) +{ + return get_arg_string(argc, argv, "--cmd", "-c", cmd); +} + +bool args_get_dns(int argc, char* argv[], std::string& dns) +{ + return get_arg_string(argc, argv, "--dns", "-d", dns); +} + +bool args_get_info(int argc, char* argv[], std::string& info) +{ + return get_arg_string(argc, argv, "--info", "-i", info); +} diff --git a/args.h b/args.h new file mode 100644 index 0000000..7fd005a --- /dev/null +++ b/args.h @@ -0,0 +1,30 @@ +/* +Copyright 2019 Intel Corporation + +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 __ARGS_H__ +#define __ARGS_H__ + +#include + +bool args_get_help(int argc, char* argv[]); +bool args_get_version(int argc, char* argv[]); +bool args_get_url(int argc, char* argv[], std::string& url); +bool args_get_proxy(int argc, char* argv[], std::string& proxy); +bool args_get_cmd(int argc, char* argv[], std::string& cmd); +bool args_get_dns(int argc, char* argv[], std::string& dns); +bool args_get_info(int argc, char* argv[], std::string& info); + +#endif diff --git a/commands.cpp b/commands.cpp index 43145f5..208e9fb 100644 --- a/commands.cpp +++ b/commands.cpp @@ -15,225 +15,245 @@ limitations under the License. */ #include "commands.h" - -#ifdef _WIN32 -#include -#include -#include -#endif - -#include "AMTHICommand.h" -#include "MEIClientException.h" -#include "GetUUIDCommand.h" -#include "GetLocalSystemAccountCommand.h" -#include "GetCodeVersionCommand.h" -#include "GetControlModeCommand.h" -#include "GetProvisioningStateCommand.h" -#include "GetDNSSuffixCommand.h" -#include "GetLanInterfaceSettingsCommand.h" -#include "GetCertificateHashEntryCommand.h" -#include "EnumerateHashHandlesCommand.h" -#include "MEIparser.h" -#include "version.h" -#include - -#include -#include -#include -#include -#include #include -#include "lms.h" +#include "port.h" -using namespace std; -using namespace Intel::MEI_Client::AMTHI_Client; -using namespace web::websockets::client; -using namespace web; - -#define WORKING_BUFFER_SIZE 15000 -#define MAX_TRIES 3 - - -#ifdef _WIN32 -std::string getDNSFromMAC(char *macAddress) -{ - std::string dnsSuffix = ""; - char dns[256]; - memset(dns, 0, 256); - - PIP_ADAPTER_ADDRESSES pAddresses = NULL; - DWORD dwSize = 0; - DWORD dwRetVal = 0; - ULONG outBufLen = 0; - ULONG Iterations = 0; - outBufLen = WORKING_BUFFER_SIZE; - - // get info for all adapters - do { - - pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); - if (pAddresses == NULL) { - cout << "dns memory error" << std::endl; - return dnsSuffix; - } - - dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen); - - if (dwRetVal == ERROR_BUFFER_OVERFLOW) - { - free(pAddresses); - pAddresses = NULL; - } - else - { - break; - } - - Iterations++; - - } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES)); - - // get DNS from MAC - PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; - pCurrAddresses = pAddresses; - while (pCurrAddresses) - { - if (pCurrAddresses->PhysicalAddressLength != 0) - { - if (memcmp(macAddress, (char *) pCurrAddresses->PhysicalAddress, 6) == 0) - { - if (wcslen(pCurrAddresses->DnsSuffix) > 0) - { - snprintf(dns, 256, "%ws", pCurrAddresses->DnsSuffix ); - break; - } - } - } - - pCurrAddresses = pCurrAddresses->Next; - } - - dnsSuffix = dns; - - return dnsSuffix; -} - -#else -std::string getDNSFromMAC(char *macAddress) -{ - std::string dnsSuffix = ""; - - // get socket - SOCKET s = 0; - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) - { - cout << "couldn't get socket" << endl; - return dnsSuffix; - } - - // get info for all adapters - struct ifconf ifc; - memset(&ifc, 0, sizeof(ifconf)); - char buffer[8192]; - memset(buffer, 0, sizeof(buffer)); - - ifc.ifc_buf = buffer; - ifc.ifc_len = sizeof(buffer); - if(ioctl(s, SIOCGIFCONF, &ifc) < 0) - { - cout << "ioctl SIOCGIFCONF failed" << endl; - return dnsSuffix; - } - - // get DNS from IP associated with MAC - struct ifreq *ifr = ifc.ifc_req; - int interfaceCount = ifc.ifc_len / sizeof(struct ifreq); - - char ip[INET6_ADDRSTRLEN] = {0}; - struct ifreq *item; - struct sockaddr *addr; - for(int i = 0; i < interfaceCount; i++) - { - item = &ifr[i]; - addr = &(item->ifr_addr); - - // get IP address - if(ioctl(s, SIOCGIFADDR, item) < 0) - { - cout << "ioctl SIOCGIFADDR failed" << endl; - continue; - } - - if (inet_ntop(AF_INET, &( ((struct sockaddr_in *)addr)->sin_addr ), - ip, sizeof(ip) ) == NULL) - { - cout << "inet_ntop" << endl; - continue; - } - - // get MAC address - if(ioctl(s, SIOCGIFHWADDR, item) < 0) - { - cout << "ioctl SIOCGIFHWADDR failed" << endl; - continue; - } - - if (memcmp(macAddress, (char *) item->ifr_hwaddr.sa_data, 6) == 0) - { - // Get host by using the IP address which AMT device is using - struct in_addr addr = {0}; - struct hostent *host; - addr.s_addr = inet_addr(ip); - host = gethostbyaddr((char *)&addr, 4, AF_INET); - if (host == NULL) - { - cout << "gethostbyaddr() failed"; - return dnsSuffix; - } - - // strip off the hostname to get actual domain name - int domainNameSize = 256; - char domainName[domainNameSize]; - memset(domainName, 0, domainNameSize); - - char *dn = strchr(host->h_name, '.'); - if (dn != NULL) - { - if (domainNameSize >= strlen(dn + 1)) - { - snprintf(domainName, domainNameSize, "%s", ++dn); - - dnsSuffix = domainName; - } - } - } - } - - return dnsSuffix; -} +#ifndef _WIN32 +#include #endif +extern "C" { +#ifndef _WIN32 + #include "HECILinux.h" +#endif + #include "PTHICommand.h" +#ifdef bool + #undef bool +#endif +} +#include "version.h" -json::value getCertificateHashes() +bool cmd_get_version(std::string& version) { - json::value certHashes; - vector hashValues; + version.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get code version + CODE_VERSIONS codeVersion; + AMT_STATUS status = pthi_GetCodeVersions(&codeVersion); + + // additional versions + if (status == 0) + { + for (int i = 0; i < (int) codeVersion.VersionsCount; i++) + { + if (strcmp((char*)(codeVersion.Versions[i].Description.String), "AMT") == 0) + { + version = ((char*)codeVersion.Versions[i].Version.String); + + return true; + } + } + } + + return false; +} + +bool cmd_get_build_number(std::string& version) +{ + version.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get code version + CODE_VERSIONS codeVersion; + AMT_STATUS status = pthi_GetCodeVersions(&codeVersion); + + // additional versions + if (status == 0) + { + for (int i = 0; i < (int) codeVersion.VersionsCount; i++) + { + if (strcmp((char*)(codeVersion.Versions[i].Description.String), "Build Number") == 0) + { + version = ((char*)codeVersion.Versions[i].Version.String); + + return true; + } + } + } + + return false; +} + +bool cmd_get_sku(std::string& version) +{ + version.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get code version + CODE_VERSIONS codeVersion; + AMT_STATUS status = pthi_GetCodeVersions(&codeVersion); + + // additional versions + if (status == 0) + { + for (int i = 0; i < (int) codeVersion.VersionsCount; i++) + { + if (strcmp((char*)(codeVersion.Versions[i].Description.String), "Sku") == 0) + { + version = ((char*)codeVersion.Versions[i].Version.String); + + return true; + } + } + } + + return false; +} + +bool cmd_get_uuid(std::vector& uuid) +{ + uuid.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get UUID + AMT_UUID amt_uuid; + AMT_STATUS amt_status = pthi_GetUUID(&amt_uuid); + if (amt_status == 0) + { + for (int i = 0; i < 16; i++) + { + uuid.push_back(amt_uuid[i]); + } + + return true; + } + + return false; +} + +bool cmd_get_local_system_account(std::string& username, std::string& password) +{ + username.clear(); + password.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get Local System Account + LOCAL_SYSTEM_ACCOUNT local_system_account; + AMT_STATUS amt_status = pthi_GetLocalSystemAccount(&local_system_account); + if (amt_status == 0) + { + username = local_system_account.username; + password = local_system_account.password; + + return true; + } + + return false; +} + +bool cmd_get_control_mode(int& mode) +{ + mode = 0; + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get Control Mode + int controlMode; + AMT_STATUS amt_status = pthi_GetControlMode(&controlMode); + if (amt_status == 0) + { + mode = controlMode; + + return true; + } + + return false; +} + +bool cmd_get_dns_suffix(std::string& suffix) +{ + suffix.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get DNS according to AMT + AMT_ANSI_STRING amt_dns_suffix; + AMT_STATUS amt_status = pthi_GetDnsSuffix(&amt_dns_suffix); + + if (amt_status == 0) + { + std::string tmp(amt_dns_suffix.Buffer, amt_dns_suffix.Length); + suffix = tmp; + + return true; + } + + return false; +} + +bool cmd_get_wired_mac_address(std::vector& address) +{ + address.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get wired interface + LAN_SETTINGS lan_settings; + UINT32 interface_settings = 0; // wired=0, wireless=1 + AMT_STATUS amt_status = pthi_GetLanInterfaceSettings(interface_settings, &lan_settings); + if (amt_status == 0) + { + if (!lan_settings.Enabled) + { + return false; + } + + for (int i = 0; i < 6; i++) + { + address.push_back(lan_settings.MacAddress[i]); + } + + return true; + } + + return false; +} + +bool cmd_get_certificate_hashes(std::vector& hashes) +{ + hashes.clear(); + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; // get the hash handles - EnumerateHashHandlesCommand command; - ENUMERATE_HASH_HANDLES_RESPONSE response = command.getResponse(); + AMT_HASH_HANDLES amt_hash_handles; + CERTHASH_ENTRY certhash_entry; - vector::iterator itr = response.HashHandles.begin(); - vector::iterator endItr = response.HashHandles.end(); - for (; itr != endItr; ++itr) + memset(&amt_hash_handles, 0, sizeof(AMT_HASH_HANDLES)); + if (pthi_EnumerateHashHandles(&amt_hash_handles) == 0) { - // get each entry - GetCertificateHashEntryCommand command(*itr); - GET_CERTIFICATE_HASH_ENTRY_RESPONSE response = command.getResponse(); + for (int i = 0; i < (int) amt_hash_handles.Length; i++) + { + // get each entry + AMT_STATUS status = pthi_GetCertificateHashEntry(amt_hash_handles.Handles[i], &certhash_entry); - int hashSize; - switch (response.HashAlgorithm) { + int hashSize; + switch (certhash_entry.HashAlgorithm) { case 0: // MD5 hashSize = 16; break; @@ -249,261 +269,25 @@ json::value getCertificateHashes() default: hashSize = 64; break; - } - - if (response.IsActive == 1) { - string hashString; - hashString.clear(); - for (int i = 0; i < hashSize; i++) - { - char hex[10]; - snprintf(hex, 10, "%02x", response.CertificateHash[i]); - hashString += hex; } - hashValues.push_back( json::value::string( utility::conversions::convertstring(hashString) ) ); + if (certhash_entry.IsActive == 1) + { + std::string hashString; + hashString.clear(); + for (int i = 0; i < hashSize; i++) + { + char hex[10]; + snprintf(hex, 10, "%02x", certhash_entry.CertificateHash[i]); + hashString += hex; + } + + hashes.push_back(hashString); + } } + + return true; } - return json::value::array(hashValues); + return false; } - -std::string getDNSInfo() -{ - std::string dnsSuffix; - - // Get interface info which AMT is using. We don't worry about wireless since - // only wired used for configuration - GetLanInterfaceSettingsCommand getLanInterfaceSettingsCommandWired(Intel::MEI_Client::AMTHI_Client::WIRED); - LAN_SETTINGS lanSettings = getLanInterfaceSettingsCommandWired.getResponse(); - - if (!lanSettings.Enabled) - { - cout << "error: no wired AMT interfaces enabled" << endl; - return ""; - } - - // Get DNS according to AMT - GetDNSSuffixCommand getDnsSuffixCommand; - dnsSuffix = getDnsSuffixCommand.getResponse(); - - // get DNS from OS - if (!dnsSuffix.length()) - { - dnsSuffix = getDNSFromMAC((char *)&lanSettings.MacAddress); - } - - return dnsSuffix; -} - - -string getActivateInfo(string profile, string dnssuffixcmd) -{ - utility::string_t tmp; - - // Activation parameters - json::value activationParams; - - // Get code version - GetCodeVersionCommand codeVersionCommand; - CODE_VERSIONS codeVersion = codeVersionCommand.getResponse(); - - // Additional versions - // UINT8[16] UUID; - // AMT_VERSION_TYPE Version and Description are std::string. - for (vector::iterator it = codeVersion.Versions.begin(); it != codeVersion.Versions.end(); it++) - { - if (boost::iequals(it->Description, "AMT")) - { - tmp = utility::conversions::convertstring(it->Version); - activationParams[U("ver")] = json::value::string(tmp); - } - else if (boost::iequals(it->Description, "Build Number")) - { - tmp = utility::conversions::convertstring(it->Version); - activationParams[U("build")] = json::value::string(tmp); - } - else if (boost::iequals(it->Description, "Sku")) - { - tmp = utility::conversions::convertstring(it->Version); - activationParams[U("sku")] = json::value::string(tmp); - } - } - - // Get UUID - GetUUIDCommand get; - GET_UUID_RESPONSE res = get.getResponse(); - std::vector UUID; - for (int i = 0; i < 16; i++) - { - UUID.push_back(json::value(res.UUID[i])); - } - activationParams[U("uuid")] = json::value::array(UUID); - - // Get local system account - // User name in ASCII char-set. The string is NULL terminated. CFG_MAX_ACL_USER_LENGTH is 33 - // Password in ASCII char set. From AMT 6.1 this field is in BASE64 format. The string is NULL terminated. - GetLocalSystemAccountCommand sac; - tmp = utility::conversions::convertstring(sac.getResponse().UserName); - activationParams[U("username")] = json::value::string(tmp); - tmp = utility::conversions::convertstring(sac.getResponse().Password); - activationParams[U("password")] = json::value::string(tmp); - - // Get Control Mode - GetControlModeCommand controlModeCommand; - GET_CONTROL_MODE_RESPONSE controlMode = controlModeCommand.getResponse(); - activationParams[U("currentMode")] = json::value::number(controlMode.ControlMode); - - // Get DNS Info - tmp = utility::conversions::convertstring(""); - string dnsSuffix = ""; - if (dnssuffixcmd.length() > 0) - { - // use what's passed in - dnsSuffix = dnssuffixcmd; - } - else - { - // get it from AMT or OS - dnsSuffix = getDNSInfo(); - } - - if (dnsSuffix.length()) - { - tmp = utility::conversions::convertstring(dnsSuffix); - } - - activationParams[U("fqdn")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring("PPC"); - activationParams[U("client")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring(profile); - activationParams[U("profile")] = json::value::string(tmp); - - // Get certificate hashes - activationParams[U("certHashes")] = getCertificateHashes(); - - // Return serialized parameters in base64 - string serializedParams = utility::conversions::to_utf8string(activationParams.serialize()); -#ifdef DEBUG - cout << "Activation info payload:" << serializedParams << std::endl; -#endif - - return encodeBase64(serializedParams); -} - -string encodeBase64(string str) -{ - std::vector strVector(str.begin(), str.end()); - utility::string_t base64 = utility::conversions::to_base64(strVector); - string encodedString = utility::conversions::to_utf8string(base64); - - return encodedString; -} - -string decodeBase64(string str) -{ - utility::string_t serializedData = utility::conversions::to_string_t(str); - std::vector strVector = utility::conversions::from_base64(serializedData); - string decodedString(strVector.begin(), strVector.end()); - - return decodedString; -} - -string createActivationRequest(string profile, string dnssuffixcmd) -{ - // Activation parameters - json::value request; - - // placeholder stuff; will likely change - utility::string_t tmp = utility::conversions::convertstring("activation"); - request[U("method")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring("key"); - request[U("apiKey")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring(PROJECT_VER); - request[U("appVersion")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring(PROTOCOL_VERSION); - request[U("protocolVersion")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring("ok"); - request[U("status")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring("all\'s good!"); - request[U("message")] = json::value::string(tmp); - - // payload - string activationInfo = getActivateInfo(profile, dnssuffixcmd); - utility::string_t payload = utility::conversions::to_string_t(activationInfo); - - request[U("payload")] = json::value::string(payload); - - return utility::conversions::to_utf8string(request.serialize()); -} - -string createResponse(string payload) -{ - // Activation parameters - json::value response; - - // placeholder stuff; will likely change - utility::string_t tmp = utility::conversions::convertstring("response"); - response[U("method")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring("key"); - response[U("apiKey")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring(PROJECT_VER); - response[U("appVersion")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring(PROTOCOL_VERSION); - response[U("protocolVersion")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring("ok"); - response[U("status")] = json::value::string(tmp); - - tmp = utility::conversions::convertstring("all\'s good!"); - response[U("message")] = json::value::string(tmp); - - // payload - tmp = utility::conversions::convertstring( encodeBase64(payload) ); - response[U("payload")] = json::value::string(tmp); - - return utility::conversions::to_utf8string(response.serialize()); -} - -void dumpMessage(utility::string_t tmp) -{ - web::json::value parsed = web::json::value::parse(tmp); - - if ( !parsed.has_field(U("method")) || !parsed.has_field(U("apiKey")) || !parsed.has_field(U("appVersion")) || - !parsed.has_field(U("protocolVersion")) || !parsed.has_field(U("status")) || !parsed.has_field(U("message")) || - !parsed.has_field(U("payload")) ) { - cout << "error: dumpMessage message is empty" << endl; - return; - } - - utility::string_t out = parsed[U("method")].as_string(); - cout << "method: " << utility::conversions::to_utf8string(out) << endl; - - out = parsed[U("apiKey")].as_string(); - cout << "apiKey: " << utility::conversions::to_utf8string(out) << endl; - - out = parsed[U("appVersion")].as_string(); - cout << "appVersion: " << utility::conversions::to_utf8string(out) << endl; - - out = parsed[U("protocolVersion")].as_string(); - cout << "protocolVersion: " << utility::conversions::to_utf8string(out) << endl; - - out = parsed[U("status")].as_string(); - cout << "status: " << utility::conversions::to_utf8string(out) << endl; - - out = parsed[U("message")].as_string(); - cout << "message: " << utility::conversions::to_utf8string(out) << endl; - - out = parsed[U("payload")].as_string(); - cout << "payload: " << utility::conversions::to_utf8string(out) << endl; -} \ No newline at end of file diff --git a/commands.h b/commands.h index 25a806b..614ef30 100644 --- a/commands.h +++ b/commands.h @@ -17,32 +17,17 @@ limitations under the License. #ifndef __COMMANDS_H__ #define __COMMANDS_H__ -#include +#include #include -#include -#include -#include - -using namespace std; -using namespace web::websockets::client; -using namespace web; - -#define PROTOCOL_VERSION "2.0.0" - -#ifdef _WIN32 -#define convertstring to_utf16string -#else -#define convertstring to_utf8string -#endif - -string getDNSInfo(); -string createActivationRequest(string profile, string dnssuffixcmd); -json::value getCertificateHashes(); -string createResponse(string payload); -string getActivateInfo(string profile, string dnssuffixcmd); -string encodeBase64(string str); -string decodeBase64(string str); -void dumpMessage(string tmp); +bool cmd_get_version(std::string& version); +bool cmd_get_build_number(std::string& version); +bool cmd_get_sku(std::string& version); +bool cmd_get_uuid(std::vector& uuid); +bool cmd_get_local_system_account(std::string& username, std::string& password); +bool cmd_get_control_mode(int& mode); +bool cmd_get_dns_suffix(std::string& suffix); +bool cmd_get_wired_mac_address(std::vector& address); +bool cmd_get_certificate_hashes(std::vector& hashes); #endif \ No newline at end of file diff --git a/info.cpp b/info.cpp new file mode 100644 index 0000000..814bebb --- /dev/null +++ b/info.cpp @@ -0,0 +1,217 @@ +/* +Copyright 2019 Intel Corporation + +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 "info.h" +#include +#include +#include +#include "commands.h" +#include "utils.h" + +void out_text(const std::string name, const std::vector value, const unsigned char delimeter=' ') +{ + std::cout << name << ": "; + int char_count = 1; + for (unsigned char tmp : value) + { + std::cout << std::setfill('0') << std::setw(2) << std::hex << (unsigned int) tmp; + + if (char_count++ < value.size()) + { + std::cout << delimeter; + } + } + + std::cout << std::endl; +} + +void out_text(const std::string name, const std::string value) +{ + std::cout << name << ": "; + std::cout << value; + std::cout << std::endl; +} + +void out_text(const std::string name, const int value) +{ + std::cout << name << ": "; + std::cout << value; + std::cout << std::endl; +} + +void out_text(const std::string name, const std::vector value) +{ + int count = 1; + + std::cout << name << std::endl; + for (std::string tmp : value) + { + std::cout << std::setfill('0') << std::setw(2) << count++ << ":"; + std::cout << tmp << std::endl; + } +} + +bool info_get_version() +{ + std::string tmp; + + if (!cmd_get_version(tmp)) return false; + + out_text("Version", tmp); + + return true; +} + +bool info_get_build_number() +{ + std::string tmp; + + if (!cmd_get_build_number(tmp)) return false; + + out_text("Build number", tmp); + + return true; +} + +bool info_get_sku() +{ + std::string tmp; + + if (!cmd_get_sku(tmp)) return false; + + out_text("SKU", tmp); + + return true; +} + +bool info_get_uuid() +{ + std::string uuid_string; + std::vector tmp; + + if (!cmd_get_uuid(tmp)) return false; + + if (!util_format_uuid(tmp, uuid_string)) return false; + + out_text("UUID", uuid_string); + + return true; +} + +bool info_get_control_mode() +{ + int tmp; + + if (!cmd_get_control_mode(tmp)) return false; + + std::string control_mode; + if (tmp == 0) control_mode = "pre-provisioning state"; + else if (tmp == 1) control_mode = "activated in client control mode"; + else if (tmp == 2) control_mode = "activated in admin control mode"; + + out_text("Control mode", control_mode); + + return true; +} + +bool info_get_dns_suffix() +{ + std::string tmp; + + if (!cmd_get_dns_suffix(tmp)) return false; + + out_text("DNS suffix", tmp); + + return true; +} + +bool info_get_wired_mac_address() +{ + std::vector tmp; + + if (!cmd_get_wired_mac_address(tmp)) return false; + + out_text("Wired MAC address", tmp, '-'); + + return true; +} + +bool info_get_certificate_hashes() +{ + std::vector tmp; + + if (!cmd_get_certificate_hashes(tmp)) return false; + + out_text("Certificate hashes", tmp); + + return true; +} + +bool info_get_all() +{ + std::vector tmp; + + if (info_get_version() && info_get_build_number() && info_get_sku() && + info_get_uuid() && info_get_control_mode() && info_get_dns_suffix() && + info_get_wired_mac_address() && info_get_certificate_hashes()) + { + return true; + } + + return true; +} + +bool info_get(const std::string info) +{ + if (info.compare("ver") == 0) + { + return info_get_version(); + } + else if (info.compare("build") == 0) + { + return info_get_build_number(); + } + else if (info.compare("sku") == 0) + { + return info_get_sku(); + } + else if (info.compare("uuid") == 0) + { + return info_get_uuid(); + } + else if (info.compare("mode") == 0) + { + return info_get_control_mode(); + } + else if (info.compare("dns") == 0) + { + return info_get_dns_suffix(); + } + else if (info.compare("mac") == 0) + { + return info_get_wired_mac_address(); + } + else if (info.compare("cert") == 0) + { + return info_get_certificate_hashes(); + } + else if (info.compare("all") == 0) + { + return info_get_all(); + } + + return false; +} diff --git a/info.h b/info.h new file mode 100644 index 0000000..0157882 --- /dev/null +++ b/info.h @@ -0,0 +1,32 @@ +/* +Copyright 2019 Intel Corporation + +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 __INFO_H__ +#define __INFO_H__ + +#include + +bool info_get(const std::string info); +bool info_get_version(); +bool info_get_build_number(); +bool info_get_sku(); +bool info_get_uuid(); +bool info_get_control_mode(); +bool info_get_dns_suffix(); +bool info_get_wired_mac_address(); +bool info_get_all(); + +#endif diff --git a/lms.cmake.in b/lms.cmake.in deleted file mode 100644 index 950ada4..0000000 --- a/lms.cmake.in +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.1) - -project(lms-download NONE) - -include(ExternalProject) -ExternalProject_Add(lms - GIT_REPOSITORY https://github.com/intel/lms.git - GIT_TAG v1932.0.0.0 - SOURCE_DIR "${CMAKE_BINARY_DIR}/lms-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/lms-build" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -) \ No newline at end of file diff --git a/lms.cpp b/lms.cpp index 53724a9..f4971c5 100644 --- a/lms.cpp +++ b/lms.cpp @@ -26,7 +26,7 @@ limitations under the License. #include #endif -SOCKET lmsConnect() +SOCKET lms_connect() { std::string lmsAddress = "localhost"; std::string lmsPort = "16992"; diff --git a/lms.h b/lms.h index 85a219c..e28c6c7 100644 --- a/lms.h +++ b/lms.h @@ -39,6 +39,6 @@ static inline int closesocket(int fd) #define SD_BOTH SHUT_RDWR #endif -SOCKET lmsConnect(); +SOCKET lms_connect(); #endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 4b12c94..c0dcef2 100644 --- a/main.cpp +++ b/main.cpp @@ -14,54 +14,45 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include -#include -#include #include #include -#include -#include -#include "commands.h" -#include "lms.h" -#include "version.h" - -using namespace std; -using namespace web; -using namespace web::websockets::client; - +#include #include +#include "port.h" +#include "lms.h" +#include "commands.h" +#include "activation.h" +#include "utils.h" +#include "usage.h" +#include "args.h" +#include "info.h" -void showUsage(); -bool isPrintable(std::string str); +// timer thread globals +long long g_timeout_val = 0; +const int g_timeout_max = 10; +bool g_thread_alive = true; -string websocket_address = ""; -string server_profile = ""; -string websocket_proxy = ""; -string dns_suffix = ""; - -long long timeoutTimer = 0; -const int MAX_TIMEOUT = 10; // seconds -bool timeoutThreadAlive = true; - -void timeoutFunc(std::condition_variable *cv, std::mutex *mx) +// timeout thread function +// used to exit application in case a timeout occurs +void timeout_thread_function(std::condition_variable *cv, std::mutex *mx) { - while (timeoutThreadAlive) + while (g_thread_alive) { std::chrono::time_point now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); long long currTime = std::chrono::duration_cast(duration).count(); - if (currTime > timeoutTimer) + if (currTime > g_timeout_val) { - if (currTime - timeoutTimer >= MAX_TIMEOUT) + if (currTime - g_timeout_val >= g_timeout_max) { cv->notify_all(); // check if timeoutTimer is not 0 since we explicitly set to zero when an // activation is successfull. If it's not zero, we are in a time out scenario. - if (timeoutTimer) + if (g_timeout_val) { - cout << endl << "Timed-out due to inactivity." < now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); - timeoutTimer = std::chrono::duration_cast(duration).count(); + g_timeout_val = std::chrono::duration_cast(duration).count(); try { - // handle message from server... - string rcv_websocket_msg = ret_msg.extract_string().get(); + // handle message from server + std::string rcv_websocket_msg = ret_msg.extract_string().get(); #ifdef DEBUG - cout << endl << "<<<<< Received Message " << endl; - cout << rcv_websocket_msg << endl; + std::cout << std::endl << "<<<<< Received Message " << std::endl; + std::cout << rcv_websocket_msg << std::endl; #endif - cout << "." << std::flush; // dot status output + std::cout << "." << std::flush; // dot status output // parse incoming JSON message utility::string_t tmp = utility::conversions::convertstring(rcv_websocket_msg); web::json::value parsed = web::json::value::parse(tmp); utility::string_t out = U(""); - string msgMethod = ""; - string msgApiKey = ""; - string msgAppVersion = ""; - string msgProtocolVersion = ""; - string msgStatus = ""; - string msgMessage = ""; - string msgPayload = ""; - string payloadDecoded = ""; + std::string msgMethod = ""; + std::string msgApiKey = ""; + std::string msgAppVersion = ""; + std::string msgProtocolVersion = ""; + std::string msgStatus = ""; + std::string msgMessage = ""; + std::string msgPayload = ""; + std::string payloadDecoded = ""; if ( !parsed.has_field(U("method")) || !parsed.has_field(U("apiKey")) || !parsed.has_field(U("appVersion")) || !parsed.has_field(U("protocolVersion")) || !parsed.has_field(U("status")) || !parsed.has_field(U("message")) || !parsed.has_field(U("payload")) ) { - std::cerr << endl << "Received incorrectly formatted message." << endl; + std::cerr << std::endl << "Received incorrectly formatted message." << std::endl; cv.notify_all(); - timeoutThreadAlive = false; + g_thread_alive = false; return; } @@ -282,35 +231,35 @@ int main(int argc, char *argv[]) } catch (...) { - std::cerr << endl << "Received message parse error." << endl; + std::cerr << std::endl << "Received message parse error." << std::endl; return; } #ifdef DEBUG - cout << msgMethod << ", " << msgStatus << ", " << msgMessage << endl; - cout << rcv_websocket_msg << endl; + std::cout << msgMethod << ", " << msgStatus << ", " << msgMessage << std::endl; + std::cout << rcv_websocket_msg << std::endl; #endif // process any messages we can // - if success, done // - if error, get out - if (boost::iequals(msgMethod, "success")) + if (msgMethod.compare("success")==0) { // cleanup - timeoutTimer = 0; + g_timeout_val = 0; // exit - cout << endl << msgMessage << endl; + std::cout << std::endl << msgMessage << std::endl; return; } - else if (boost::iequals(msgMethod, "error")) + else if (msgMethod.compare("error")==0) { // cleanup - timeoutTimer = 0; + g_timeout_val = 0; // exit - cout << endl << msgMessage << endl; + std::cout << std::endl << msgMessage << std::endl; return; } @@ -325,7 +274,7 @@ int main(int argc, char *argv[]) msgPayload = utility::conversions::to_utf8string(out); // decode payload - payloadDecoded = decodeBase64(msgPayload); + payloadDecoded = util_decode_base64(msgPayload); } else { @@ -335,24 +284,24 @@ int main(int argc, char *argv[]) } catch (...) { - std::cerr << endl << "JSON format error. Unable to parse message." << endl; + std::cerr << std::endl << "JSON format error. Unable to parse message." << std::endl; return; } try { // conntect to lms - s = lmsConnect(); + s = lms_connect(); } catch (...) { - std::cerr << endl << "Unable to connect to Local Management Service (LMS). Please ensure LMS is running." << endl; + std::cerr << std::endl << "Unable to connect to Local Management Service (LMS). Please ensure LMS is running." << std::endl; return; } #ifdef DEBUG - cout << endl << "vvvvv Sending Message " << endl; - cout << payloadDecoded << endl; + std::cout << std::endl << "vvvvv Sending Message " << std::endl; + std::cout << payloadDecoded << std::endl; #endif // send message to LMS @@ -375,7 +324,7 @@ int main(int argc, char *argv[]) // read until connection is closed by LMS while (1) { - string superBuffer = ""; + std::string superBuffer = ""; while (1) { int res = select(fd, &rset, NULL, NULL, &timeout); @@ -394,8 +343,8 @@ int main(int argc, char *argv[]) if (res > 0) { #ifdef DEBUG - cout << endl << "^^^^^ Received Message" << endl; - cout << recv_buffer << endl; + std::cout << std::endl << "^^^^^ Received Message" << std::endl; + std::cout << recv_buffer << std::endl; #endif superBuffer += recv_buffer; } @@ -406,9 +355,8 @@ int main(int argc, char *argv[]) } else { - // case where res is zero bytes - // discussion below, but select returns 1 with recv returning 0 to indicate close - // https://stackoverflow.com/questions/2992547/waiting-for-data-via-select-not-working + // case where res is zero bytes, select returns 1 + // with recv returning 0 to indicate close break; } } // while select() @@ -416,14 +364,16 @@ int main(int argc, char *argv[]) // if there is some data send it if (superBuffer.length() > 0) { - string response = createResponse(superBuffer.c_str()); - websocket_outgoing_message send_websocket_msg; - string send_websocket_buffer(response); + std::string response; + if (!act_create_response(superBuffer.c_str(), response)) return; + + web::websockets::client::websocket_outgoing_message send_websocket_msg; + std::string send_websocket_buffer(response); send_websocket_msg.set_utf8_message(send_websocket_buffer); #ifdef DEBUG - cout << endl << ">>>>> Sending Message" << endl; - cout << superBuffer << endl; - cout << send_websocket_buffer << endl; + std::cout << std::endl << ">>>>> Sending Message" << std::endl; + std::cout << superBuffer << std::endl; + std::cout << send_websocket_buffer << std::endl; #endif client.send(send_websocket_msg).wait(); @@ -437,13 +387,13 @@ int main(int argc, char *argv[]) } catch (...) { - std::cerr << endl << "Communication error in receive handler." << endl; + std::cerr << std::endl << "Communication error in receive handler." << std::endl; closesocket(s); } }); // set close handler - client.set_close_handler([&mx,&cv](websocket_close_status status, const utility::string_t &reason, const std::error_code &code) + client.set_close_handler([&mx,&cv](web::websockets::client::websocket_close_status status, const utility::string_t &reason, const std::error_code &code) { // websocket closed by server, notify main thread cv.notify_all(); @@ -451,37 +401,37 @@ int main(int argc, char *argv[]) try { - // Connect to web socket server; AMT activation server - client.connect(utility::conversions::to_string_t(websocket_address)).wait(); + // connect to web socket server; AMT activation server + client.connect(utility::conversions::to_string_t(arg_url)).wait(); } catch (...) { - std::cerr << "Unable to connect to websocket server. Please check url." << endl; + std::cerr << "Unable to connect to websocket server. Please check url." << std::endl; exit(1); } try { - // Send activationParams to websocket - websocket_outgoing_message out_msg; - out_msg.set_utf8_message(activationInfo); + // send activationParams to websocket + web::websockets::client::websocket_outgoing_message out_msg; + out_msg.set_utf8_message(activation_info); #ifdef DEBUG - cout << endl << ">>>>> Sending Activiation Info" << endl; - cout << activationInfo << endl; + std::cout << std::endl << ">>>>> Sending Activiation Info" << std::endl; + std::cout << activation_info << std::endl; #endif client.send(out_msg).wait(); } catch (...) { - std::cerr << endl << "Unable to send message to websocket server." << endl; + std::cerr << std::endl << "Unable to send message to websocket server." << std::endl; exit(1); } std::chrono::time_point now = std::chrono::system_clock::now(); auto duration = now.time_since_epoch(); - timeoutTimer = std::chrono::duration_cast(duration).count(); - std::thread timeoutThread(timeoutFunc, &cv, &mx); + g_timeout_val = std::chrono::duration_cast(duration).count(); + std::thread timeoutThread(timeout_thread_function, &cv, &mx); // wait for server to send success/failure command std::unique_lock lock(mx); @@ -502,32 +452,3 @@ int main(int argc, char *argv[]) exit(0); } -bool isPrintable(std::string str) -{ - for (char c : str) - { - if (!std::isprint(c)) - { - return false; - } - } - - return true; -} - -void showUsage() -{ - cout << "Usage: " << PROJECT_NAME << " --url --profile [--proxy ]" << endl; - cout << "Required:" << endl; - cout << " -u, --url websocket server" << endl; - cout << " -p, --profile server profile" << endl; - cout << "Optional:" << endl; - cout << " -x, --proxy proxy address and port" << endl; - cout << " -d, --dns dns suffix" << endl; - cout << endl; - cout << "Examples:" << endl; - cout << " " << PROJECT_NAME << " --url wss://localhost --profile profile1" << endl; - cout << " " << PROJECT_NAME << " -u wss://localhost --profile profile1 --proxy http://proxy.com:1000" << endl; - cout << " " << PROJECT_NAME << " -u wss://localhost -p profile1 -x http://proxy.com:1000" << endl; - cout << endl; -} diff --git a/network.cpp b/network.cpp new file mode 100644 index 0000000..efa4940 --- /dev/null +++ b/network.cpp @@ -0,0 +1,196 @@ +/* +Copyright 2019 Intel Corporation + +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 "network.h" +#include + +#ifdef _WIN32 +#include +#include +#define WORKING_BUFFER_SIZE 15000 +#define MAX_TRIES 3 +#else +#include +#include +#include +#include +#include +#include +#include +#include +typedef int SOCKET; +#endif + +#ifdef _WIN32 +std::string net_get_dns(char* macAddress) +{ + std::string dnsSuffix = ""; + char dns[256]; + memset(dns, 0, 256); + + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + DWORD dwSize = 0; + DWORD dwRetVal = 0; + ULONG outBufLen = 0; + ULONG Iterations = 0; + outBufLen = WORKING_BUFFER_SIZE; + + // get info for all adapters + do { + + pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); + if (pAddresses == NULL) { + std::cout << "dns memory error" << std::endl; + return dnsSuffix; + } + + dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen); + + if (dwRetVal == ERROR_BUFFER_OVERFLOW) + { + free(pAddresses); + pAddresses = NULL; + } + else + { + break; + } + + Iterations++; + + } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES)); + + // get DNS from MAC + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + pCurrAddresses = pAddresses; + while (pCurrAddresses) + { + if (pCurrAddresses->PhysicalAddressLength != 0) + { + if (memcmp(macAddress, (char*)pCurrAddresses->PhysicalAddress, 6) == 0) + { + if (wcslen(pCurrAddresses->DnsSuffix) > 0) + { + snprintf(dns, 256, "%ws", pCurrAddresses->DnsSuffix); + break; + } + } + } + + pCurrAddresses = pCurrAddresses->Next; + } + + dnsSuffix = dns; + + return dnsSuffix; +} + +#else +std::string net_get_dns(char* macAddress) +{ + std::string dnsSuffix = ""; + + // get socket + SOCKET s = 0; + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + std::cout << "couldn't get socket" << std::endl; + return dnsSuffix; + } + + // get info for all adapters + struct ifconf ifc; + memset(&ifc, 0, sizeof(ifconf)); + char buffer[8192]; + memset(buffer, 0, sizeof(buffer)); + + ifc.ifc_buf = buffer; + ifc.ifc_len = sizeof(buffer); + if (ioctl(s, SIOCGIFCONF, &ifc) < 0) + { + std::cout << "ioctl SIOCGIFCONF failed" << std::endl; + return dnsSuffix; + } + + // get DNS from IP associated with MAC + struct ifreq* ifr = ifc.ifc_req; + int interfaceCount = ifc.ifc_len / sizeof(struct ifreq); + + char ip[INET6_ADDRSTRLEN] = { 0 }; + struct ifreq* item; + struct sockaddr* addr; + for (int i = 0; i < interfaceCount; i++) + { + item = &ifr[i]; + addr = &(item->ifr_addr); + + // get IP address + if (ioctl(s, SIOCGIFADDR, item) < 0) + { + std::cout << "ioctl SIOCGIFADDR failed" << std::endl; + continue; + } + + if (inet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), + ip, sizeof(ip)) == NULL) + { + std::cout << "inet_ntop" << std::endl; + continue; + } + + // get MAC address + if (ioctl(s, SIOCGIFHWADDR, item) < 0) + { + std::cout << "ioctl SIOCGIFHWADDR failed" << std::endl; + continue; + } + + if (memcmp(macAddress, (char*)item->ifr_hwaddr.sa_data, 6) == 0) + { + // Get host by using the IP address which AMT device is using + struct in_addr addr = { 0 }; + struct hostent* host; + addr.s_addr = inet_addr(ip); + host = gethostbyaddr((char*)&addr, 4, AF_INET); + if (host == NULL) + { + std::cout << "gethostbyaddr() failed"; + return dnsSuffix; + } + + // strip off the hostname to get actual domain name + int domainNameSize = 256; + char domainName[domainNameSize]; + memset(domainName, 0, domainNameSize); + + char* dn = strchr(host->h_name, '.'); + if (dn != NULL) + { + if (domainNameSize >= strlen(dn + 1)) + { + snprintf(domainName, domainNameSize, "%s", ++dn); + + dnsSuffix = domainName; + } + } + } + } + + return dnsSuffix; +} + +#endif \ No newline at end of file diff --git a/network.h b/network.h new file mode 100644 index 0000000..8d014f4 --- /dev/null +++ b/network.h @@ -0,0 +1,24 @@ +/* +Copyright 2019 Intel Corporation + +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 __NETWORK_H__ +#define __NETWORK_H__ + +#include + +std::string net_get_dns(char* macAddress); + +#endif \ No newline at end of file diff --git a/port.h b/port.h new file mode 100644 index 0000000..8ee9651 --- /dev/null +++ b/port.h @@ -0,0 +1,37 @@ +/* +Copyright 2019 Intel Corporation + +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 __PORT_H__ +#define __PORT_H__ + +#include + +extern "C" +{ + // main entry into microlms + extern int main_micro_lms(); +} + + +#ifdef _WIN32 +// Windows +#define strncpy strncpy_s +#define strcasecmp _stricmp +#else +// Linux +#endif + +#endif diff --git a/usage.cpp b/usage.cpp new file mode 100644 index 0000000..21a7703 --- /dev/null +++ b/usage.cpp @@ -0,0 +1,61 @@ +/* +Copyright 2019 Intel Corporation + +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 "usage.h" +#include +#include "version.h" +#include "activation.h" + +void usage_show_help() +{ + std::cout << "Usage: " << PROJECT_NAME << " --help | --version | --info | --url --cmd [--proxy ] [--dns ]" << std::endl; + std::cout << "Required:" << std::endl; + std::cout << " -u, --url websocket server" << std::endl; + std::cout << " -c, --cmd server command" << std::endl; + std::cout << "Optional:" << std::endl; + std::cout << " -x, --proxy proxy address and port" << std::endl; + std::cout << " -d, --dns dns suffix" << std::endl; + std::cout << "Informational:" << std::endl; + std::cout << " -h, --help this help text" << std::endl; + std::cout << " -v, --version version" << std::endl; + std::cout << " -i, --info info on an " << std::endl; + std::cout << std::endl; + std::cout << "Info :" << std::endl; + std::cout << " all all items" << std::endl; + std::cout << " ver bios version" << std::endl; + std::cout << " build build number" << std::endl; + std::cout << " sku product sku" << std::endl; + std::cout << " uuid unique identifier" << std::endl; + std::cout << " mode current control mode {0=none, 1=client, 2=admin}" << std::endl; + std::cout << " dns domain name suffix" << std::endl; + std::cout << " mac wired MAC address" << std::endl; + std::cout << " cert certificate hashes" << std::endl; + std::cout << std::endl; + std::cout << "Examples:" << std::endl; + std::cout << " " << PROJECT_NAME << " --url wss://localhost:8080 --cmd \"-t activate --profile profile1\"" << std::endl; + std::cout << " " << PROJECT_NAME << " -u wss://localhost:8080 -c \"-t deactivate --password P@ssw0rd\" -x http://proxy.com:1000" << std::endl; + std::cout << " " << PROJECT_NAME << " -u wss://localhost:8080 -c \"-e [encrypted-command-text] -h [signature-hash]\"" << std::endl; + std::cout << std::endl; + std::cout << "Note:" << std::endl; + std::cout << " Since can contain multiple options and arguments, the entire must be in quotes as shown" << std::endl; + std::cout << " in the examples." << std::endl; +} + +void usage_show_version() +{ + std::cout << PROJECT_NAME << " " << PROJECT_VER << std::endl; + std::cout << "protocol " << PROTOCOL_VERSION << std::endl; +} \ No newline at end of file diff --git a/usage.h b/usage.h new file mode 100644 index 0000000..0ee549e --- /dev/null +++ b/usage.h @@ -0,0 +1,23 @@ +/* +Copyright 2019 Intel Corporation + +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 __USAGE_H__ +#define __USAGE_H__ + +void usage_show_help(); +void usage_show_version(); + +#endif \ No newline at end of file diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..d1e7b81 --- /dev/null +++ b/utils.cpp @@ -0,0 +1,70 @@ +/* +Copyright 2019 Intel Corporation + +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 "utils.h" +#include +#include +#include + +std::string util_encode_base64(std::string str) +{ + std::vector strVector(str.begin(), str.end()); + utility::string_t base64 = utility::conversions::to_base64(strVector); + std::string encodedString = utility::conversions::to_utf8string(base64); + + return encodedString; +} + +std::string util_decode_base64(std::string str) +{ + utility::string_t serializedData = utility::conversions::to_string_t(str); + std::vector strVector = utility::conversions::from_base64(serializedData); + std::string decodedString(strVector.begin(), strVector.end()); + + return decodedString; +} + +bool util_is_printable(std::string str) +{ + for (char c : str) + { + if (!std::isprint(c)) + { + return false; + } + } + + return true; +} + +bool util_format_uuid(std::vector uuid_bytes, std::string& uuid_string) +{ + if (uuid_bytes.size() != 16) return false; + + char tmp[100]; + snprintf(tmp, 100, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid_bytes[3], uuid_bytes[2], uuid_bytes[1], uuid_bytes[0], + uuid_bytes[5], uuid_bytes[4], + uuid_bytes[7], uuid_bytes[6], + uuid_bytes[8], uuid_bytes[9], + uuid_bytes[10], uuid_bytes[11], uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]); + + uuid_string = tmp; + + return true; +} + diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..1d445e8 --- /dev/null +++ b/utils.h @@ -0,0 +1,28 @@ +/* +Copyright 2019 Intel Corporation + +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 __UTILS_H__ +#define __UTILS_H__ + +#include +#include + +std::string util_encode_base64(std::string str); +std::string util_decode_base64(std::string str); +bool util_is_printable(std::string str); +bool util_format_uuid(std::vector uuid_bytes, std::string& uuid_string); + +#endif \ No newline at end of file From eb42f1e89d2517e0f302a2329a83b1b51cb2cb70 Mon Sep 17 00:00:00 2001 From: Mudit Vats Date: Tue, 8 Sep 2020 13:40:45 -0700 Subject: [PATCH 2/5] Update license. --- MicroLMS/MicroLMS/main.c | 19 ++---- MicroLMS/core/utils.c | 20 ++++-- MicroLMS/core/utils.h | 20 ++++-- MicroLMS/heci/HECILinux.c | 67 ++------------------- MicroLMS/heci/HECILinux.h | 33 ++-------- MicroLMS/heci/HECIWin.c | 9 ++- MicroLMS/heci/HECIWin.h | 9 ++- MicroLMS/heci/HECI_if.h | 9 ++- MicroLMS/heci/LMEConnection.c | 9 ++- MicroLMS/heci/LMEConnection.h | 10 ++- MicroLMS/heci/LMS_if.h | 9 ++- MicroLMS/heci/LMS_if_constants.h | 9 ++- MicroLMS/heci/PTHICommand.c | 9 ++- MicroLMS/heci/PTHICommand.h | 9 ++- MicroLMS/heci/StatusCodeDefinitions.h | 9 ++- MicroLMS/microstack/ILibAsyncServerSocket.c | 9 ++- MicroLMS/microstack/ILibAsyncServerSocket.h | 9 ++- MicroLMS/microstack/ILibAsyncSocket.c | 9 ++- MicroLMS/microstack/ILibAsyncSocket.h | 9 ++- MicroLMS/microstack/ILibLMS.c | 9 ++- MicroLMS/microstack/ILibLMS.h | 9 ++- MicroLMS/microstack/ILibParsers.c | 9 ++- MicroLMS/microstack/ILibParsers.h | 9 ++- MicroLMS/microstack/ILibRemoteLogging.h | 9 ++- activation.cpp | 19 ++---- activation.h | 19 ++---- args.cpp | 19 ++---- args.h | 19 ++---- commands.cpp | 19 ++---- commands.h | 19 ++---- info.cpp | 19 ++---- info.h | 19 ++---- lms.cpp | 19 ++---- lms.h | 19 ++---- main.cpp | 19 ++---- network.cpp | 19 ++---- network.h | 19 ++---- port.h | 19 ++---- usage.cpp | 19 ++---- usage.h | 19 ++---- utils.cpp | 19 ++---- utils.h | 19 ++---- 42 files changed, 190 insertions(+), 483 deletions(-) diff --git a/MicroLMS/MicroLMS/main.c b/MicroLMS/MicroLMS/main.c index 0d20ec6..fa8d004 100644 --- a/MicroLMS/MicroLMS/main.c +++ b/MicroLMS/MicroLMS/main.c @@ -1,18 +1,7 @@ -/* -Copyright 2006 - 2013 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2006 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #if defined(WIN32) || defined (_WIN32_WCE) #ifndef MICROSTACK_NO_STDAFX diff --git a/MicroLMS/core/utils.c b/MicroLMS/core/utils.c index 1c30a05..ff1ec84 100644 --- a/MicroLMS/core/utils.c +++ b/MicroLMS/core/utils.c @@ -1,8 +1,18 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/* +Copyright 2011 - 2020 Intel Corporation. + +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. +*/ #if defined(WIN32) && !defined(_MINCORE) #define _CRTDBG_MAP_ALLOC diff --git a/MicroLMS/core/utils.h b/MicroLMS/core/utils.h index dd03029..3eefd38 100644 --- a/MicroLMS/core/utils.h +++ b/MicroLMS/core/utils.h @@ -1,8 +1,18 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/* +Copyright 2011 - 2020 Intel Corporation. + +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. +*/ #if !defined(__MeshUtils__) #define __MeshUtils__ diff --git a/MicroLMS/heci/HECILinux.c b/MicroLMS/heci/HECILinux.c index cffcfea..4b97e32 100644 --- a/MicroLMS/heci/HECILinux.c +++ b/MicroLMS/heci/HECILinux.c @@ -1,69 +1,10 @@ -/****************************************************************************** +/********************************************************************* * Intel Management Engine Interface (Intel MEI) Linux driver * Intel MEI Interface Header * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Corporation. - * linux-mei@linux.intel.com - * http://www.intel.com - * - * BSD LICENSE - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - + * Copyright (c) Intel Corporation 2003 - 2020 + * SPDX-License-Identifier: Apache-2.0 + **********************************************************************/ #include #include #include diff --git a/MicroLMS/heci/HECILinux.h b/MicroLMS/heci/HECILinux.h index 9bde522..37a6f3c 100644 --- a/MicroLMS/heci/HECILinux.h +++ b/MicroLMS/heci/HECILinux.h @@ -1,32 +1,7 @@ -/******************************************************************************* - * Copyright (C) 2004-2008 Intel Corp. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * - Neither the name of Intel Corp. nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Intel Corp. OR THE CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *******************************************************************************/ +/********************************************************************* +* Copyright (c) Intel Corporation 2004 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __HECI_LINUX_H__ #define __HECI_LINUX_H__ diff --git a/MicroLMS/heci/HECIWin.c b/MicroLMS/heci/HECIWin.c index f885fa3..8f047ea 100644 --- a/MicroLMS/heci/HECIWin.c +++ b/MicroLMS/heci/HECIWin.c @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/HECIWin.h b/MicroLMS/heci/HECIWin.h index 457ffc9..537655a 100644 --- a/MicroLMS/heci/HECIWin.h +++ b/MicroLMS/heci/HECIWin.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/HECI_if.h b/MicroLMS/heci/HECI_if.h index 9b862e0..1ebfad4 100644 --- a/MicroLMS/heci/HECI_if.h +++ b/MicroLMS/heci/HECI_if.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/LMEConnection.c b/MicroLMS/heci/LMEConnection.c index 1bd4129..618d68f 100644 --- a/MicroLMS/heci/LMEConnection.c +++ b/MicroLMS/heci/LMEConnection.c @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/LMEConnection.h b/MicroLMS/heci/LMEConnection.h index 9f51466..7169e5c 100644 --- a/MicroLMS/heci/LMEConnection.h +++ b/MicroLMS/heci/LMEConnection.h @@ -1,9 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ - +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE #ifndef __LME_CONNECTION_H__ diff --git a/MicroLMS/heci/LMS_if.h b/MicroLMS/heci/LMS_if.h index a3fbd2c..e12fef0 100644 --- a/MicroLMS/heci/LMS_if.h +++ b/MicroLMS/heci/LMS_if.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/LMS_if_constants.h b/MicroLMS/heci/LMS_if_constants.h index 0b1dda8..c8104df 100644 --- a/MicroLMS/heci/LMS_if_constants.h +++ b/MicroLMS/heci/LMS_if_constants.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/PTHICommand.c b/MicroLMS/heci/PTHICommand.c index 0ab42be..c43504d 100644 --- a/MicroLMS/heci/PTHICommand.c +++ b/MicroLMS/heci/PTHICommand.c @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/PTHICommand.h b/MicroLMS/heci/PTHICommand.h index 3039093..1f94647 100644 --- a/MicroLMS/heci/PTHICommand.h +++ b/MicroLMS/heci/PTHICommand.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/heci/StatusCodeDefinitions.h b/MicroLMS/heci/StatusCodeDefinitions.h index c903f97..ce8473a 100644 --- a/MicroLMS/heci/StatusCodeDefinitions.h +++ b/MicroLMS/heci/StatusCodeDefinitions.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef _MINCORE diff --git a/MicroLMS/microstack/ILibAsyncServerSocket.c b/MicroLMS/microstack/ILibAsyncServerSocket.c index 91520d8..857e066 100644 --- a/MicroLMS/microstack/ILibAsyncServerSocket.c +++ b/MicroLMS/microstack/ILibAsyncServerSocket.c @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #if defined(WIN32) && !defined(_MINCORE) #define _CRTDBG_MAP_ALLOC diff --git a/MicroLMS/microstack/ILibAsyncServerSocket.h b/MicroLMS/microstack/ILibAsyncServerSocket.h index 60c5aa9..8ad42cf 100644 --- a/MicroLMS/microstack/ILibAsyncServerSocket.h +++ b/MicroLMS/microstack/ILibAsyncServerSocket.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef ___ILibAsyncServerSocket___ #define ___ILibAsyncServerSocket___ diff --git a/MicroLMS/microstack/ILibAsyncSocket.c b/MicroLMS/microstack/ILibAsyncSocket.c index 8a4e340..2b01b04 100644 --- a/MicroLMS/microstack/ILibAsyncSocket.c +++ b/MicroLMS/microstack/ILibAsyncSocket.c @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifdef MEMORY_CHECK #include diff --git a/MicroLMS/microstack/ILibAsyncSocket.h b/MicroLMS/microstack/ILibAsyncSocket.h index a4776a7..6d157f9 100644 --- a/MicroLMS/microstack/ILibAsyncSocket.h +++ b/MicroLMS/microstack/ILibAsyncSocket.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef ___ILibAsyncSocket___ #define ___ILibAsyncSocket___ diff --git a/MicroLMS/microstack/ILibLMS.c b/MicroLMS/microstack/ILibLMS.c index f82d9e7..0b1214e 100644 --- a/MicroLMS/microstack/ILibLMS.c +++ b/MicroLMS/microstack/ILibLMS.c @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ /* Real LMS code can be found here: http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers diff --git a/MicroLMS/microstack/ILibLMS.h b/MicroLMS/microstack/ILibLMS.h index a0fcf7e..2a2e7ea 100644 --- a/MicroLMS/microstack/ILibLMS.h +++ b/MicroLMS/microstack/ILibLMS.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #if !defined(_NOHECI) diff --git a/MicroLMS/microstack/ILibParsers.c b/MicroLMS/microstack/ILibParsers.c index 0e4565c..409a20e 100644 --- a/MicroLMS/microstack/ILibParsers.c +++ b/MicroLMS/microstack/ILibParsers.c @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #if defined(_POSIX) #ifndef _VX_CPU diff --git a/MicroLMS/microstack/ILibParsers.h b/MicroLMS/microstack/ILibParsers.h index 60dee22..85107f4 100644 --- a/MicroLMS/microstack/ILibParsers.h +++ b/MicroLMS/microstack/ILibParsers.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ /*! \file ILibParsers.h \brief MicroStack APIs for various functions and tasks diff --git a/MicroLMS/microstack/ILibRemoteLogging.h b/MicroLMS/microstack/ILibRemoteLogging.h index 9092f05..0dcbcaf 100644 --- a/MicroLMS/microstack/ILibRemoteLogging.h +++ b/MicroLMS/microstack/ILibRemoteLogging.h @@ -1,8 +1,7 @@ -/* INTEL CONFIDENTIAL - * Copyright 2011 - 2019 Intel Corporation. - * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. - * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. - */ +/********************************************************************* +* Copyright (c) Intel Corporation 2011 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __ILIBREMOTELOGGING__ #define __ILIBREMOTELOGGING__ diff --git a/activation.cpp b/activation.cpp index 59eed9d..81959db 100644 --- a/activation.cpp +++ b/activation.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "activation.h" #include diff --git a/activation.h b/activation.h index 86006dd..71c1f3f 100644 --- a/activation.h +++ b/activation.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __ACTIVATION_H__ #define __ACTIVATION_H__ diff --git a/args.cpp b/args.cpp index bdf3d0f..17028c9 100644 --- a/args.cpp +++ b/args.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "args.h" #include diff --git a/args.h b/args.h index 7fd005a..7e9918c 100644 --- a/args.h +++ b/args.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __ARGS_H__ #define __ARGS_H__ diff --git a/commands.cpp b/commands.cpp index 208e9fb..7b0b027 100644 --- a/commands.cpp +++ b/commands.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "commands.h" #include diff --git a/commands.h b/commands.h index 614ef30..6f13833 100644 --- a/commands.h +++ b/commands.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __COMMANDS_H__ #define __COMMANDS_H__ diff --git a/info.cpp b/info.cpp index 814bebb..16a07d3 100644 --- a/info.cpp +++ b/info.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "info.h" #include diff --git a/info.h b/info.h index 0157882..43f456e 100644 --- a/info.h +++ b/info.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __INFO_H__ #define __INFO_H__ diff --git a/lms.cpp b/lms.cpp index f4971c5..5599dd3 100644 --- a/lms.cpp +++ b/lms.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "lms.h" #include diff --git a/lms.h b/lms.h index e28c6c7..9f51f55 100644 --- a/lms.h +++ b/lms.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __LMS_H__ #define __LMS_H__ diff --git a/main.cpp b/main.cpp index c0dcef2..f409de4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include #include diff --git a/network.cpp b/network.cpp index efa4940..5c2587f 100644 --- a/network.cpp +++ b/network.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "network.h" #include diff --git a/network.h b/network.h index 8d014f4..fdd1d39 100644 --- a/network.h +++ b/network.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __NETWORK_H__ #define __NETWORK_H__ diff --git a/port.h b/port.h index 8ee9651..c24630e 100644 --- a/port.h +++ b/port.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __PORT_H__ #define __PORT_H__ diff --git a/usage.cpp b/usage.cpp index 21a7703..5947e46 100644 --- a/usage.cpp +++ b/usage.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "usage.h" #include diff --git a/usage.h b/usage.h index 0ee549e..8fbbb17 100644 --- a/usage.h +++ b/usage.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __USAGE_H__ #define __USAGE_H__ diff --git a/utils.cpp b/utils.cpp index d1e7b81..345b2b5 100644 --- a/utils.cpp +++ b/utils.cpp @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #include "utils.h" #include diff --git a/utils.h b/utils.h index 1d445e8..471a947 100644 --- a/utils.h +++ b/utils.h @@ -1,18 +1,7 @@ -/* -Copyright 2019 Intel Corporation - -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. -*/ +/********************************************************************* +* Copyright (c) Intel Corporation 2019 - 2020 +* SPDX-License-Identifier: Apache-2.0 +**********************************************************************/ #ifndef __UTILS_H__ #define __UTILS_H__ From 6d2e37e7fc60f63b501fe5cb6aef334b3523b18a Mon Sep 17 00:00:00 2001 From: Mudit Vats Date: Fri, 30 Oct 2020 09:51:18 -0700 Subject: [PATCH 3/5] Update amtinfo, usage and admin check. General code cleanup and bug fixes. --- CMakeLists.txt | 7 +- MicroLMS/heci/PTHICommand.c | 4 +- MicroLMS/microstack/ILibParsers.c | 4 +- README.md | 2 - activation.cpp | 10 +- args.cpp | 53 ++++++++- args.h | 1 + commands.cpp | 112 +++++++++++++++++-- commands.h | 24 ++++- info.cpp | 171 ++++++++++++++++++++++++------ info.h | 4 +- main.cpp | 103 +++++++++--------- usage.cpp | 87 +++++++++------ 13 files changed, 434 insertions(+), 148 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e14006..a50f3cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,11 +58,14 @@ find_package(Boost COMPONENTS system REQUIRED) # Find OpenSSL find_package(OpenSSL) +# Find ZLIB +find_package(ZLIB) + # Download and build CppRestSDK, If GIT_TAG is changed then need to delete cpprestsdk-prefix because UPDATE_COMMAND is set to "" include(ExternalProject) ExternalProject_Add(cpprestsdk GIT_REPOSITORY https://github.com/Microsoft/cpprestsdk.git - GIT_TAG v2.10.14 + GIT_TAG v2.10.16 CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=0 -DBUILD_SAMPLES=OFF -DBUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/../../install TEST_COMMAND "" UPDATE_COMMAND "" @@ -72,7 +75,7 @@ set(CPPRESTSDK_LIBARIES ${SOURCE_DIR}/../../install/lib/) set(CPPRESTSDK_INCLUDE_DIR ${SOURCE_DIR}/../../install/include/) add_library(cpprest INTERFACE) -target_link_libraries(cpprest INTERFACE ${CPPRESTSDK_LIBARIES}/libcpprest.a OpenSSL::SSL OpenSSL::Crypto ${Boost_LIBRARIES} Threads::Threads) +target_link_libraries(cpprest INTERFACE ${CPPRESTSDK_LIBARIES}/libcpprest.a OpenSSL::SSL OpenSSL::Crypto ${Boost_LIBRARIES} Threads::Threads ZLIB::ZLIB) target_include_directories(cpprest INTERFACE ${CPPRESTSDK_INCLUDE_DIR}) else (UNIX) diff --git a/MicroLMS/heci/PTHICommand.c b/MicroLMS/heci/PTHICommand.c index c43504d..154bd61 100644 --- a/MicroLMS/heci/PTHICommand.c +++ b/MicroLMS/heci/PTHICommand.c @@ -898,7 +898,7 @@ AMT_STATUS pthi_GetRemoteAccessConnectionStatus(REMOTE_ACCESS_STATUS *remoteAcce AMT_STATUS _verifyRemoteAccessConnectionStatus(const CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE *response) { ULONG ByteCount = response->Header.Header.Length; - if (ByteCount != (sizeof(CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR *) + response->MpsHostname.Length)) return PTSDK_STATUS_INTERNAL_ERROR; + if (ByteCount < (sizeof(CFG_GET_REMOTE_ACCESS_CONNECTION_STATUS_RESPONSE) - sizeof(PTHI_MESSAGE_HEADER) - sizeof(CHAR*))) return PTSDK_STATUS_INTERNAL_ERROR; return AMT_STATUS_SUCCESS; } @@ -1347,7 +1347,7 @@ AMT_STATUS pthi_SetHostFQDN(char* str) AMT_STATUS status; UINT8 *readBuffer = NULL; CFG_SET_HOST_FQDN_REQUEST command; - int len = (int)strnlen_s(str, _MAX_PATH); + int len = (int)strnlen_s(str, 256); memset(&command, 0, sizeof(CFG_SET_HOST_FQDN_REQUEST)); // Fix the valgrind warning command.Header = SET_HOST_FQDN_HEADER; diff --git a/MicroLMS/microstack/ILibParsers.c b/MicroLMS/microstack/ILibParsers.c index 409a20e..de69d3e 100644 --- a/MicroLMS/microstack/ILibParsers.c +++ b/MicroLMS/microstack/ILibParsers.c @@ -2441,8 +2441,8 @@ ILibExportMethod void ILibStartChain(void *Chain) // // Free the pipe resources // - fclose(((ILibBaseChain*)Chain)->TerminateReadPipe); - fclose(((ILibBaseChain*)Chain)->TerminateWritePipe); + if (((ILibBaseChain*)Chain)->TerminateReadPipe != NULL) {fclose(((ILibBaseChain*)Chain)->TerminateReadPipe);} + if (((ILibBaseChain*)Chain)->TerminateWritePipe != NULL) {fclose(((ILibBaseChain*)Chain)->TerminateWritePipe);} ((ILibBaseChain*)Chain)->TerminateReadPipe=0; ((ILibBaseChain*)Chain)->TerminateWritePipe=0; #endif diff --git a/README.md b/README.md index 8400006..788706f 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ The Default ("master") branch is our release branch that is for production use. RPC is an application which enables remote capabilities for AMT, such as as device activation. To accomplish this, RPC communicates with the RPS (Remote Provisioning Server). -As a prerequisite, a Local Management Service (LMS) must be installed and running on the operating system. - ## Linux Steps below are for Ubuntu 18.04. diff --git a/activation.cpp b/activation.cpp index 81959db..d758ac8 100644 --- a/activation.cpp +++ b/activation.cpp @@ -17,15 +17,15 @@ bool get_certificate_hashes(web::json::value& hashes) { std::vector hashValues; - std::vector certHashes; + std::vector certHashes; if (!cmd_get_certificate_hashes(certHashes)) { return false; } - for (std::string hashString : certHashes) + for (cert_hash_entry hashString : certHashes) { - hashValues.push_back(web::json::value::string(utility::conversions::convertstring(hashString))); + hashValues.push_back(web::json::value::string(utility::conversions::convertstring(hashString.hash))); } hashes = web::json::value::array(hashValues); @@ -269,10 +269,6 @@ bool act_create_request(std::string commands, std::string dns_suffix, std::strin utility::string_t payload = utility::conversions::to_string_t(encodedPayload); msg[U("payload")] = web::json::value::string(payload); -#ifdef DEBUG - std::cout << "Activation info payload:" << serializedPayload << std::endl; -#endif - // serialize the entire message request = utility::conversions::to_utf8string(msg.serialize()); diff --git a/args.cpp b/args.cpp index 17028c9..63ac07c 100644 --- a/args.cpp +++ b/args.cpp @@ -21,6 +21,19 @@ bool get_arg_exists(int argc, char* argv[], const char* long_opt, const char* sh return false; } +bool get_arg_exists(int argc, char* argv[], const char* opt) +{ + for (int i = 1; i < argc; i++) + { + if ((strcasecmp(argv[i], opt) == 0)) + { + return true; + } + } + + return false; +} + bool get_arg_string(int argc, char* argv[], const char* long_opt, const char* short_opt, std::string& value) { value = ""; @@ -48,14 +61,41 @@ bool get_arg_string(int argc, char* argv[], const char* long_opt, const char* sh return false; } +bool get_arg_string(int argc, char* argv[], const char* opt, std::string& value) +{ + value = ""; + + for (int i = 1; i < argc; i++) + { + if ((strcasecmp(argv[i], opt) == 0)) + { + if (i + 1 < argc) + { + std::string tmp = argv[++i]; + + if (!util_is_printable(tmp)) + { + return false; + } + + value = tmp; + + return true; + } + } + } + + return false; +} + bool args_get_help(int argc, char* argv[]) { - return get_arg_exists(argc, argv, "--help", "-h"); + return get_arg_exists(argc, argv, "--help"); } bool args_get_version(int argc, char* argv[]) { - return get_arg_exists(argc, argv, "--version", "-v"); + return get_arg_exists(argc, argv, "--version"); } bool args_get_url(int argc, char* argv[], std::string& url) @@ -65,7 +105,7 @@ bool args_get_url(int argc, char* argv[], std::string& url) bool args_get_proxy(int argc, char* argv[], std::string& proxy) { - return get_arg_string(argc, argv, "--proxy", "-x", proxy); + return get_arg_string(argc, argv, "--proxy", "-p", proxy); } bool args_get_cmd(int argc, char* argv[], std::string& cmd) @@ -80,5 +120,10 @@ bool args_get_dns(int argc, char* argv[], std::string& dns) bool args_get_info(int argc, char* argv[], std::string& info) { - return get_arg_string(argc, argv, "--info", "-i", info); + return get_arg_string(argc, argv, "--amtinfo", info); } + +bool args_get_verbose(int argc, char* argv[]) +{ + return get_arg_exists(argc, argv, "--verbose", "-v"); +} \ No newline at end of file diff --git a/args.h b/args.h index 7e9918c..1e7f4f0 100644 --- a/args.h +++ b/args.h @@ -15,5 +15,6 @@ bool args_get_proxy(int argc, char* argv[], std::string& proxy); bool args_get_cmd(int argc, char* argv[], std::string& cmd); bool args_get_dns(int argc, char* argv[], std::string& dns); bool args_get_info(int argc, char* argv[], std::string& info); +bool args_get_verbose(int argc, char* argv[]); #endif diff --git a/commands.cpp b/commands.cpp index 7b0b027..453b9f1 100644 --- a/commands.cpp +++ b/commands.cpp @@ -6,6 +6,7 @@ #include "commands.h" #include #include "port.h" +#include "utils.h" #ifndef _WIN32 #include @@ -22,6 +23,13 @@ extern "C" { } #include "version.h" +bool cmd_is_admin() +{ + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + return true; +} + bool cmd_get_version(std::string& version) { version.clear(); @@ -184,8 +192,12 @@ bool cmd_get_dns_suffix(std::string& suffix) if (amt_status == 0) { - std::string tmp(amt_dns_suffix.Buffer, amt_dns_suffix.Length); - suffix = tmp; + if (amt_dns_suffix.Buffer != NULL) + { + std::string tmp(amt_dns_suffix.Buffer, amt_dns_suffix.Length); + suffix = tmp; + free(amt_dns_suffix.Buffer); + } return true; } @@ -222,9 +234,9 @@ bool cmd_get_wired_mac_address(std::vector& address) return false; } -bool cmd_get_certificate_hashes(std::vector& hashes) +bool cmd_get_certificate_hashes(std::vector& hash_entries) { - hashes.clear(); + hash_entries.clear(); // initialize HECI interface if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; @@ -242,28 +254,38 @@ bool cmd_get_certificate_hashes(std::vector& hashes) AMT_STATUS status = pthi_GetCertificateHashEntry(amt_hash_handles.Handles[i], &certhash_entry); int hashSize; + cert_hash_entry tmp; switch (certhash_entry.HashAlgorithm) { case 0: // MD5 hashSize = 16; + tmp.algorithm = "MD5"; break; case 1: // SHA1 hashSize = 20; + tmp.algorithm = "SHA1"; break; case 2: // SHA256 hashSize = 32; + tmp.algorithm = "SHA256"; break; case 3: // SHA512 hashSize = 64; + tmp.algorithm = "SHA512"; break; default: - hashSize = 64; + hashSize = 0; + tmp.algorithm = "UNKNOWN"; break; } if (certhash_entry.IsActive == 1) { + std::string cert_name(certhash_entry.Name.Buffer, certhash_entry.Name.Length); + tmp.name = cert_name; + tmp.is_default = certhash_entry.IsDefault; + tmp.is_active = certhash_entry.IsActive; + std::string hashString; - hashString.clear(); for (int i = 0; i < hashSize; i++) { char hex[10]; @@ -271,7 +293,9 @@ bool cmd_get_certificate_hashes(std::vector& hashes) hashString += hex; } - hashes.push_back(hashString); + tmp.hash = hashString; + + hash_entries.push_back(tmp); } } @@ -280,3 +304,77 @@ bool cmd_get_certificate_hashes(std::vector& hashes) return false; } + +bool cmd_get_remote_access_connection_status(int& network_status, int& remote_status, int& remote_trigger, std::string& mps_hostname) +{ + network_status = 0; + remote_status = 0; + remote_trigger = 0; + mps_hostname = ""; + const int MPS_SERVER_MAXLENGTH = 256; + + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get DNS according to AMT + REMOTE_ACCESS_STATUS remote_access_connection_status; + AMT_STATUS amt_status = pthi_GetRemoteAccessConnectionStatus(&remote_access_connection_status); + + if (amt_status == 0) + { + network_status = remote_access_connection_status.AmtNetworkConnectionStatus; + remote_status = remote_access_connection_status.RemoteAccessConnectionStatus; + remote_trigger = remote_access_connection_status.RemoteAccessConnectionTrigger; + + if (remote_access_connection_status.MpsHostname.Buffer != NULL) + { + if (remote_access_connection_status.MpsHostname.Length < MPS_SERVER_MAXLENGTH) + { + std::string tmp(remote_access_connection_status.MpsHostname.Buffer, remote_access_connection_status.MpsHostname.Length); + if (util_is_printable(tmp)) + { + mps_hostname = tmp; + } + } + + free(remote_access_connection_status.MpsHostname.Buffer); + } + + return true; + } + + return false; +} + +bool cmd_get_lan_interface_settings(lan_interface_settings& lan_interface_settings) +{ + // initialize HECI interface + if (heci_Init(NULL, PTHI_CLIENT) == 0) return false; + + // get wired interface + LAN_SETTINGS lan_settings; + UINT32 interface_settings = 0; // wired=0, wireless=1 + AMT_STATUS amt_status = pthi_GetLanInterfaceSettings(interface_settings, &lan_settings); + if (amt_status == 0) + { + lan_interface_settings.is_enabled = lan_settings.Enabled; + lan_interface_settings.dhcp_mode = lan_settings.DhcpIpMode; + lan_interface_settings.dhcp_enabled = lan_settings.DhcpEnabled; + + lan_interface_settings.ip_address.push_back((lan_settings.Ipv4Address >> 24) & 0xff); + lan_interface_settings.ip_address.push_back((lan_settings.Ipv4Address >> 16) & 0xff); + lan_interface_settings.ip_address.push_back((lan_settings.Ipv4Address >> 8) & 0xff); + lan_interface_settings.ip_address.push_back((lan_settings.Ipv4Address) & 0xff); + + lan_interface_settings.mac_address.push_back(lan_settings.MacAddress[0]); + lan_interface_settings.mac_address.push_back(lan_settings.MacAddress[1]); + lan_interface_settings.mac_address.push_back(lan_settings.MacAddress[2]); + lan_interface_settings.mac_address.push_back(lan_settings.MacAddress[3]); + lan_interface_settings.mac_address.push_back(lan_settings.MacAddress[4]); + lan_interface_settings.mac_address.push_back(lan_settings.MacAddress[5]); + + return true; + } + + return false; +} diff --git a/commands.h b/commands.h index 6f13833..82c2e5a 100644 --- a/commands.h +++ b/commands.h @@ -9,6 +9,26 @@ #include #include +struct cert_hash_entry +{ + std::string hash; + std::string name; + std::string algorithm; + bool is_active; + bool is_default; +}; + +struct lan_interface_settings +{ + bool is_enabled; + bool link_status; + bool dhcp_enabled; + int dhcp_mode; + std::vector ip_address; + std::vector mac_address; +}; + +bool cmd_is_admin(); bool cmd_get_version(std::string& version); bool cmd_get_build_number(std::string& version); bool cmd_get_sku(std::string& version); @@ -17,6 +37,8 @@ bool cmd_get_local_system_account(std::string& username, std::string& password); bool cmd_get_control_mode(int& mode); bool cmd_get_dns_suffix(std::string& suffix); bool cmd_get_wired_mac_address(std::vector& address); -bool cmd_get_certificate_hashes(std::vector& hashes); +bool cmd_get_certificate_hashes(std::vector& hash_entries); +bool cmd_get_remote_access_connection_status(int& network_status, int& remote_status, int& remote_trigger, std::string& mps_hostname); +bool cmd_get_lan_interface_settings(lan_interface_settings& lan_interface_settings); #endif \ No newline at end of file diff --git a/info.cpp b/info.cpp index 16a07d3..ddd3471 100644 --- a/info.cpp +++ b/info.cpp @@ -10,13 +10,16 @@ #include "commands.h" #include "utils.h" -void out_text(const std::string name, const std::vector value, const unsigned char delimeter=' ') +const int PADDING = 25; + +void out_text(const std::string name, const std::vector value, const unsigned char delimeter = ' ', const bool hex = true) { - std::cout << name << ": "; + std::cout << name << std::setfill(' ') << std::setw(PADDING - name.size()) << ": "; int char_count = 1; for (unsigned char tmp : value) { - std::cout << std::setfill('0') << std::setw(2) << std::hex << (unsigned int) tmp; + (hex) ? std::cout << std::setfill('0') << std::setw(2) << std::hex << (unsigned int)tmp + : std::cout << (unsigned int)tmp; if (char_count++ < value.size()) { @@ -29,26 +32,23 @@ void out_text(const std::string name, const std::vector value, co void out_text(const std::string name, const std::string value) { - std::cout << name << ": "; + std::cout << name << std::setfill(' ') << std::setw(PADDING - name.size()) << ": "; std::cout << value; std::cout << std::endl; } void out_text(const std::string name, const int value) { - std::cout << name << ": "; + std::cout << name << std::setfill(' ') << std::setw(PADDING - name.size()) << ": "; std::cout << value; std::cout << std::endl; } void out_text(const std::string name, const std::vector value) { - int count = 1; - - std::cout << name << std::endl; + std::cout << name << std::setfill(' ') << std::setw(PADDING - name.size()) << ": " << std::endl; for (std::string tmp : value) { - std::cout << std::setfill('0') << std::setw(2) << count++ << ":"; std::cout << tmp << std::endl; } } @@ -70,7 +70,7 @@ bool info_get_build_number() if (!cmd_get_build_number(tmp)) return false; - out_text("Build number", tmp); + out_text("Build Number", tmp); return true; } @@ -111,7 +111,7 @@ bool info_get_control_mode() else if (tmp == 1) control_mode = "activated in client control mode"; else if (tmp == 2) control_mode = "activated in admin control mode"; - out_text("Control mode", control_mode); + out_text("Control Mode", control_mode); return true; } @@ -122,29 +122,31 @@ bool info_get_dns_suffix() if (!cmd_get_dns_suffix(tmp)) return false; - out_text("DNS suffix", tmp); - - return true; -} - -bool info_get_wired_mac_address() -{ - std::vector tmp; - - if (!cmd_get_wired_mac_address(tmp)) return false; - - out_text("Wired MAC address", tmp, '-'); + out_text("DNS Suffix", tmp); return true; } bool info_get_certificate_hashes() { + std::vector hash_entries; std::vector tmp; - if (!cmd_get_certificate_hashes(tmp)) return false; + if (!cmd_get_certificate_hashes(hash_entries)) return false; - out_text("Certificate hashes", tmp); + for (cert_hash_entry entry : hash_entries) + { + std::string name = entry.name; + (entry.is_default) ? name += ", (Default, " : ", (Not Default, "; + (entry.is_active) ? name += "Active)" : "Not Active)"; + tmp.push_back(name); + + std::string algorithm = " " + entry.algorithm; + algorithm += ": " + entry.hash; + tmp.push_back(algorithm); + } + + out_text("Certificate Hashes", tmp); return true; } @@ -153,13 +155,99 @@ bool info_get_all() { std::vector tmp; - if (info_get_version() && info_get_build_number() && info_get_sku() && - info_get_uuid() && info_get_control_mode() && info_get_dns_suffix() && - info_get_wired_mac_address() && info_get_certificate_hashes()) + bool status_ver = info_get_version(); + bool status_bld = info_get_build_number(); + bool status_sku = info_get_sku(); + bool status_uuid = info_get_uuid(); + bool status_mode = info_get_control_mode(); + bool status_dns = info_get_dns_suffix(); + bool status_ras = info_get_remote_access_connection_status(); + bool status_lan = info_get_lan_interface_settings(); + bool status_cert = info_get_certificate_hashes(); + + if (status_ver && status_bld && status_sku && status_uuid && status_mode && + status_dns && status_ras && status_lan && status_cert) { return true; } + return false; +} + +bool info_get_remote_access_connection_status() +{ + int network_status; + int remote_status; + int remote_trigger; + std::string mps_hostname; + + if (!cmd_get_remote_access_connection_status(network_status, remote_status, remote_trigger, mps_hostname)) return false; + + std::string tmp; + + switch (network_status) + { + case 0: tmp = "direct"; + break; + case 1: tmp = "vpn"; + break; + case 2: tmp = "outside enterprise"; + break; + case 3: + default: tmp = "unknown"; + break; + } + + out_text("RAS Network", tmp); + + switch (remote_status) + { + case 0: tmp = "not connected"; + break; + case 1: tmp = "connecting"; + break; + case 2: tmp = "connected"; + break; + default: tmp = "unknown"; + break; + } + + out_text("RAS Remote Status", tmp); + + switch (remote_trigger) + { + case 0: tmp = "user initiated"; + break; + case 1: tmp = "alert"; + break; + case 2: tmp = "periodic"; + break; + case 3: tmp = "provisioning"; + break; + default: tmp = "unknown"; + break; + + } + + out_text("RAS Trigger", tmp); + + out_text("RAS MPS Hostname", mps_hostname); + + return true; +} + +bool info_get_lan_interface_settings() +{ + lan_interface_settings tmp; + + if (!cmd_get_lan_interface_settings(tmp)) return false; + + out_text("DHCP Enabled", (tmp.dhcp_enabled) ? "true" : "false"); + out_text("DHCP Mode", (tmp.dhcp_mode == 1) ? "active" : "passive"); + out_text("Link Status", (tmp.link_status) ? "up" : "down"); + out_text("IP Address", tmp.ip_address, '.', false); + out_text("MAC Address", tmp.mac_address, ':'); + return true; } @@ -169,7 +257,7 @@ bool info_get(const std::string info) { return info_get_version(); } - else if (info.compare("build") == 0) + else if (info.compare("bld") == 0) { return info_get_build_number(); } @@ -189,14 +277,18 @@ bool info_get(const std::string info) { return info_get_dns_suffix(); } - else if (info.compare("mac") == 0) - { - return info_get_wired_mac_address(); - } else if (info.compare("cert") == 0) { return info_get_certificate_hashes(); } + else if (info.compare("ras") == 0) + { + return info_get_remote_access_connection_status(); + } + else if (info.compare("lan") == 0) + { + return info_get_lan_interface_settings(); + } else if (info.compare("all") == 0) { return info_get_all(); @@ -204,3 +296,16 @@ bool info_get(const std::string info) return false; } + +bool info_get_verify(const std::string info) +{ + if ((info.compare("ver") == 0) || (info.compare("bld") == 0) || (info.compare("sku") == 0) || + (info.compare("uuid") == 0) || (info.compare("mode") == 0) || (info.compare("dns") == 0) || + (info.compare("cert") == 0) || (info.compare("ras") == 0) || (info.compare("lan") == 0) || + (info.compare("all") == 0)) + { + return true; + } + + return false; +} \ No newline at end of file diff --git a/info.h b/info.h index 43f456e..8a8e470 100644 --- a/info.h +++ b/info.h @@ -9,13 +9,15 @@ #include bool info_get(const std::string info); +bool info_get_verify(const std::string info); bool info_get_version(); bool info_get_build_number(); bool info_get_sku(); bool info_get_uuid(); bool info_get_control_mode(); bool info_get_dns_suffix(); -bool info_get_wired_mac_address(); bool info_get_all(); +bool info_get_remote_access_connection_status(); +bool info_get_lan_interface_settings(); #endif diff --git a/main.cpp b/main.cpp index f409de4..2ad0ba9 100644 --- a/main.cpp +++ b/main.cpp @@ -59,10 +59,11 @@ int main(int argc, char* argv[]) std::string arg_cmd; std::string arg_dns; std::string arg_info; + bool arg_verbose = false; if (argc == 1) { - std::cout << "Use -h, --help for help." << std::endl; + std::cout << "Use --help for help." << std::endl; return 0; } @@ -80,14 +81,24 @@ int main(int argc, char* argv[]) return 0; } + // Check if running in elevated privileges + if (!cmd_is_admin()) + { + std::cout << "Unable to launch application. Please ensure that Intel ME is present, the MEI driver is installed and that this application is run with administrator or root privileges." << std::endl; + return 0; + } + // check for info if (args_get_info(argc, argv, arg_info)) { - if (!info_get(arg_info)) + if (!info_get_verify(arg_info)) { std::cout << "Incorrect or missing arguments." << std::endl; - std::cout << "Use -h, --help for help." << std::endl; + std::cout << "Use --help for help." << std::endl; + return 0; } + + info_get(arg_info); return 0; } @@ -95,10 +106,16 @@ int main(int argc, char* argv[]) if (!args_get_url(argc, argv, arg_url) || !args_get_cmd(argc, argv, arg_cmd)) { std::cout << "Incorrect or missing arguments." << std::endl; - std::cout << "Use -h, --help for help." << std::endl; + std::cout << "Use --help for help." << std::endl; return 0; } + // verbose output + if (args_get_verbose(argc, argv)) + { + arg_verbose = true; + } + // Print version info usage_show_version(); @@ -122,8 +139,8 @@ int main(int argc, char* argv[]) try { // check if LMS is available - SOCKET s = lms_connect(); - closesocket(s); + SOCKET lms_socket = lms_connect(); + closesocket(lms_socket); } catch (...) { @@ -137,10 +154,6 @@ int main(int argc, char* argv[]) } } -#ifdef DEBUG - std::cout << "Activation info: " << std::endl << activation_info << std::endl; -#endif - // webSocket Interface web::websockets::client::websocket_client_config client_config; if (args_get_proxy(argc, argv, arg_proxy)) @@ -149,16 +162,17 @@ int main(int argc, char* argv[]) } #ifdef DEBUG // skip certificate verification if debug build - std::cout << "!!! SKIPPING CERTIFICATE VERIFICATION !!!" << std::endl; + std::cout << "Skipping certificate verification." << std::endl; client_config.set_validate_certificates(false); #endif web::websockets::client::websocket_callback_client client(client_config); std::condition_variable cv; std::mutex mx; - SOCKET s; + SOCKET lms_socket; + memset(&lms_socket, 0, sizeof(SOCKET)); // set receive handler - client.set_message_handler([&client, &mx, &cv, &s](web::websockets::client::websocket_incoming_message ret_msg) + client.set_message_handler([&client, &mx, &cv, &lms_socket, arg_verbose](web::websockets::client::websocket_incoming_message ret_msg) { // kick the timer std::chrono::time_point now = std::chrono::system_clock::now(); @@ -169,10 +183,6 @@ int main(int argc, char* argv[]) { // handle message from server std::string rcv_websocket_msg = ret_msg.extract_string().get(); -#ifdef DEBUG - std::cout << std::endl << "<<<<< Received Message " << std::endl; - std::cout << rcv_websocket_msg << std::endl; -#endif std::cout << "." << std::flush; // dot status output // parse incoming JSON message @@ -224,12 +234,6 @@ int main(int argc, char* argv[]) return; } - -#ifdef DEBUG - std::cout << msgMethod << ", " << msgStatus << ", " << msgMessage << std::endl; - std::cout << rcv_websocket_msg << std::endl; -#endif - // process any messages we can // - if success, done // - if error, get out @@ -280,7 +284,7 @@ int main(int argc, char* argv[]) try { // conntect to lms - s = lms_connect(); + lms_socket = lms_connect(); } catch (...) { @@ -288,22 +292,23 @@ int main(int argc, char* argv[]) return; } -#ifdef DEBUG - std::cout << std::endl << "vvvvv Sending Message " << std::endl; - std::cout << payloadDecoded << std::endl; -#endif + if (arg_verbose) + { + std::cout << std::endl << "vvv -- message to AMT -- vvv" << std::endl; + std::cout << payloadDecoded << std::endl; + } // send message to LMS - if (send(s, payloadDecoded.c_str(), (int)payloadDecoded.length(), 0) < 0) + if (send(lms_socket, payloadDecoded.c_str(), (int)payloadDecoded.length(), 0) < 0) { throw std::runtime_error("error: socket send"); } // handle response message from LMS - int fd = ((int) s) + 1; + int fd = ((int)lms_socket) + 1; fd_set rset; FD_ZERO(&rset); - FD_SET(s, &rset); + FD_SET(lms_socket, &rset); timeval timeout; memset(&timeout, 0, sizeof(timeval)); @@ -328,13 +333,9 @@ int main(int argc, char* argv[]) // read from LMS char recv_buffer[4096]; memset(recv_buffer, 0, 4096); - res = recv(s, recv_buffer, 4096, 0); + res = recv(lms_socket, recv_buffer, 4096, 0); if (res > 0) { -#ifdef DEBUG - std::cout << std::endl << "^^^^^ Received Message" << std::endl; - std::cout << recv_buffer << std::endl; -#endif superBuffer += recv_buffer; } else if (res < 0) @@ -353,31 +354,32 @@ int main(int argc, char* argv[]) // if there is some data send it if (superBuffer.length() > 0) { + if (arg_verbose) + { + std::cout << std::endl << "^^^ -- message from AMT -- ^^^" << std::endl; + std::cout << superBuffer << std::endl; + } + std::string response; if (!act_create_response(superBuffer.c_str(), response)) return; web::websockets::client::websocket_outgoing_message send_websocket_msg; std::string send_websocket_buffer(response); send_websocket_msg.set_utf8_message(send_websocket_buffer); -#ifdef DEBUG - std::cout << std::endl << ">>>>> Sending Message" << std::endl; - std::cout << superBuffer << std::endl; - std::cout << send_websocket_buffer << std::endl; -#endif client.send(send_websocket_msg).wait(); // done - closesocket(s); + closesocket(lms_socket); return; } } - closesocket(s); + closesocket(lms_socket); } catch (...) { std::cerr << std::endl << "Communication error in receive handler." << std::endl; - closesocket(s); + closesocket(lms_socket); } }); @@ -403,12 +405,7 @@ int main(int argc, char* argv[]) { // send activationParams to websocket web::websockets::client::websocket_outgoing_message out_msg; - out_msg.set_utf8_message(activation_info); - -#ifdef DEBUG - std::cout << std::endl << ">>>>> Sending Activiation Info" << std::endl; - std::cout << activation_info << std::endl; -#endif + out_msg.set_utf8_message(activation_info); client.send(out_msg).wait(); } catch (...) @@ -433,10 +430,8 @@ int main(int argc, char* argv[]) client.close().wait(); // clean-up socket - if (s) { - shutdown(s, SD_BOTH); - closesocket(s); - } + shutdown(lms_socket, SD_BOTH); + closesocket(lms_socket); exit(0); } diff --git a/usage.cpp b/usage.cpp index 5947e46..30abebf 100644 --- a/usage.cpp +++ b/usage.cpp @@ -10,41 +10,62 @@ void usage_show_help() { - std::cout << "Usage: " << PROJECT_NAME << " --help | --version | --info | --url --cmd [--proxy ] [--dns ]" << std::endl; - std::cout << "Required:" << std::endl; - std::cout << " -u, --url websocket server" << std::endl; - std::cout << " -c, --cmd server command" << std::endl; - std::cout << "Optional:" << std::endl; - std::cout << " -x, --proxy proxy address and port" << std::endl; - std::cout << " -d, --dns dns suffix" << std::endl; - std::cout << "Informational:" << std::endl; - std::cout << " -h, --help this help text" << std::endl; - std::cout << " -v, --version version" << std::endl; - std::cout << " -i, --info info on an " << std::endl; - std::cout << std::endl; - std::cout << "Info :" << std::endl; - std::cout << " all all items" << std::endl; - std::cout << " ver bios version" << std::endl; - std::cout << " build build number" << std::endl; - std::cout << " sku product sku" << std::endl; - std::cout << " uuid unique identifier" << std::endl; - std::cout << " mode current control mode {0=none, 1=client, 2=admin}" << std::endl; - std::cout << " dns domain name suffix" << std::endl; - std::cout << " mac wired MAC address" << std::endl; - std::cout << " cert certificate hashes" << std::endl; - std::cout << std::endl; - std::cout << "Examples:" << std::endl; - std::cout << " " << PROJECT_NAME << " --url wss://localhost:8080 --cmd \"-t activate --profile profile1\"" << std::endl; - std::cout << " " << PROJECT_NAME << " -u wss://localhost:8080 -c \"-t deactivate --password P@ssw0rd\" -x http://proxy.com:1000" << std::endl; - std::cout << " " << PROJECT_NAME << " -u wss://localhost:8080 -c \"-e [encrypted-command-text] -h [signature-hash]\"" << std::endl; - std::cout << std::endl; - std::cout << "Note:" << std::endl; - std::cout << " Since can contain multiple options and arguments, the entire must be in quotes as shown" << std::endl; - std::cout << " in the examples." << std::endl; + // 80 chars "01234567890123456789012345678901234567890123456789012345678901234567890123456789" + std::cout << "Usage: " << std::endl; + std::cout << " " << PROJECT_NAME << " [optional]" << std::endl; + std::cout << " " << PROJECT_NAME << " " << std::endl; + std::cout << std::endl; + std::cout << "Required:" << std::endl; + std::cout << " -u, --url websocket server" << std::endl; + std::cout << " -c, --cmd server command" << std::endl; + std::cout << std::endl; + std::cout << " Since can contain multiple options and arguments, the entire" << std::endl; + std::cout << " must be in quotes. Please see examples below." << std::endl; + std::cout << std::endl; + std::cout << "Optional:" << std::endl; + std::cout << " -p, --proxy proxy address and port" << std::endl; + std::cout << " -d, --dns dns suffix override" << std::endl; + std::cout << " -v, --verbose verbose output" << std::endl; + std::cout << std::endl; + std::cout << "Informational:" << std::endl; + std::cout << " --help this help text" << std::endl; + std::cout << " --version version" << std::endl; + std::cout << " --amtinfo AMT info on an " << std::endl; + std::cout << std::endl; + std::cout << "Item:" << std::endl; + std::cout << " all all items" << std::endl; + std::cout << " ver BIOS version" << std::endl; + std::cout << " bld build number" << std::endl; + std::cout << " sku product SKU" << std::endl; + std::cout << " uuid unique identifier" << std::endl; + std::cout << " mode current control mode" << std::endl; + std::cout << " dns domain name suffix" << std::endl; + std::cout << " cert certificate hashes" << std::endl; + std::cout << " ras remote access status" << std::endl; + std::cout << " lan LAN settings" << std::endl; + std::cout << std::endl; + std::cout << "Examples:" << std::endl; + std::cout << " # Activate platform using profile1" << std::endl; + std::cout << " " << PROJECT_NAME << \ + " --url wss://localhost:8080 --cmd \"-t activate --profile profile1\"" << std::endl; + std::cout << std::endl; + std::cout << " # Activate platform using profile1 and override DNS detection" << std::endl; + std::cout << " " << PROJECT_NAME << \ + " --url wss://localhost:8080 --cmd \"-t activate --profile profile1\" --dns corp.com" << std::endl; + std::cout << std::endl; + std::cout << " # Deactivate platform and connect through a proxy" << std::endl; + std::cout << " " << PROJECT_NAME << \ + " -u wss://localhost:8080 -c \"-t deactivate --password P@ssw0rd\" -p http://proxy.com:1000" << std::endl; + std::cout << std::endl; + std::cout << " # Show all informational items" << std::endl; + std::cout << " " << PROJECT_NAME << " --amtinfo all" << std::endl; } void usage_show_version() { - std::cout << PROJECT_NAME << " " << PROJECT_VER << std::endl; - std::cout << "protocol " << PROTOCOL_VERSION << std::endl; + std::string project_name = PROJECT_NAME; + for (auto& c : project_name) c = toupper(c); // get upper case string + + std::cout << project_name << " " << PROJECT_VER << "." << std::endl; + std::cout << "Protocol " << PROTOCOL_VERSION << "." << std::endl; } \ No newline at end of file From 722a97958f32bf64fc11acbd945de8f0b105d62f Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 13 Nov 2020 14:49:10 -0700 Subject: [PATCH 4/5] build: Add Github Actions Support Signed-off-by: Mike --- .github/workflows/build.yml | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..190d4bf --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,69 @@ +#********************************************************************* +# Copyright (c) Intel Corporation 2020 +# SPDX-License-Identifier: Apache-2.0 +#*********************************************************************/ + +name: Build RPC (Native) + +on: + workflow_dispatch: + +env: + BUILD_TYPE: Release + +jobs: + build-windows: + runs-on: windows-2019 + steps: + - uses: actions/checkout@v2 + - name: Create Build Dir + run: mkdir build + - name: Clone + run: git clone --branch 2020.01 https://github.com/microsoft/vcpkg.git + - name: Build VCPKG + run: cd vcpkg && bootstrap-vcpkg.bat + shell: cmd + - name: dir + run: ls + - name: dir + run: cd vcpkg && ls + - name: Integrate with VS + run: ${{ runner.workspace }}\rpc\vcpkg\vcpkg.exe integrate install + shell: cmd + - name: Install C++ REST SDK + run: ${{ runner.workspace }}\rpc\vcpkg\vcpkg.exe install cpprestsdk:x64-windows-static + shell: cmd + - name: dir + run: ls && cd vcpkg && ls + shell: bash + - name: Prepare for build + run: cd build && cmake .. -DCMAKE_PREFIX_PATH=D:/a/rpc/rpc/vcpkg/installed/x64-windows-static -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_TOOLCHAIN_FILE=D:/a/rpc/rpc/vcpkg/scripts/buildsystems/vcpkg.cmake + - name: Build RPC + run: cd build && cmake --build . --config Debug + - name: GitHub Upload Release Artifacts + uses: actions/upload-artifact@v2 + with: + name: RPC_Windows.exe + path: ${{ runner.workspace }}\rpc\build\Debug\rpc.exe + + build-linux: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-18.04, ubuntu-20.04] + steps: + - uses: actions/checkout@v2 + - name: Install Dependencies + run: | + sudo apt install libboost-system-dev libboost-thread-dev libboost-random-dev libboost-regex-dev libboost-filesystem-dev libssl-dev zlib1g-dev -y + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Debug .. + cmake --build . + + - name: GitHub Upload Release Artifacts + uses: actions/upload-artifact@v2 + with: + name: RPC_Linux_${{ matrix.os }} + path: | + build/rpc \ No newline at end of file From a2c396bae1aac0f347d3ab2476c7183e3b13665c Mon Sep 17 00:00:00 2001 From: bwendlandt-intel <74682355+bwendlandt-intel@users.noreply.github.com> Date: Fri, 20 Nov 2020 14:31:36 -0700 Subject: [PATCH 5/5] Add readme edits for release of doc site (#11) * Update README for 1.0 * Fix gif link * gif sizing and punctuation matching Co-authored-by: bwendlandt-intel --- README.md | 88 +++++------------------------ assets/animations/forkandbuild.gif | Bin 0 -> 3251747 bytes 2 files changed, 15 insertions(+), 73 deletions(-) create mode 100644 assets/animations/forkandbuild.gif diff --git a/README.md b/README.md index 788706f..d1ae79b 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,27 @@ -The Default ("master") branch is our release branch that is for production use. All other branches are pre-production and should not be used for production deployments. +# Remote Provisioning Client -# Remote Provisioning Client (RPC) +The Remote Provisioning Client (RPC) is an application that enables remote capabilities for Intel® AMT, such as as device activation and configuration. To accomplish this, RPC communicates with the Remote Provisioning Server (RPS) to activate and connect the edge device. -RPC is an application which enables remote capabilities for AMT, such as as device activation. To accomplish this, RPC communicates with the RPS (Remote Provisioning Server). +**For detailed documentation** about RPC or other features of the Open AMT Cloud Toolkit, see the [docs](https://open-amt-cloud-toolkit.github.io/mps/). -## Linux +## Prerequisites -Steps below are for Ubuntu 18.04. +We leverage GitHub Actions as a means to build RPC automatically leveraging Github's CI/CD Infrastructure. This avoids having to deal with the challenges of getting your build environment just right on your local machine and allows you to get up and running much faster. Read more about GitHub Actions [here](https://github.blog/2019-08-08-github-actions-now-supports-ci-cd/#:~:text=GitHub%20Actions%20is%20an%20API,every%20step%20along%20the%20way.) -### Dependencies -``` -sudo apt install git cmake build-essential libboost-system-dev libboost-thread-dev libboost-random-dev libboost-regex-dev libboost-filesystem-dev libssl-dev zlib1g-dev -``` +## Build the Remote Provisioning Client (RPC) -### Build +

+ +

-``` -mkdir build -cd build -cmake -DCMAKE_BUILD_TYPE=Release .. -cmake --build . -``` +1. Create a fork of the rpc repository [here](https://github.com/open-amt-cloud-toolkit/rpc/fork) or via the Fork button in the top-right corner of the rpc repository. -To build debug: -``` -cmake -DCMAKE_BUILD_TYPE=Debug .. -``` +2. Click on "Actions" and Select "Build RPC (Native)" Workflow. -### Run +3. Click "Run Workflow", select branch "master", and click "Run Workflow". -``` -sudo ./rpc --url wss://localhost:8080 --cmd "-t activate --profile profile1" -``` +4. Grab a coffee. The build for Windows will take approximately 30 minutes and the build for Linux will take approximately 5 minutes. -Use --help for more options. +5. Once complete, click the completed job, and download the appropriate RPC for your OS under the "Artifacts" section. -## Windows - -Steps below are for Windows 10 and Visual Studio 2019 Professional. - -### Build VCPKG - -Open an x64 native command prompt for Visual Studio 2019 as Administrator. - -``` -git clone --branch 2020.01 https://github.com/microsoft/vcpkg.git -cd vcpkg -bootstrap-vcpkg.bat -vcpkg integrate install -``` - -### Build C++ REST SDK - -Open an x64 native tools command prompt for Visual Studio 2019. -``` -cd vcpkg -vcpkg install cpprestsdk:x64-windows-static -``` - -### Build - -Open an x64 native tools command prompt for Visual Studio 2019. -``` -mkdir build -cd build -cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -cmake --build . --config Release -``` - -To build debug: -``` -cmake --build . --config Debug -``` - -### Run - -Open a command prompt as Administrator. - -``` -cd build\Release -rpc.exe --url wss://localhost:8080 --cmd "-t activate --profile profile1" -``` - -Use --help for more options. +For detailed documentation about RPC and using it to activate a device, see the [docs](https://open-amt-cloud-toolkit.github.io/mps/) diff --git a/assets/animations/forkandbuild.gif b/assets/animations/forkandbuild.gif new file mode 100644 index 0000000000000000000000000000000000000000..b55fc3ae0388c4f351798eeb4647bbd7fd18b206 GIT binary patch literal 3251747 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¬bgsT2SC3>C^0`>C0n zi4;yS3%ZFGS3nNl1D&8rpiN<*XweDofTKvE04kTAm}#C4iI^dI1^rMBH9!@%u%b4B z3r5NlDhd@YikvWd6r1@BnXw7ha6U{ipwkJWT!E$jmT3?2$&weEg+`DIR4NrqpbLg@ z6H|JeDf$mx`V?onrCMqfT$&lUK%+S-odxQiX2GXRVGBTdq=#9ORNxO$+7n0M2@+}# z+i*HHVW5{Fq3gg1PMQ-LdSCZ-s5SAQqk3P3Fs1~ssh#?%IDrMjaAwC42#HDniyEYE z+77l!09U{dkCU8EKnwhEshLVImcX9&zz*a|0H=zo0eY+NV5|ht3DRm4u^OJV$*cca zs5N1$!O9B^Y7>?qq3>`EgTNE3N-vdqU*Nz9&v_F@z^>x?u66333SbNH06MF>poK66 z!mtnKilPJ%tFsCSwkoQlTB!d@tm<$KciR51RL~3XFs)PY58b+^NLrE$N~b=73+Ac{ zpD?i<%c|Kx3+>Pfpb)Ok0IxT339sM}?|=;41*$f&1fTE<{a_8R@Ctvhs-$_cDZ8>Q zo1yLS3ZO6y?U1y0Y7>~S51k3A_CT_mz^I$K2)%F&s}KwPunw8p3Gk4$Dr*nAd9%Z? z4?Ft`=!z3ds;4!v1iJvH(PIm$nX&-84$#A`J4&r^dK1Z+3-@I;_-YTw5V8~7pgFOs zPQVLbs|uhXq1W&OHletT>$vs+xl&4<_zDk;kU0Em53JA+$)F0tpqkxTv`3q?OREzv z+Y2)L3XkxdoWKw4zzCzTweZlH9R6#mYFeaBo04pbg&C@*JMjs;YO#fi3-(YAK~NLf z$*4MEr#)e%kfWoTs=Ulg0M3h}OWLG1!JB+*6H#!n@Tv*z&<}dbob^cnn~A&LyAP6T z3DQ&(xappNx)X1Es7v}d1fU5M+Okye4!4z!4lG>vgN}<$Ss0-Q-@=LAuu)o|}v)}8xHi4zwi@gMZo6ozT#=s0M z+q}9k!ZiuJRVci=k_+znvBtUyQo9q;Tcvqw6F+OMHZjBo)moZxE{w;;gG`3$z8 zzT!)xo7t^S;0tl96ShF5{`R{Qn*a|aE5<$JrrE%&HPOTajIjpH#x~)m&Uq7>K(RV8 zrjRPB&-A7d!^bSDtF~#N9$cZGJE#N@$hZ2uH4()t?5%M6#5duzG`gT*d=s5;w>Y`O zR5--LyAxRe3a?NM#0tc_dJ_u@t(q#uH!;1GQ@yN;$~K|Ou&lnB+{*k54-6x|hL8!2 z>a@^9#;|;-O<)N;_fKPdzC9rYd73z09HlDCqU*cNd5g;{Jj~9Ds;R2S)yuJ=n#bRa z6JL8er1=T_&UBw5KmA-L-B4X>aI zx&Xwbn$J05#5?i+%0G+H_Fy&Ee9b%2%(|HgBihVXV+#-5pGj=crkSDdz<8@53(DNK zI>DR*?6e&_%ryb15*^9d+z&p#Oby4PDb7m0${FjZBZVINh;GA{*nUdPk)JvU9n$GZ?nJt|YE1kafDZYJN)4IvjZmkn{O#t_7!;@OQ z{Y$~#YORnRs!L3opwJK3fTEtOrg^*8HCq#iy%R1fr}YfS&zaOX0nO8Fq5VvfhRhQS zO}}9M&{h7u(0F>*u^iS&J=Rf-qHi0|n}C_V(6O744WsZ6B&*n;Nu50%Jx1);IPtdC z(9AT=+*|F&XH3ios;e@sob-#`jxC*BD#$-PLgXE<=B=rvxulxy(INcPHj%fw`rEq@ z&scg3ja(B>yT7T8l$}h4*$EHIpumy~3R`mvOI*SSoz`O=#tM$x?@ge)O#u1azkBVi z=n&7F2@Bi6nw$9wvrNs($=qAR3F2C!{6M9UYR5fsnV!J6LyX}BFa^DE%eSE2Dc;J8 zUBb;!o;3alJ29qiI>C3$4>U^T6wI50+M~RAxv&i1olU^}>%T0l;gBrPP0J6lwKj`V@~ENiJSIdt~@KD)QJgP%LqtY4Pi~;35^r|s|`C=rlyL-M_sZFPB=&K zx7x4@j(ZI!i{)>d4uy)FiN2%D{n3gXv&n$8g-)e!`wq$Q35UQFkecW=EVxDs3IMyB zHnEwPKDydq=!xy8H*62ukg{fb(l@cHQZTV$E1_(CpS3>dw_dYhExVV?*-9;)fZpo6 zPU%>FrWwi(1iiIgE7qV4=%cQ~WKQi~(UO*s7-zN(hw#FjY7gq53_&30wY?Jx;0gQy z52mf>xsAN!{!Hg?51twXwrUe|tDJL7(<3hHk4~_%dg(rZrBe{DMPBZ>kmERk{;c;! zyDwZ5S5U6CX~57buM!Fm+uqm`p2W29PwNK>KMpw>e`em$@%DP4849_`lD}EqEGs#UxsUs`m0}6rqBAXA5)(G5BsyfP_9q= zx4%lZkNdmdM!L`Yzwbi65B$RqKf+J^#~(Y!kNnG@I?B)d&p%kQ5B<}h{L)YT*Wdis zkNw*p{i@IX-|scf5B}pHDdJE5=g%nSFN#$_40cZdwUG2kQ3c45pF>dv%AmJ7!3p>u z9<@OKKmia?f-NFY(BMIY2^B6>ScRCkK@1UcQYJ&<#fwX19m3er<42GNIUV~G@?b4w zI83f&+0x}pm@#F}q*>GEO`I3O?Bv})Ob}VPmp+%1-y)bXy(y3LiX3bD30mK5m zd2?Fzt>=Uey&W4>TQ=>en2_nNT~Om}Z-#AOn*CO{H-eiBI>v+?>_OqWNg8}kRXFoS zX=UyHQ{CHJ>edMyKQBW%fm-Sbd>s!FA^YUhkt>J&5O~%7jN3>Sk{sj2K7-8r@3-Ov z+%K*8@^i2~{m3gw!RroUFgRquTd%%DWC82BrRbXtEa7nYZMNv@b1FjUY#Zyq*{FiB zMv-PCNu#S;^YKR@K|2kvAd58e$gQ9%tUs_gajG)h4*qf|B8{AyayqA~tBA+Odd!g^ zfvDo7q_1TBFrpM;8w?>WEy83w!N?2-%@{x2F20JwoUWocVTChSv&F!RnEln&+O0!QXiD@#+I=KX} zNjW1zv?N8_YH(FZ3Ia4r#~igt&*Ml{)u~kB^AbsBn{}3^A$@vwT57B9sFqh=yh@{p zpnS_9Kw;C?Aa2{jG)j?{{WPk|z~!h{38c{yB;gDs<(APq}c*tykBmL=M>CiwT}@ z&V+CSmQRHt9#m8`8Mbz3o_jVZ+Mj$DdT1aWvXsz(z5VP%wc$LYIkIjBdWUS+E7-pBlRadp~?^WTk4dN2Kel;ZvArb9zBEm_jKLn&hD1K z?R>gph*9@tzhB=S;l&5nnJHpYg8n}K?*S(`vT_AlYRTs*udVVdvzmEq>r;~MJ%>O= z{eJ+`g>*X!U;zzi6u6a4WA{TyxYDFRG|g{n>Z*)bqJ*!eR3R(F^IGx#v@Eh2&umL+ z7wbWN@#5A)1>C;};q5hi1DGoj#8Si1%a?`aBwAl(R7rVDZ; zei2m3?<6HNtI2?d5wp<{0e3(wW^pJ31cMg6_(hk%%~OuKl0$S>FM(KzZ)PHx%QV-R zDE6s3YN-V=)N-;hv8hgETq7o!7{23?up)lM*b*jTkvMHeR()C`9LFN87M_qWl%mqp z{$F=UX&V#wEsQp5fF+nPX`q_0sl6Gtp=f_|g}nkORUF z&89dCvLzr#s5ug~(n6lpky#pPF$uwlhui~~d~9ex#pOtin9F1^lH*CL{AN1}(%Uy1 z;-A|T=Qo3Roi^deLL{a{L1H70Gc9r)f>cJH;Tu?hlyyvyf|QscC22`}=0=kO>0l{k zX-nraQkTY5rm8{dOiKevo90xfH-YI+efm?l)D)s;-6 zSG?v`uQsXcUj6!4zy>z2eHCnB4SQI`UX`$kWo%;|`&gDP7P6D2Y-KHbI>}yEvzz5? zXMdDg&xTgCqa|%zKwDbWrdGA9b;)U2``VTWKmY`AKnlEYTioV01rBgY1XOTa6-1yQ z89)IGjLQNI@D{eq1*&VA``n9^VikH2fCK6Pice%$yV?asB~UQikO;sFdB|=hwvd7b zoIn(;XhnP7TMFpLSEkODZ+!{C3UX|N6<#<&C4%t_Uj*0}zX0%nWl@I|gqH($_=JJ$ z@Y_BVgB%m4{_uohVP6b~l)f2eE(>5W;bM#e4O_@VFarDwV8}ui|MdkhK2e8|IDiYE z0ERDOQHkB^0T$cHFE@(84GND#4;&^kFgILe);gdRAKnHrO!0*oo7fk@s6>@%{0k~? zLAe+aKpp&x;!4y(0DWM^HpamXR;*$bI|ea)k^JTZ9vRN5^@1sYOb%A`Ap&*y1b|~v z2mXR#766__9#R0_i=^PozmSC<9B_hFNO&8q@PY*B&LATNOr?1eJuLBix{MIS~zicGJf4h;x(u|WrFV|(@l zWae@HF;wA(Em$HJ1pY)G8o-4wyLi}$P6!ILKw}$M!T~GLv6+ie3KVES6vW6cEc$?g zWXHQ_$d)%}OHqt*kb@iOMgh28yjwc>cfgfU0kIV#01}({7nR_G0Nk(&5SyY5Cvd}u z$pH&fu%O->rxv|AZdo+k+rp-30G|h(mCsFDP0TE&jw7ye)8Gm{+(3@$!E|PX`C+ffa-= z1sc4d<9uUW1|YM1 zJq$s+lEqb z02SQ4Rnr0&tg+DJf(USfwUb2tQUfE@g9xy`Pt3)T@I+lSE8V-eQqTY`fVodl!xu{d zH8{FrSOqW8fO5OMQ78jk>_uwS2{5e2#;UF|gar;v!xjUuyF)NJ{>-#Ia{#X!!~w)c zbsVN^Tt~zb05pI&eH*|N5WWIqv`TnFQIIrZ&;torfo*I?cN|EC;6;MeDhbHK5c7gp z8?-5_1TA0z*kij=D1$OE1$?_WQ<%n>Fak*^gqg^LX_y8cxQRQ61{yfX$1+Hj#H!b0 zH;r6@@d}1u=sXRegQMeyRmcEmlslgD#Yg8~T3kCX|dJW7;o%EV$yw3Mno__}%QgSR^`^}0ko zkVy?hGd1YJmhi}tJWB%L%V|i2vHVL(7)ik#O29lzku1snC1`|15XrtQh)Ed9X&6c^ zV1__2jz-ALqVxl17|D_>h@u=xQ&7xkFonRBghViivRq5KQcKr-DlF8wQDA|vWPzO9 zz1z~Yk$VY`TulO)glHhl;UoZ&1WAu9&f_FZ;1o{gG|u8I%j7JG%LK{G%*^%JOadrM zl$_4(oQBM7OvMb$g2)5EoCM&EO}K(h^=vBmo3LIx0Rf-`Hgh%>AVD3xyi({umT1k( zCF{bSw7! zP^3}=OPsKzivT)Mg<$K06R3kSW4mYL$d7EK41= zP#TcX?4;1p4A90LN(N2IA^lD06w$W|Q7QeY6Hv%fRDlw`z&enCGSI+DD}gJN(T*6> z2Q5y*1W5?p(XlksgAmCeHO+%SN+a!10u@b?v{NF5&ZK0}0j*NCqEbNBsXADNL2SJh z$jyt>0~UBsI;^e;I042p2?w21qs&f_tkfmFQ-ruo&rH$=z0@exQwgoq0EN=>G|fJ3 z%ugNEv=UTTjj1toK2|`36{v$M3^C*?!OJrRhRlO1+k(B6gyLKRN660RtW0E;Ol9>> zV|`BNY*ON+PRyKEzC==spk=F&R6+yUPPIy$L-9jn>Rqu)RQ$(&q;2(UUv z%!BVz+x<&hzr~^zs62Bkg%wx<{xIc671#n$kOfxb0&>$qiYx;XJ3wEkHlGXJ+5=q8 z4WI^qgILsqGGGDvw6H$Vfbp`sYAXN%5V;GqMF#){h5Lk_>s;L9T-!Yz0qDM(V}*%( z#$phKr%S#9bFnSp0Ib!oKIpPOlR;Nig>Uc%QTRdJ&0W#?MXqarJJdJ1dbMPz7(GUZ%BPrV;>*Jk$ngfNlH&0r)nBlLbTs2EH}NSnPw$ z(}K;r+&akI_KXJyD1mtR03WPg`4yT75JZYpgItr`Bdo_o1BE;=zz8%y7J$TFJps+* zJn^m4cqoV!j^Oy6V4!mTfaa4hG(&;NZMITS0TieNRhtE?{DMkA*<*x*62-ISjX^3C zGbwH178XGXhT)yT&0S-K{QCm?`$;t504|t?04p>VAb{#p!l0!C@nSRmV%_m$Mf3aG zgJfc3Fa}f51F_S;Cx&8bQL@47)k<@|R&aw`WkmDhE!ny*-^;QH&;uL1;4d&h1iaS| zWnwim25=CESCHfFt7AKM78*?C5OafEGeT34fK@~>yhDh$bI3wF0Sd&o3*1i?G%;QU|}zS19kq`$xsWA88s4wBE5_d|xLK}d z0=VXTw&&aJW^Ya@0ieMAlth^B7l8OMei?}cFf(SFwVFThNbZCj-JBOq)0g!IxdhySSSW!5Ct@7fMN7K zZBt6(pr7T`Pu+`{mwcfe5ezzXP*F^9Y0!xP{Y#253|- zSbD* zTFz@pRZs^uD2U~jh}S;5*p_Y4po3kjunFr`4cOt$b1^PZV2pq|L=-Ub9&Z851EqWg za3}$caA0mG9qDGNM7Hil{eqOs>=(O5lQ8W(hy~yKUjP2@yF*K2Ctpq(#UzQ-TD$8&U-*EF@OJiV#i&zC?IPhpma1-|_J&1=rkO**a z{b3m7K{@n9q({n=ah&~T=MPGD3AM``_G(&%MhRA?G zH-j^{bWG25P2Y4*zw|Ny^gEaIP>Xa?Pl!bK^D~fxK3H{DZ*^CHby)8MIZy*Ycl1&( zGE%?wp8<3?I0Z=fbzl#6VIOv4hXhoJ13>R0U5B$=U-m$M15_w>X`gmrM|ME|Uv_4{ za%}I5XODJj|Mp?G_CMctBx`nE?{;uc_i7Jxa_qfTaFy%2ts9aMC+_ar}I>=UX_9iDB9avd%ynuBd)gg*X@Gt zt_tps*UlbCjLto-p1Ifdwbxud*KiZp%G>Tfxo*Cx&VD0|{ur(S3^&FCH=GI{_u3v( z_8y@K?ry0bw*@y6_U9}PfgDht@d%z=KdvGfuJ|!;q7D zV+{i{V7O9=1FN2S$Cmfb>N^;#<-1Y~`Z5Zaa*BK7<-kT?XAZ{KRsyfXa_=f)@0ldM zno~Wy86uuqX1q9a@7sI4S}EMxB<_1Gs++gZdsD9lQ@z@+-wj|qq#Ae+Uwcg=M2z@) zb=f=jmwI!Kc#SUKPZ?AXkDO0SJj_^}&r-N`5IhbLT#atu&nr|faGWoeJ}w!YFB4p? z_&R^Q_8#ha|8e}0Y5ToV?fcnW@0Hqzj|@Kh7<301&Yuw6_`<+AR(wv~12b#y`Hy{0 zeZlzZyw@SapR>ZpE+#TOR^uF0!UY!tF=r=O*FTriO!fF?wD}lH1gkK<_G2V-*r8I5 zgC~~tuqGT(DMc0zDuSF!ivXuLo-9KuL^tPU4}dFYQeed@b{aV8* z;TT#iVzEeU9{Ei3RFFff0ABs1bguZKOmI;J6RvQx5#dB;o9-L##?LYS^z>FP_tUHb zlgy1|y*!OnDWr08DuX92CsSBbhhvjJjW6c%E%XWWG6YXEJKuw8KzDfP4j;}GD%9%p z8TEhN9*AY|P^yaaK1^7}g`DlA_UOHv|nyXwPjiO%|93|)+BGF?_XKlZ|4P5JaZ+qTc- z8?2LkW(BF_Vk1ksI(DcOu4LKJ#4#)JQijp5D#i|TGUw8Z@m-FrWb0LpGk0uO#q;DYRN{8}7S^e#0e4nOuqmET7UC7w zqZJu7*p_znTk^`1sg&}iG_f|f)d&q?W;7qlal zl9kqE%yVxbN|jkZ#H81@eaWG!^0%A*^i6tW35y{IyL)wK&`-I_zBVgD3%{RbUbj&k zjKi#Pn&-LlM}IQ=M!A){M=QDCZ8s%O8#DbblwWX~WPX%|52bb`edwK%#PC^~4ogW2 z+`KLwzZ{{QlmwLB#F6=XqFCMmJog69K%&-7cNy(D69E=otR(C=%lX~ux5Fvbuy*b) z=KiwLyXC8Nr`vm0?`TV1hT3U5TX~d> z<;bbp@vU{=4xPH~Jf;KJqDU;M{YWYubF7Xe_rbWDX1nwJou?WEw*8<|*X6Q_{d)q0 z=wNAr)xg+C9Lpr62gQucQW9e@{_XC2U8d*mhjWZi$&c6ju{}5?ajw$1w_uEZf?T@R zvw>KLASK-Kg#=(Ynn^cYi~-1SQXn)QJ&%{)ebF~|z1W5TU(6+>|pq#DEPOdBs938b>M4Silq=@v{KCb3LpGJ7X8lDXS27 zyO$4l*gxhSZ7%fY@rc03&}cv0B5!pM2(AmF_z2k|{8LOM)nlXh$fUq+P03LiV`Qd) zNm2@oMqv(4C!;dI0T>>uaTRN`#4=qn1N9q-z#rcTHJV6iY^)~o%YLS`S4F&hWnW9( zA+;aZT-r%$(qw5Qs5YvYRwjJX0(m}dHtSIS-I%N-h>G9B202af*p$7pVtV(14!b+l zw6pQY%wy3~wsfuOhiaA_-y$7B5D=nU@K;bE&mgo4pc?SNLa|oIH;(2sHpg8Xawl!gy`r*7#PIAAi==+55dGF#l$4}&%weX1;ECB0SAW^ z7nckVkDP#joRE-$h=`Jego>1unw*@5l9HC1nx2-Hk)EE3iHU`km5qaggOih!i;Ih! zo12G+hnJU+k552AKuAzfL{LanNJvarSVCAtLPSJDR8&$-Oj2B2NqN=K@rlzK@uCAe>p{c2Cc{uWw*rU}$J)WMpJ)Y;0m;Vrpt?W@ct?Zf;>=VQFb;Wo2b;ZEa&?^Y-mqTU%Q@ zJ3D)Odj|&xM@L5|CnslTXBQV2S65dzH#c{8cMlH_PfyQx@7{TNd3k$#zkmPU$H&Ll z*VoU_&)?raARr(xFfb@6C^$GcBqSs>G&Br27Z)BL5fKp?85tE76&)QN6B82~8ygoF z7at#=kdTm=n3$B5l$@NLl9G~|nwplDmY$xTk&%&^nVFT9m7SfPlarI1o12%Hm!F?s zP*6}(9qD> z*x1|*Jdo}~dwXYRXLnauZ%I+9=2}L4srJRUDd~swdbjKQs zCsH953J#zeOQte8ZO60H8%t+$1<6hiu$s!|3OPcN#m1Y;=VPQ(`Lm^)t!0CDm9;m= zldTqfx0~GXWYCY6eK!YF^d?#kR(&@@nzCiC_owZ_Lys4AT5ApR!FyikUDl3P^@O3Y z@KR~6!nMVs$>c=kp9zq6(G%oM-hPh*!A%~zNsSginvX~~y|(~sw&~e44okmkZ~3;~ z=KOVQs-yM$c5fgofqZA%+5TuEjlp#1hx4P^LYZ9ouJ+5XD~&eW(_J0c-**O%A_)|_ zJ8#dA=PM0ny1VYLf9y}>D)e+e-rt;m-Ja>`d42|gpxp5TN4MPZhaxZA34mii-U&n$ zr`!!fRkz#?#xyV64Z(Fg-VG%Tq1+22O|jeyrz|Pki=b^eCJwwGY@3Os9pfa4re5_R zi6Pl+BZ(!xg(8*W|H4@$A@&7xKOP2kqAX&xw^QS}ur!mD8 z!xXFHwAWOtv+0)nRtIUe zt>X&A$iI|D$HzdE6(vjGkqf836&=C4;NLV5P{O}7@RypA~lQUCL0I&d1 zQnCV2QLzC~Qv~f5}}(M`u@8S5J3OZ%=Q3U*AA~ z|6u>X(7@pE;Lykr!0_nsFQFR-gzlHt0dhAoI`JoeO5ZO5oE#l{0nor-OaU_ZqJ)49 zz9`|$)b#B1%-qcEixPhPxG)ds;NsHq^2*BU+WPwX=H}-1_Rikk-r?cl@$vEJFJHcW z`}Y0&kF&F%=jRs}msgip*Vos8M&91u-QV3myvXFk{4ITSjQ=Wq-j=V% zdc!eUtT#sMO@v{eY2V>WH5847!#>=epM5EsNM+;;Af;q}kv^p9B2Okj`pDgXf=f4* ziRX(%Q|q-Oe(rH+OP4nLa$(S3rqT#neOHUCUTtXW^vD4(wbEz|TT>WbRISzIpfIhA zkyo6M;&=}6D3O{D+u(Z%ld^hkfUX;I2CpB5B;^-5Df@7a9$0S{qt*G?==gyJxZr5k z@LaaN>GOP~{E2n2{8d@sJ@bjIfP z_&nU-YUKLez6n$G{|3F18*t2YljDCy z^dkLcTL`}flkolDHRl;*>(v~H44^qMsIYLDZ{Ts@5%3TY2@sKp5Rr+IkV%kGNPj_w zg!1ozj7o-#O8)PIMvj6;@dxM>DCiWZ02mZ#0GN~*SX7ue)Yy15xCFEWM0CWY45Z|Y z6jaPq)U4FBY_xP7bPSyIj9d(iT#QWIjLh6j%-qZ@+$^lztZY2&96X#{d^|h?e0;(J z0-}OK;zBPjOHx!+T1-qvOk7S}LS8~rK|)edQVKvxN?I8}S_Tjn6=_)&8Cg{TKvm@A z)#Me_UnJ$1swgRIs;FqGs%ojJYinp~tLqr67}zKpxk#G$3z|lAo2Ri_nRGv&i}@`v4|_Wh*xPl@e^ zi5*7?9mfeBpW{2e#CLp+d*K8ivGYqx=W%-Hr_8SXoUYycuI-}k&64i*^6u5j?&a#9 z#k!t_hMtd2J#)>yv#q@|A9|Z;_78XWkM<6Z4v)WnIXV6I{p`ot`OjZ+bp0YkfLr^+uKgPg`mH(6U09NT*e%tv zqujsQt>2pS54#0uP9mksA9jn;WIRjim)&yyvN86{ZUw^N0h%*cD49sD_hPq76$_;S zyH&ARso7}Fr)52kZmu)%B}gfLFAWFwv^F$`rXEDkws*_uM`-3a_kESRmmac55$^ahP&Hign3y zKbrHfY(Iwg>Ucj^5Q6F;P840MP~Zu36*WPMx?D9uk~=+#M^%~XlakyM%BN&ev-09p z-6!zF6r%_#q;#{N;D@O;C7vpovh5#KGT)A;qhuSbg{!2Hro?LsMT%%Ru|ZVi_A4ni?CMo0|HzwzeL&vqAO0(H{+TyloY;#E`)#_$$6o^G z)YQ!EERZS}0Ure<%FRt6QSR*T?|=IA3CNY7KLa-E%h#`80V{QSdiou(Q$GMhb@ub; zPatnzUS9sgQQZPBAbb9bpMa$O@>VbY>L2#%KQ~x^Ghe@T?-?4EQYJD@7wlKe+!dtj zu?}~Xtl#U0`!?;!X;Dui1_LVe4)=p%kz@kpb2{(9Ay$(m6;Fn$Swu8EZd^fM)atCM zq|=iAPVZb8g-GtRWDsg-b?{sQ(Q9fs>`N=*cwF%5IPFU+iDb}vXmE-+jpekab_Yb4 zQ`4DJJrDo+!oF{FrJ7;T4`hV}n?X;Y_s+)^V(k8*0^F#)EBV9}22|F%lW7I`+~|Sy z=qqV^nM!DC*{ct=jUtx#B}#AwQLOvDZ#Z@dCuUL1mup`$prV3h^t*+zN*1oPd7K6p z@*ZoEf72ktb8k_jolb8&4L)Gxs?RTuwU1no5zxS}cQw2@|1_6~Fmuj%R(dMc%E6Dv z|Mut4qf9%GZT_3bb2&&u+2u11LjOmqB*S;G>gxE0AJA^{JkK8gkQB(5=IRM#^A#Eh zHb^N1#2ZK`1ZZd^7+4foI5fC7m~RlU;Sq5W5b+U_2oRA85dkFu%oPe?uKx5_q{t}0 zcrjFe1C1ORoe~*?8X1!o1)Bi{ml*|*4Vi!wnUEKmSP+?12$@s_g-i^ETpWc$5`|I< zl}Z|wN(Pl$7L7(0okk9WRt|$s4)YgS^m5qrvN#N~cnmW5j535w(!|VCq%2b8tdi7h z67(Dr%v=(z+!7qT;@o`V`~qUX%PSF4Nl`H=F>z@L2^pZal9G~>l9rd2QIL^Ql$BMK zl~a%KxYLMRuhwdNXqZR zY7U5LFLl+goCY*jFHvn}b#--ZZS8kh+uhyU-#<7w0K^6eZGhT*IXO82MCO;gTwYxP zf$djb`-f5bFO%BegygS}FOqTx0!Rub&3K9)AStS0d#Q97uq5OQz4CgHu1^nAx|TwD z!BN;8@ZNhIUJq@Gv?4z-%BC}rSv%_82!6ULSFC6zf5a@+F+fLjf!?1o06PX@08 z&)rt?mv~HvOIJ38^yf#~#R0fIj(jnJb{J!VO$|!CbW7@Kka^U1}ks<8N=4 z@t%0{q!wQ%JHxeEL|1%3j$})15S&E60(#N0mqrmGdoT_Xw3znCRM{YsMmm!Wn>OUz zbQEGPaqis`!cWT2=b}gT!a}@7Lgt~CB5dn060Wm>lWVooqNh<{-9K>2$ps58!mlu0 zMB`S#>?iH`2!A;Yq!&530hxP+34-|_I1ETgNGK>MXlQ5{7#LVsSU5PiH*em+!^0yW zARr-lao_WP*74*Qc+P+Q&ZE>(9qJ-($Ue;)6+9BFfcMQ0_`<3 zGcyYdP+_yOv9YnUv;Rwx{nBLf^Ya5`wxFP(kdTnDu&{`Th^VNjn3$NjxVVIbgruaT zl$4aTw6u(jjI6AzoSdAzyu5;ff+A33EB%j)Y+Vy8EmH>#GZzhWH*HHt0~>2|TSI$$ zRYylfXJ=VgS4lT_Q4bG6&v$%YUL4-d7uWNn3TjPLx_VVPS4% zWnq1Nd24HJcX#99VC(2;=kw>iuU`*NPY=(|j?d4(Twb1BUw^x~IlaC8es}l%{{9ER z!^7#r!|~(e!Qqv)knluiIe5HnYv? z3HgqOq6Vig6s#lue5AHuDB@|9D~J*icPNsODPW(HzDO!^4?h5m3jvuzHf~{YLKIrL zID!he&&;ykjMVkvr%^;xj6S*R?OHODB)zGO5?Zi$#CYshvBqo9tDq)&YF^0%?#=z> zhD4!6x(@Md4%LMACdfIb==RzI-kJ+qe_=`u+iqDgjv-VSsidW9A11i~Q7K!#v{0dL z8q?#=u^^&lp*7jj&4Z~tkyIWnq}eX75;9y?}y7f`&nXf&FD1;IZKmUW@|*(u-^ORmuD|4!>KO zzq%P@6tWlo?j8R5GYTph3K|*e@0fy4ijF~w0VEY{GAtZ299(i-JaT+|3Iak(B4R2a zppcPMQ&Q4UQ@@DD|4=Ugxeyi-5)l#>6&4W_5fv8|l@Jq?00iUzu3r4-i!Uo%XA1{! zGp9gfmqYD1B+L~G*kpN4t#)gI_VCe-E z!OhLBKq6^vYX^J-0I&fA+`~&WdD(;Yba%h(!G?N=M|;L5x+kW3re=Dk=Xz%r`e&Dh z=GI0(ZcWYaOfBqBEgnuS9nP*DE$!^BA0F>~{d{ox`RMHH*Nbo8uYa81UR>SZ-aR}4 zG2&mE+PkNx+oz}dmmfSl{p*ka@&W(LPcM4$1F#RcH#51w;tQG@H>}QR0Jw==nia~i zK+G2gj~!N2N}14Mk(Ugn2`WqAEfS6$9P3mrr-FYD-vqdPR-GUynVpNNVpef1#iNvS zQ(4}$VCp+$SeN6EY`!>DS{rFirH!(o@LGl3XjU_CA;RBZY2bNz>M-Ts85XeOrqF)>_g-DnyTY7N9(9~?f=x3UQxt13(iDqT<6pgfZ6qq4}IjFSZ zF+HA9sHBVew_4%@$n~pM5E775Z~u^YzCrJN0)cnnvtMw;pZJGF1cpV0M8t$g#YM*@ z#>6N6|NNi)4a6oS|Chu8w~8dD#3uoyCM2gOrlcjNq$j0jB&TJjq-UmPWTj?gr)B-925vrl_yKw|}62VDM#4 zG(0jqGCDFk_Ob>a0{|A_FN{x20$~DJeFNK`kMl39o`uCF0ASzqvhCT}*xCk`J;3rC zh!A_f*axD--?GHf(eX=n@f8RZzq*S*>Wg0m#_tZ}cZ~r=jq_hGpwIaI0$L5=^=Gm1 zyVdy9G)Vr)Z4elT`RDDoSR~{#Vdv=Y?YGZlnPcJTuR4}1&QvOq_N9)U-9En$RNeyY+xUcSNquVV4r$dfV zstd$SvXRD*tm@*)rc{25u9Hh^Y_FD$we)YREYUjqN3#r4nK6;tmml{kndVbAFRpbD zrd=w{ZLyl1qx(M*3*>O$&TmQUJ(V}|U2`05A6Hi*z{h3@+F(~hZ@gE@7Idhex^*tO zeI&SjK3U!tNPZog1p9IpDljbQ9i=-V1ccyv zR1u|;a!y~c=mkpDmzYX@If2OG##)=-^+7;CE`D2&)~FR<{1~bg3x7qD!A<*=4{zxn zkbLM-&orPLCv9#)VF6CniWk+$#~J5G_|%9K7wl9rm;YhvD8ieo8@$w4DRNjq3 zH~j7*k;E_MssP@8@=XLD@6&A49a?a4{&2qi*Vk)@c{nqm#5vO#j0#9oQYx9B>^#VTY0QKUaz15}6PPYJ+@0NSg zY>wWVpcQs5ccRDZf{+Md29*)qUtl;v$t@ye)O$X)e8aq4J90|;mvLsMa|7#6vye!ew+mT?Lm@;9T!-h zAd8~lUyb&kql1)+{(lDx9lG89!&@xfd0S_uA|YbpCJR?&;$G?oV7jyga;oynOt9 z{Db_1LIZ-sfbl7I=5lAfHJk(!>Bo|%)K zo1b4;@-jCns>&;?|D>X-x}vJ)Hz<2-#1TZ21fH?uM2C%*Xu)eVgu(7$dxwXCZ zkMZ$)f&fG0WsdCa0{}zh_Z$HVDF9%O{0VTV;7@?z@-kmuy78A01C-;xrph1V1ehei z3;_x(pu+mw=rAaTPX5=?LGj-m9mMtAvfQGW(oL2@@ErfN$y1|FCyRY`Z_6EwlW2rrqQGix{j+?RJk}HZ%0U9UU?`a95};3H|g6jOVAS zhtT~&{^)wl^PDVUQ&AHOOAYhFSIlVdG>$)?thw0R`&`2}+ePO~^sq+1tFc~^i)~$c zn|U?A3&zxoYU@P&@0>E!6z()PS{3)Q3CnUE~~yzgwE-Vqn+om zjG*K0>*4phDd7UoCxJTTQ-U|}zUhWwkwI5zl(`zE7a>zrDO9aF4#h^CLgZJd#(Hjf zOc?o=illx${3YjgW${|DrgKhYCyZlp0H+2H^=~Ny1VkM~6TAYn zl%9>7fsKoS70~NnuyD{bvNF;$(b6zbP%)BHvXW9VkWkW*P|%WK<^GZNx36XUXw z;IWh8^H35A(2xkzk%5EFcIf41rXyj6$Rij69eEj6Mw<{54bGExhy5Q z?PPcymH5r&h4mH0^pvIb)fIKLRJ66!baXX!fWuz~#umm#PWs00RIS|1Tx{)qtSmyz zbfXNU!^L=lX&6FDse?(W-w`vr<4c8NYbFsG6cC$M5LhoXprv8g#(f)hywIJ)|NZZvEx8;1F zh5U%I!kngp;_~9M%F>Fe%9{F`#@5=Fj{462#=epEzMI42M?7vh4kB|3{Pgf65C-+ZB_m7|M9(J!Ew=bWzE}u3p9+z$(r|%!f z9v+8p?xs#J=f3=$JUW~B^nK>=bm7yt<-Mb|t(~=vjg|HFg_ZT$rL~WXE5OEdZeej_ zY4rP6^Yu>6^-}T8bn5kZ?9aZ?<3^8zYP-!6n}uxasU+pG2%bJajygN0ato$1E1qg+ z&1T?%j6RO3Ufv1*J_#XVF){J+soBYy1%Hi-fB%f_KOg!?D4>+v>@NR(#`gE2kHZN& z&JMH5CM(tI+-B^MaqyU|za9EGaQZShI&YyEN};m0G2Z-js+?>f0#Bypcgg_H*jBH% zI&i$-KR`*>Gj$m9K^fN26xR~rdDWGnh{7e8t5~f4N6L`N;Z4^y=)rttfh?<;uiM^y za%k3u0UV6|+d;F?%_OSDDlI`Xd)g^?lX|%bul?Z_%w17W4_z%XfW>`+{OGXKZ~x&0ikkyuZRq+M<3vyp-+r z*BM(RtB>J=(8`jC-gvDgHGHXnr}l#AigKFmcCs3)Y%X4(&J8B8}YEU#lB7 z!}GJe>=sJXd{fe-kflt8kMlx)Vigy_FU~0lr0b@RHM%NNB_y{IvyLDhK#5KxI){C- z63dU#w$8xoY8+&(0=`F6O)`{0(Fr}5`K{}wyO_oc!GHg)fUBPgsYjRf#aE%e1SrUQ z2iytMhHm-^1`!|9dWE8b2U3`NjQibFiIqdx5rHSk_He;W(=RguB<|XW>Bn|W6G&!t z*Y(0z`lj~Nh+|)8r#X&jE@n-?bx;ZcmnqQJg9`D~*}b-KsS|>J<~V5_;;tecgpl#R z5PT$6lEIUL(uL)eQq2?<5OPLk?#@498ANnNR2|v4SN-zysT3=H9EnFHZrr)cRssBC z8Toc&c2|_+)I(cbWzwOk*|fm{86SaOiC2x)gk=v|EP8I?wc5AsesQw7x+PL4y5{9K zQ~B*5vmOSekRNDq$&X>OQTmC5dftCm5Yg40Z3$Ljtr7RJS(xB6_NciPnx{CmyM(2Z zuWVKjIDNb(601R6eNd#Y{0|QYJN!aHc5qR#=e1dyT9H;)4Sf5dU_!6)o9{D;DkSu@HW5Ir6+(YgFa0kEF zK}euRBZx{tbkH7c@c3biu~Dw#c(9s&f|G`H+pl8XpPmtc=YQ0LfrI41jm;vRB{($r zzZF(K#Rf;OcooKEXoTI|_L`_F>@~A07-czT5L%;Rh+GmhrE*aLT8Cknc93JVEp8ZU zxnqsWA~A-^aR93yau}=OD_A2-k}z`@c#I6$F_l4wH z5{mX=KUQtaa z(YmX?iHW{flm%gVx86dr40Q$QKQV<-RNG@HR14#v6a_J}b~|wS{jr-ZAtB2N-%LX@ z!UUWU2%bqH>PO|7KZil0 znGWZPfZrWL&#p0?_7EcejA5gwx=8hQF_zwQyd5{NW(X z78V(Xgv*Stt{K7wgB0Pax&zlTTJl81uLSE(x&xPq3C^~c@5g)`fIohsFD6P6rx&)P z`pu)CRr{*~-!oWCy;`<2y7~G++XuL1L3qlQSHZ##1sE>9gW1b6x>SaR0uzs+xv%Js z#j#A=AQa@P*KtjNtKtKFk|j81WzzNy0pKFY8Z_nKB76V|25A%i~4D8SMmEBAc8cY!JB?NRL@ zf%qALmmVyKp%APW?w~-^8Xw+~=IFCHZaf&jsbc%$wgQQh))CoKJp@*mY>Y=#mMkKr zjAxdbe1EcULJSNkF`itRIPX(8p+H@NJ9vz!y|X~6wiVQ1{l^Ch+yKFILO4?R5Ok>A z=e|i_@OQI4_+cDfmjvQ0Bga!(kWevY1n<^VxK15t9Vd2j6|WzTgfO^a@d84*7xGv1 zWAoD-$_9LKiSeK{&zSOi!^#m@z#V4~M>cpw@q&oLFOo63tck?L5W$Bn+r-=Jdv|9% zVWKq+!4-LV<3x?G*3-I3b{82d`r^ha_0Ng@dpZUrs)L6Pzg}n76Kty-1Hx_XSvSGQK8Lln>56*E41Yw|N0QwNB zFT&M7meA7h6|d-buF0qApVQDq9nHC(Q)W5N*@9;OkePT`n!H?c11j+RdrGfk5jBdv=nt!jHvUr*8s_4bN_0A z__gII46kbCqFq?E6kIT(T|xE_-9 zLu;^7zZb-^bOD;Txh4Jp2V(o6%=3=S$t&437RjHAuP+=V2b&PagLI4>-k^r*t1Q3I zIY-!8rrxt~{$%0v6l9J_bB*vr@%6+Hgm6v7Z=sgceF*oxxc9vX_wi$7u zf`a;j6wB1>G#u2!g1)!$iKP%}OM10($iTN!HWP<{kHlEq#0mvazzXpnI-^OUgud;E z2btAt7SwzNA~5(GBC8G_GwwaHA_VNBd`KO%J)=c73Tj{*F$)G{vxHvpXqeJ0)T#>v z1y?u;vCzSfCt6=*_Nh78dqv2NIQSNJ7(91~tR47db$H-5f>)SP;B)fSSfI4G{>lM< z@csrJiHLQWsDo@a%_Whjny~yu@MxduT`VF>(O`_nV5GrOca2d+c49AujrhoI-B}?n zz)&prC5%d(VDl}Mz#Id1D6Ma1{e@G4j&3BZuMv9k0(5%e@_!=SEZGNh z88?rZSdYa#9LKtn$#t~Gb{(0dLdO+Qd4qmHL@VS@GT|B|mdzHD#YoeKt)m{fc%?)L zNqLR(!w*DW@J%*fLWp?y3x{SZW@B8I6DN6WJ&)BR75k`m^DM@Mo&F(rfM!(IW8 zp;t823fH$=vVl2EE*a8+1rd-Q#OL-0Py6UoGm_E_0)u>o(8>rg-;$;|D@9W*2^|tB zq!hj25$#H4XU(k4X~q)Qlq={68w^Q&QsVXr^PRWzJMu$uHMN4hL&H@@lTAYtzLIn( zcaxt`MSR$4{9nA1v=Y>Aq`Qv1^p2$pZ;LE7GhmtM_7_-ROEFSNA<=&(C=Jg@@3l@F z;1>IwgXVAjx-SAwh<(S8{c|C@K{$=TF67W5BvO8$chYOi9cA?cy*EqB8Kw|SL+-c{ zsZVa{sjFf1%^)TLNs@Yg5EQPjbD2rL=Ye~HI~+RN(N&Kt~sez5pqfs(*OW;<=u7;8*MM!BpPLMxb zS;@(Sz(gw}!eu92vf8iiI-U=g0-+J7W-1tyb|4{c2E{~KR&tc@5gxr;Vi#^L7GMgi z))Zi|183<9FX0Cv=~n)eU2L*VrQ-%A!?1cK;!PEd@xx86bc92KXN4F{5g13vYGcuJ zdwSztogzswcSEK2TII8U_MBGLF?2PGQ3*~#6#`P?qaeIl8xAgP{h%lMo+LGL-&0U; zC|@X4J+lsea|IFVdOdn&J@*9eh<5$homb&m38hYh=^3w)pB9%edFtpsPgLd_5c85iZ z!66HS1@0_vZiH#6C2F&dYzg6MFFb?Zti#Wt!H-%;JbwifvIM=8N03-qpZpcE8i%YT zl2HCDx{pkI_kBk%O=G1Wayeo9n}&ASGn~vkpu>Vm|M5nOxx-ky11`U_{V5Z1fQPJO z0=)~T6SyyK2gk8I589asrp~a_GOZXsA1hO_vpE4KT@ap^2gK|yuILA4COzd)AP{u>wDogdi9CWlud|EJ9@Ha-|T~+VP^GVNA)4{aw9)5 zL1y*B^Y)WIyx~;prOfK5re&>gfGPOVUx4UXng?UK-p`0Tz{xxyWHP|n+0XwlAXwF3 z$2eG5XL`(oD5^Wit{ftQJ1GA!sE9kH%sZs2JEZPCq?t9O-8rPYF{J-6WQaR#%sXtV zJ8bSfY?(D|-8uYrW7zIt*x>~4Ofe765nk3Xu*)(Jd=9<~EsRr`{K=STm2nI%pNRfjjxVkC)ZlvY0v-(_QYIIK}|wGKFx9LRiSsP7#w} zX`EV+bt@G{Of8ix;)O`~A>o~9oXZSlj`+?!yj)XE0zC?=?iv^pLa5(bH<(Yoc%DAvTH&UJ#J7nrbciQYYd)cC#r+(} zbnAm}W_=gN)ZeVPX3bqatb^liT2)UnS#MYy2BKzfpvr9>Ji5cf@1WpAjf-!FdfWR< zZq9!j-^SazKbb}G*`nRrGvi&Ywp~rb8+D^yJUy8JLt5k4SordOjj3iWeQF!LesYj* zJtTWqfX!e~53zS+eQs*!qb;Hp-L8Ms22IVzlOdO+@IS?}8uZg{ z3BTE*?>>AxJ;a)=&5=D{jW^~mF2N^!+wJPP>jw$6 z_nW51J24C2TB~!cV>UtLMrUllK0SR0F+gmXGL?)eAH)3tmHXp~|A&nEHwdLo7=kn8 zo3`Bu5%-l2Vo-^F-GvJxlgaki{1%8qmJpCm1{Yjbole*_8?e!7s3J;q6Qb@z88|Y7t%c!vfCH(1l>h<7lfFV$_AGx z#2V!`mo@>H&y|-_&+27;ms+(fdImCvx$ef#c*eCKZj9^nzYl8mT-q31Y5ojW#J(P2 zzuscHe%s@~=5%GvpyBfI+Fk&?in`9W@7jbR)>QK5*6Bv#1fO94rmo<|rKiqD0N?6U z9jEXup8fSvymPz(a*FS5nn6I8eMmviO}kLYt+PY5{Vgd|2t?^^_UGGrN0zqjL(z!a zz=1m?BK0%QIy0raj@Sy>cYI;b6(a~Z;!orbwqOs0yLZ|irV|Pe^Y#yC0kJn_UgtND zmmWpLM8OW9u45P;L%_IOw^>vJo=hg+jU&VgS)>0@@Y*tXSQEJCZ*>3p{qa-*2}G%K zNg&|$8+_3;#a&^4Fj#Kh6^GrP5JWWTtTd&4@fajXX>h8z08P`Lm8J}RFhFY z*gaGvmn%0Y6mFr)oPSzNu^M@T!&xj@Zm}~fXtqJwTWFK}yf+o8YC0MlIQOLqM}tmh z*mEL*bzSX~VnGCo$nXiOgZhLWP43f0W*v)kcRX0Gc(%6dNL}^WQg)Sd(@wH70#1sa z$LVT%-6gYmBzMDJ24e^Ib94u%>|niTj|=|IxzhYxE@Z6ShZ_5t;4Xrm>dg;l8)NCA z>CAQxcIz1XYToBFB-)tt8C#==F5QGxnu}S&DXbV@d=pSUW+gK8V#?k|F;5r_8`b1q$n3 zoUAQ(jst7c6t2-l;yBxPUe-=-G&T64E6z=pS=fB+=9%jNmH1JD+A4VgZVB6ZNu54k z#aSkrjbs@Ez4s^sAil6Inja}IFZj;6#aw1pE>B!1_(jMZ)l_neBW8OtY?6B!&6-g- zF}}|ychOc))+&pen6ghwelt~-l-Qk8<+C4YURuytS9P7iRMWkdz`1>`sw=}(y(po3 zOUtVJX|0)UcQp#QN7jjIIBh|6eoA4?gKZOL)9-s1%isrc+Hcx%jdCeuW}gb-l_QbR zSxSebaZ?VIne?ueLwBDBuqe!$quI;MP53#s1+Ve2v|~qJd+bj@*u5K2MM=igXUcTR zV9R)%|7qfSHg&T?3r){sTh6xRxnH|_dOB87oTJL|L09FSR91Y9ny7jFDaTlqUL#K# zw)+W{jGGWsjHVEdEwrn`&FYYzneu&tUwx$?XvJL=W^9?D;0x+Q%xa7 z`_V^Re&$=QOEix`TTh#uqaPa48&~4d7JAoB>M;*9=hVrVrz_t_1d2Vmh!NP4Gv9A( zxC~J{j43Em51`vV!7LM{QhoDq$y5e+UaUTmrFH!ok+Vyx*>$N^N0~96vrc@Ib>L0I zyOWu5^AVDb%63XO!FuJS1w6MZe#U26WvA3bLkPHr%U1y07!k{*FtYr!_KcS*62U=V z6H&qy5P}^8jWLHfhsm&IZ8b!R5A~PDqE;iLXkoW9 zDppWfjnAT$!3~F3EL%7O&SHGQO$pJCh5sJ_UqGP0im6bLe*EKF4hg3|!f}p_G~*)M zxJThh(rS;iWF|Ga$ucUElb-}-C|9#ZQI^t_r$l8cRk=!5w$hcagk>ycIZImB(w4Wx zWiEBOOJ4TUmv;mODt`W1m2UW<4sd`&9sJM@R)QlA{(xpQr76ugfMb|{L5n>qLX~ldgC7Js zP=XQ^psTcJH0R(4VH$Iqb@(GF$T0_KCKC?OoC7)h*~&VQW0>Ud2OjX?50B2%q$d>% z9O6JxkHV9rncxIUCjyR(GSncb@Pj|#U`{{pKm72Ikp5$z(Om0S4FU?4 z#xx-VtB&TXU$Y99`P`lpspsXxxM*QGTRc7L%bEqj< zbxKhqOR31OzMq0A}cjFX&N)W;_U)&ul4fyYWno#A5-! zUBDqS(hl0n7PhUVsAmzv38VJ*3DiJIJD{M5*j@FiL)ENc{h^R^gaEj-jqY^w7ukQH z0-9a@hhhz)j^`Fc9pWVjd#NE^>JqoM1=+<2ynvd>g2E1aP49Lm;@jWuS1a*o@0-Z0 zkdZ<(A@MkFN9a2d`(g|gc{OGoph^&M4Al=RMXx~!yZ{5oq`MIYEoem>fYknD#VtNa zFYK{^{wVN+ANmMGY-fAl262L-JT31W8@}WVqfD@tQ{q05I7J^5Kt7^ zu>JsYLUwTk7ASckTc++0t?OXy{v)T_ZqgRvG)RIG_sU>nk z3{4P3EBYW{CN(|coMKkLI3cc9$U78J23GV$wwh+pnQz)qe^?nImi|Ydmt0(Wp!iL~ zZqP{0nbv+D#Jq7;h&sH0Y=xj&%nrV;gLO@4B&(Sq)=mqY0qx*Ale-|?Zt_25TZBXY z7u(OcR!yLLU1tF&Uf)5%AGBUqz7#5s$kl^@J0!0u-QxM&LK(Q8>en{{f74d_VzBNJMIMjgU|eydd`d z^S*;kY-1aj9^zF8aJ}5!1wcU#-p;w3gZ^73=eNMJ1ukrt9^BK+Ar1yfD>Q9dkT{t8 z*N3=wzJo3FsXte~1kv+v`CNsiyE*Ofwab_Vt8D>u`yc1<_CN9~T;jfC&w3xhZxej> ze@uPo-X$^7i(cQ~!rd)%_IiL7QenKG9o1*Au$~W*?!d@<(Dg>h-XARZHIM$C>1-}> zpM5@E`;OQPgs*tq$(>Wo?o^y@wfWB5eqX~Ee(-|VIdON-X5jAE=_E!CJm+9qZlVGe z+mt6}m3!w^$5-fu2ksLTKV8wYm*@$`{sNw$^W#I=;;~G^0+v6KIV>Ov2}wr<&S38l zNWl(#&_NU!@sD26BMNBXgB?Hs+NuS3h=M{W2Yb*z)R?)0sJVpzws_dDq^m6^bGJhK zxiCAsu)Dhu5H8tjFADTM=nA_g^94dcI|JjubBMXlGqX)LM0Rn$U_Lbn?gprJS_}C+IlbQ;=H^_CWBxu&tfME`#TXcu*}oJn3KSp z+b`LIuL>hSJnOd7dprjlsX0IemwG13$~__kJlI;nnA<|}$}jMeL?MhX@G?OZL=B)y zE1r@vd0H@ou)>4jLh7QpFa$i|3OZslur~xeo>MnaJU>~&IE2_m_o6z4fCnhpzkfi7 z4k&}dLxB0yI}xC*b0`62>@H;tj0QA_2Q;$dqD2w3z@DRrcJs0$`@=)rIo>0-Z#0A0 zOF?l=4XeXCxaz8|Te3X_FL3*|9uz}r;J{9-zy#|vo#QWT{<8y`tGg%^$59Km1Pezn zgvIBIJ#gekCG*GAFvG+m$X8@D*g7&{!@MfMLNFw_w7A2Gn8S%6IwhMz9#jN+L_I(R z!_)&We+)-zOGFoIK7zojDa!~;%fU74vy;3>&I>+%d`VAq#nezf=CcFmgT8-|K9ck< zC2L67YQ?5AL|QC7ThvE%%*9#iMS|!`>oWv|xV8JruL*>&v7EZ7W59~E#?d1^v{O1w z#L9w5x8o~5&YMCCw95h;yG9c>sQbvY$f;%HCIBlhoIHq%<3Pg0FMAw8#>`8%qb;Ya zz9n=pa_}`M-KeA(YH!08IBvPD9j3 z&-6@x)H8$=3x%_*gu1GSGfaf=O~3;#zMRT$tIENO1dj_0A45zkqo}RBMugDDxlFa=DoxrPI;4xsW~k7E@U0<) zvix#9w~#VutFna1sVp-H#Z$|OtVbWDhKP*J`}(u)0>7DD2+g#&yhBBzjI$$?uMI`X z#4O4VtkLC6tktZ@tDHg`bWF^H0v0H-IJEu?R2WH`JgZ}(w1`s#;k-&yOGwo-&Y`?7 zU%-bgolmSd#gx#=>10o!OHRk^QtdoWMeESh;LU>YG&}&cdCQ2f1JWQ>NcLRFHTzLG zjmSEsPp%xT(z;KakOu`QgkKXzf+)sgT!#gK#u8}85Fmrqpi-5pDuZ~Y2F<-plTo>( z&f1*NRprnU+rsIT(iBBH^9wdwO^f832FG6^L^v0(_HQ#jS(3Ra(NGTsKr(wl&+G^x9^iUg|a5 zvGv=YecrXOETgI^fyk+q(y6$83Ef3r?zP;j4P3;vTeHR5*4^6fnoP32U-2E^{{`Rx z7T~t!p8+=DhVX@VIDrLFfFURZfiQ=9KmY&`gO^?29asSS8vz3Bg?rEd*D8Z$IAAJO z-=M9Cyc(y|WsB);iL_(8r{%e*-CW)+zNr$L z`%OFJE!(JTkUU{S_q1j>X=2xkM%gtv+VaxUk!fHO)K&uM~ZFc#n^ zTW8<^DUtpnsd}ymde-ND_Gbhd=6@FGfj(epCg}bsrejK`Wjg2sM&fve=W6=jK5poW z24HZ&11COc?X~F7W!i?mJ$_Q+e*Wl=HtBi}=#y6Im2L@xX6cuPX|`}WDURuyw&|cd zWt(PZDfcrDp1;cIu~wYLhPNsHWa5o4 zt>)^kPTZ;X>aZ5;u_o)XHtVxS>$I+;ZBgsCcI&r>>$sNdxu$Dh2J5=U>%7+Mz2@t_ z_Uom->%SK4!6xj&HtfSjY>o}=#AfWqcI?N7?8xTo#g^>Kw(QHs?9A5eaHj0d_Uz9F z?f%df?a_V~(l+hWM(xyAZNKj9)pqUIhV9sv?V)b%*|zQ5#_inJZD6kL-S+L@2JYY% z?mSAD;WqB$M(*U6?cP@I=63GqhVH>;?y??->bCCd#_sIa?(OF8?)L8Q2Ji3|@9`$@ z@;2}DM(^}i@AYQy_IB_0hVS^6@A;b0_!nKL_+c7xY0V^g=iELq~K^(AY(1^hS5|M~C!C zm-I=e^h&q%OULv~*Yr*2^iKEmPY3l-7xhsm^-?$WQ%ChwSM^nA^;UQFSBLdjm-Shv z^;);}TgUZW*Y#cJ^0H*jMw;$ z=lG8I_>TwqkQe!pC;5^$`IATalvnwcXZe(<`J2c2oY(oC=lP!Z z`JV^+pcnd~C;Fl{`lCnsq*wZ-XZogh`lpBbsF(Vwr~0b5`m4wKtk?Rj=lZVq`mYE3 zuowHWC;PHD`?E*;v{(DJXZyBy`?rVtxR?96r~A6M`@6^cyx04^=lj0*d$t#bz!!$U zCw!F`{KF4?q&WP-XMB@K{KYQ{$Z!0~2l>Yre8s?{oQ~3+b8~TpMBUL z{^9@q#E1RZm;B^keCTKW;-~&?KmN=Y{^c)y?QeeP-+t~di0H@u>L-70zy9wRfAqKf ziRk?HzkJ^>|M;Kw(RY95AOH1d|NGZ}`~QCV_y73^2w?*M5iBThpuvOx4hk!{kfB0{ z5*a$Am~fy)i5fR@?C9|$$dDpOk}PTRB+8U3SF&vB@+HieGH24PY4aw|oH}>%?CJ9- z(4ZePUTj$OAwq%>iB62DG$Tc(LZ?!#YV|7CtXj8n?f&ZZE7-7N$6}QV5vf9=QKddj ziWcMAxMb(jt!wu#-n@GE^6l&QZ&0>NEvk)pR%7A8NfjFf?D#R{$dV^hu59@-W|(Z> zI!)a8a7Cw_nJUg~`ZVg)s#mja?fNy)&9GPabA0)r+`SW2w*bl_@bc%=umAj9 zhz0uh!&kZf`$p*eP)0KLb%dUIHX*29cL2KApGLvFGejaI`9j_q25Pt=haJ*|-+uhX zm(qV5{W8ycBYODHf(nwC;7=v~^Pq|jfmp>d{vw^z!4uW2_#==(3W*blhJ9WrejHJ|JO;~m`WtB0>NTZ4NB#GG4eq6!EpQz!TH3_+l^jXrMq7720Ue z0mLq{Yp))elaB@q0H8!64lz4TIyy*8hBOkHgRHb@}U%B8n1fv3p++2AZ-}k zlFREf?5HY*p})KntrXS%17e6cdW5R}s;pM1CBdtF|I60AWP)qDKJl zC-6TBv+Aluzqm8;0#cw6O+lEB2qKgVYOEi}{{Vv`fC!$m!vbd@s4u@DJ7f+%(N23N zsYM%ov}6rS)XuvhE&wI0Q0Vh+uO$YPrUG*QbM3F?k+)x=1*wBUh*9W6FGp854K^7C zZq2JZYnSXRx$gqd!@E+@&8$6gmjO~ds|KO!-1ABuVxeE_7xv2w#H^7z960?QmP|YS zG^htj||1)UeF6S5Uxc_zl#hK_$(Vce)Dz0A` zZf`C30$L+|Jo3rc)wr7JkRARni|@Sa;Nt!|%p}takok7A=q(UNj_Z(}Ana0D35@h; zZ^RA)3XtKGzl#TKug+}}%80M%}i=6kpZs}`!sLPM>HaDXRd=GxQsE2++x2Hc5M1;^Q zAp?0q9x22p7!+K@698r@2QU4xOO+_3-)62yB|keBzM_rg^fOC2n%0ya9R4r3{= zi(dTVRhVa@_&yETOK7DwHiezjXnw) z#>jX_Mv78%-5Dc_R!NXukU&iyv6CwC7)mw%qmNMFBB$DCD3zrtR|UZmq__jhuJ!1c zJEYs|YWcTY?GcRDye2k%QY0jWf)zq^BS9>QyBSf@A?qnacY+tmI@BF6qTJD82jP9F{Dd#n^sT>>y?LZluP;DCWeI@6Yr>J%?QxngS|UiPG>KM>s3^=;V+E>6M)Q>8N@1+9JE+wWKucE10HQ zyO%PSoCjb!wiX4z;tM{jBIvD9EFt6be34Y5NlDA8_xR|w zg6d9&Fg1t#s57|5rL73YDOt?6bCV)19%$36Ue9tim#hpTYP|!y(-F6|%~GOx|6xdn znzK9pgw!v2n7At_IMumNQwDnE`P74Q)**^DYFr&U*Fa*BAnrwFdM+HwAcAOnJ4(-f zV}_0nw38#ajHNCg8AM+K^S4e_+1TVb-tWftz~qHkf?+#imkv^R>-{m1yVzhWjxc)d zBU608!!l5D&Az_+Fcqv3zxnYulJ0|_{8sh90Y+J^?Yk%YT1XIIyyHx~MQ=v}XUtzv zxuFN0Fx}jvy^nOtaar^s2t!Kb8%l4^LX_YxzLp>*Uin`-c$W+zx2o;!nS^~NQ~7%M z!!Ibwb=j*~X63xS3n}29GuEjB%Ah1&;H;VMT$ROSwhmwhxz*V84Ps!u5m_SzFVp_{ zi9#A2jJP>1)EA`ee*|JVbv8M(!q_LT_alXORQZ@xM%?xWjI$7Rw{uQ|(3!6~5fLsR z00@{uNZ}mOK*ElsMG=OCh)21Q?wWYM=BM1kP|F1Em)g(eNVy%ijCS{$KnMAu)2JXX zl?mfki^NjCag>e<O39M<|5shah<8E;^$Xy~EjyI(-e<68Q z8cdLH9Y9)2;2=^n_0ooPPR7Ny<*^-_t!+u8sv1FuyIh{daZh7kB`buFBNp^&`7GNX zNe9jVP=-!~Tf$an@DAD+26ZjvA1rsdkF@mlX~F!A=8{$!Us+|1a`U765JIK23E`uoZi)}F8ZbbB@nG3=~y}$Seg=)djuhr4~@yi zVwjJgg!3T>^2j~Uk;~JC^u5XaOGG~u)f42wDDgSQSub+Xe1-I;pSA2aGAIIz#O*h^ zeN);}65vy1^QbpT?myqzu9rM5gc%H4F#ncV)A}&hXZ~V7-gV}moBGcGzGo~S7wkVD zG}T8v`M1CQT|~SL**6PA0(utp)872!pa1pkpZWZEMlQ7mzsdDqTZo@g_y+%JA7eB` z`T^i)px;6C5h4{JV;qt3;ok%fAY53WXb>OVTzDKDlHUO8;Ts0xAr2xKj$son;ukU^ z9EzbAwxA1UQyA)C6qaBNqMQMmVD{0WA0~twa$G3RA@{9f{RyD}cH;4MU>laUt6?@D_-Lp z%3>KZ;{{$K0!rf?o?;0G<18X!42mEnYGWSyU?%<;8FJ$qz8^AjU-NyV^r@rs>EQ-e zVm&@#4MHD2(qJ}{qWvjiXx!r+9$^OoU9@x?KMWl!e&Q~&pbA!GGj3u!dLI2@qz5u# z5L%!z8YD;hqY`!`^wHyJ)L==9$BXzMBQ7F94kJYhBQKI<9UdV~LWN0cq(?ep99kqV zwxc@&BsYd+^qu2TisKm`q)`Ik4<_Um^5aU{WF8VEFy3KPCdV%xq(Tm4L~^1%DxyW+ zV<8IRHqu@;-k?&lVpN)C`VnI@lBE~c;Z2eTPkR0%{KcdA4P;$PkOeUgB z{-$7dqH*fv3~t;vdgOBkWclSLV=kX%u4YEIWhC+?aY|xE65}@Z=4aAiI^v=%CZ<$g zCsX$2V8-S;+M`_tWm)RwWdVFlu$A!4Q$3MU@Y=1KzSZIUN?(&T}{B38bn zgL>a19;au*WLk>m68_|7f?su7B83`ccK*H~Rqp0wCTD3JrW_`t2-0OgR;W6j=l%WS zIR4~(f+LI0Vo0)QcV;G0!YBgIibPC_SM z7AQ{Yry*h~HKHh9re{p1AX36&PB!V6Vq%C674cn#VOB+&rkjk;#lw}pVH}|vP7N!UZFZ6pytG(&P1bHMV&tCZAhx0Rw`{|shTG0 zkA>XB_M`+mhyiRB#5+{$NkA*N zrbId{fxOHKJ`BOPibXnLtFx{vyV?h|{!hAQD@S-MOI)kG{=>Ge1iga9yLv>pj;l-T zt6juvyB4g$UPr%1jJke=z@F$w*lTFyt4h$T#w9FA{HtVU?3hKXbS`SBdMrs0?7@yK z$u38~PRI}pf#LB&xvFgZP%Fyz;X<%%%8D!MAVKTogO%8;K%i_HNUTKIRmv`GxSGQe z41u^79JP9@QHbon94*pXNjjvgxz>=)PA!>CZOTfkL!j&uG@iq>?AJyN)f&n<*et%P z>)8TqFV*asXzdV4tI)dsY}nSUycF%xqAl4XEy2<(-Igri2JUSntkZ66hWG*@ylmV? z%-GshL9m0(o&(OlkzH+O@zC*W2uGEUF zUBPSQPOdq*0YR8(wr-)piRywC?Io?(4=+;udc&UF{Hv?mg^J=Y}ow z9&X|aF7;M#YY?vQZmgjk#ON*r)4qfI3B@2P3fcjxY&l2KL@7J1j3VG{nAoE7K}PzLxL%R<6Bb>;gjs z%WiJ~3j@Y7t~Agq)3QVOBE|S_Y}qm{5@d)#_$}2A@k7jQ(kev1GI7efU%axz8DKHA zp20@?Jlha!*GVk z@cmjYGZ5?zBgFkyumJb)zfx>El)?B$XA#@g9UDrxBEjXpF9Zi7>8&9 z{5HZpFhaVT!xEgLU8ykyw?!cvGDe(n6#wfV*O2epu^itrF6#vybHv(0YaVBa279js zw{rQW@BZW}Edd*D7soH!CNj0oY(AK>G%Q6U3$gb4aP#JGGH09tBZM!%ECDMZwBA7* zFa!K9@^Nl!!y+?8XmI{Yax9Chz}oJ==xr|FGd_O>?y@Z}&@Kt2Ugs(%RuO=Hr0}})%Z>&;$G&^YW zLhsLn2yk1ZG&{?0N|UrEBkt2~FT|p8L?`d+9s#!Y^Y4~#J`XifS4F}?>)$Fa(ss)? zgY!bnt?vr$5=a6KUoKLcs{~6R-0sg#`!L1+vr0!N*^X_@ax5C#RU0?4VbJUn;6X{h z{wz|Ya1}rC5&RG2PIFtpEk(<<$|@k-(iYSbZ9L11T#G>n+wH%|Zc#5bV{ZjuCZ>i~ zCx^-<1!JRJj;VuQssGjGYIggf(?U^cc zUpIDVw{~wgcXzjUe>ZrCw|I{?d6&0&pEr7^w|cKPd$+fHzc+lxw|vhxeb={r-#32e zw|?(8fA_b4Z+9weLN|B=ffu-eA2@<1xPmV@gEzQ?8+bQtLMr?>g;zL8bb>ejd;%&U z!!dNYhkrPThq#E3IEk0IiJy3fBLgaYf;V)6g}*qA|3fEa!YZJ+jo&zq=eUWlLMFI) zjQ==)r$RTpf{qutkso=9zd|=mIFL8_eQ&}iAi0!J`H_FZCOA2k(|0%2IF)y~mz%gM zbVHVpIedG=m!CPBgSa=4Ih(V$ndf*X;DJ9R!9Mf@9-u;+=edWkd7Jn7dB6FI&jFs3 zLlP9ap%;1`=y{&=xt}+>cmukL_X9u7L7|gFCv-R}_(7q^fuf^1qdPjNcQ>S$cs~?6 zKU_M9qXIu9fgK<@JAA+sP(zN#!ye`Mr-wSOWA~_&c&R4=Kj3+YqeA|nqq>I=P`D_= zh~q~Ygo2LKIy2aMu1CAB+xQ&xgSEH1h-*3?fOtDRfi-NxJ0t)iBzp)PLXkT=v`f3X z8~3gsc_$=+KX7}j9|L!o!7-rE0Z1J)u#w~7M?Zjo7aYT@3tluF!#UIg#Tddd(7HPu zfdAO|t*^VphjzOkc|UOahj+U*=mI=A0V5m(JZ#W8j6fO40tFcfepG=o2z(SILZE%Y zcvyime9*$nkuK1K-k^BIPdv{Pb;XBx9P~qx8+#QLe7$#gJU|J4m^*$b{D}noF|>md zPy;(JH7LcK~cla zD8Lgfe66WeB{G@)JK6Ke8LCyvony| z<>&q0cYWvoJ{*6(huc9C#6j76IO!L<>36u^Cql3!OB298+`Ij~8=Vv+0>!nD7)S%& zKYQkHKJbS>^$I_x#{r%D0rC@i5+qa-bh?*&z4*s}yORI#8@i+$I^m;&(7QjC!@vCh zKS2BwIFMjLg9i~NRJf2~Lx&F`MwB>_V#SEwj%C!i{*j|bRCxOB+vg8Yr;aC4rc}9- zrABWSVaAj>lV(kuH*x0Fxszv4pFe>H6*`n?QKLtZCRG{}V@sz`p+<%Ja%okoSFvW* zx|M5JuV2B26+4zJ&Zbh)rd7+5YFW2$;l`Camu_9Vck$*e+ilfazkk)P>ZX@)VZ(xE3{@v@n`|Z5zMXq_@87{IwH0bp zH*e?Br&qt8eS7!s;m1$kZBwZ1;qm9!zn_2pf9=R%NkzZ`2`tdS0})J6!37yK5SHNl zi%`M|DXh>!x{AqA!wos?(8CWw3{k`pH@wiq6H!c2#T8j>(Zv^Gj8VoJX{^!48*$80 z#~pd>(Z?Tw{70A}vl@~}os3i{NurEAQlch&oPo(^$ zY0)?X9qQ4B8to}kfhH|f(@i-g?$9%r%#=w{D|(dFJN4}I)SW8r)YVsE^~}pOu{?5B zpjwT|)Kq!8HB4V|$~D$ui7i&ISrPt)G*Up-1hm#dMd`)((uBK(6y%k|{a9sWFKwCS zk^^2>cPu^nJ4@O$>z-Ry6ZmOS7`efgC zM;&virLSCcxDV!eZ_AHY-u&}{Za(+6{|&wUU#U+SX1qHFK5U*1&iY#3uUDG>qZ{VD zm5~j71vDVBs%N{bm1}daqa5b;rZ9o!Y;vU|-orrGxty`jPq`acz6f|g5solQ34|Wl zK6p6?KG1ux3m*f)7e4XbuY#s~od9L%!5lKLd?fVY4}oYS#08OvMKmIghIm9JE|G~R zN@5eC7)2?9=ZRCa{^Av}IGZYFk&9jQ;upafMlp_&jAb$-}wy)QIw+{)ygO8fKhpRl%yq9N-;J;hkeSS4*oRh zOB3U;75?!UnwY~%QwdX@5=N$<10*usxl(eZVy8u|OHX?WNL%D{rQg`5QK3o}q)v5s zbW{aF_c@15pfjpn{R&mVTGco8d$NiaT6ik=Qm*SSH+G>uy(a$F*2djeP&{#i?wX88Y|d3BBL8Oh36bT z!PU!-HdvVrY#n9sjg{5`p`>+fO-$QZI=TX@@bm*JUYlE)#5S{YM8zL43Q^q-mm<71 ztsMT{iqTBSg1F69m2ov2$Gl>+6P0!Db+rcF=)SSA4yA)BM2lVVvQWEJjbmi@nFm~s zm%SP+FIeAL3~zY$oqkAcd)fQm-ntPP=Z5vhb z*3X_IybK<$gCESsQAqfmbr7(Hn~UN7u5r5WoI@Y13*y|0IKyh(gdGYx2XdrX#ja)X zcGZ{(!=ADm6|OO9Z+zM{7ULU|J!NQreBmJ9bta_3Ali^Y4*Jqzy-8m2lG)ni9=dI? z7b*raz#L{VkD1J6HuIU$9A;SfVV`)AMVjFZW?9Uc&ULo)o$;J!J@1*%eeSa@0RA0l zK@VEc$FQ;$v7F`IZrOq_k_9Rt9cf8Vn$nfF^rcUcX-#ih)5$gQZ8;rkQH$CXr8f1c zQJrd4ubS1ZcJ-@GO$u4hn%1?pwXL6X=qV-|zS>LIul?q*wV7y?p`f)XW_{{pCp*@p zfc3LcUE*?*0~M&&G^I0*ifm6h+uN3Ow@-oXa9^6-qh z8QAp-b`AT33|YsS-~I0QC-nMGc^+fT#~{(c5uR{`FPz~Cf43ClJyrgJENK*<-uCdt zJqi_LJgIPZQo7q=xjr1o5l<@Wyq343L5|)A+hTF36N(RGr5ii0LqthF{#uitJcIPM zxb?*5IehO28KNM?$B|MDaL}Qkdq8^5(YkY)PCQO22O!WfUON;O0~VsFGFFb^2hXAs z>N=OYPh}=?2xfii6rzqOQY;MVoFo}_ue+g(@rsMeVcOD3#@tb2cfk*y@P#-0;R|o* zvP1mr3QPOFGLCh-)jNdUk%cHIT##ZYV;PEpQvIApj$A1357M zmZ8h)PlWDort}2_qr?_Sfv5`N7@FZ4tReoc0TaSu25GPcZO{hE0S|Q056Iy>xWNv@ zDGsnn9B!}(jj#yCp$^7r37Ies{NMpaLwQ7%iXzGT;PM z00cas7oG7Lp)nd)F$QM92(C~7KTs2uh!eZa(`9 z24dg^5HTL*5fNj62auqe_~Dvr5eZ~q9{o`sUtk8>u^o3{2Vx)tD1ZY{U?EZ91d0&^ zm=OmCk{x50w~}naWW@$QUmf~4t`0Q_5lfIKqrZ^DE>1*1txMG3sMC#-~-e#2A=W+GJpe4 zpaUdwB4NM-igGKtvMar^0zg0pl9C7b0SlKwB;joYe+LW)r%F%|6%>ykmSGgj@de$l zA9g@2Co%(?5+eQbFa7cae1IR|;3t7HD0=`f6%#P|G6j;d1`09-k`V`HpaU+zF;!p# zI^YC2AP16C1vtP07IQR7vouNb12~`pULYxHzzANl{hZGZy zHwBbRkl_?eVdey)73i`YuK^m`&ktmBAYZ@(D!>Df@-f-b2=dV|m#LP3Ngs>=Lno3c zF#rWP(mRv!1wxYoHb5EC(*!o)D%&wLEx<8F^hR-1L}TCt>N5pqvIb;;2l`<@*Do9W z^FNUB1Tx?PJm3UO6iy3LPEDX9L4X5%^aVDcMKeG~RRB!q6a;?sO%)YK zV*ms$-~y-;1uirPj35J};ZiaFHB&Ja`d zpaxWt8c*N?1XV^i-~uo}R~NNg-*iVgAOn=u1J-c{dO#8mFkRIZ0bf!XO7&A!N9riW z3$=tmO(E9`f)y6D9JApP{PkY}7GUonMBR}>K|mQ3^)Yjx5`}f8R!R?%pj#`_1Ui5M zm=#4WfCK*W190_V2X!5B-~y7-N4@nORUidWzywaf1W+IabTMIb^aW4=1aNgVV?YM_ zK@u!sXL+_~clHt*^!omm!BgRNgiMZh=oL$-WEDtZ6)Mab_VxQJVQQ(iYN^%|{J|h= zATvK!QOWYZ9Cn}JKqyPr1S;TS4H5-H6IXFnWKZA)r1C0ppec9tO=b23P;*X;kr*vN zWH-PAKp;F}_C`P80#1M(kw6lxc5$aR8%i+?i}r_(c3$^KNv7l&PC+FLA{qA8055@Y z8J9ZOQ7X4{QFnkJz&32(2@VKWVtI5X3sM!o)C6dkb|n>VGvGdR;BQypTMM-VFn|Ik zV0b5hD2bN>ETAdFb5W&|T-UJ&?%)wXw`woJF0D@iBNtS8t$Wx~TBbx6NC985L|>=W z04-s9srC^h;r<8M(E~8RMiuo3`XO~ymvz_kSRM5S;8X?h_5=L)e*yRdI-n^*7CkRi zL}%ayV3YzRKmy2fTLJe3Bp3wp_5vz^cq@P^RbX%jGBYtiEN9>Z;y`iHw-SKXL9LJY zvR8YT%uLFM(P~5$M4{crv3&crgr^n~ z_eNiUV>c27E?@#0xQeZ~fvGqGRI>(fpacB$Mk`V)BR~Qu00YdkZt+uQ!&G=9AR$oz zh}*XUF7ySE;0|!O5*+vXAa{jX7{iK7tYCOXV5$EMf^%*7b8#3F^nnM~@jE?0MsqX< zgqT=Y{#PGdFAhXv4&tC6?$-gB7)MoLfTMT=C?En%`IJ%Flp){&?30T>U<2+HLs0+& zOnCxIG-XpjIb}cw-ctn{@&vR}0w90_k`qU(wRCIX2kaP!s~7u*mXG^bzbxW-0$E0E z!RxH+7+9fv&G(Ka!3c^oOie&yjldtmNgwus@BCpF&$$xeU?1w%0si5WMHDnQU?#;l zmGya*4;TbsSwvC50>+q_VE_jzz=7-Y1elQn(31l`AUhdY0xqB;IbZ_DI0NAKF(oqt z*zpABfP~T460iYk+CU!+iQo%mLD;a*1RS~&_BcTq8^9J}5jd7HYUC$r> z^no9ObszGfr++$}@*$oDf}S5UGu;zT^I4x!83G{SfTePrAM*t=pn)sk1k~{c4%(Ej zIIK^Z0+130c(j2_R7A7#Y}Zi*_FCGww4r(e?%5rmb$Bu0fRmC(4;M&_zz*&hXq}m8>-vCr3srU|H(`Y9LO~T? zx*51R8_svI!FhDGQ?VHv7s)9OkYFENuMf$|AJ}(^2|}qm7z02+Al;DzN}2w(RT%+oI9CwR6`Uc{BoCn}cVW1K6<#@SQ7f-K4So};W#N@Kp%qI2#k0Ksv8OPVGiDTA~E~BHG3UXzyhqg zhv{3!)tenv-~y1DJxduZuQCQ0xW_>o1MaiM^?L&_bVtd$t&Kppnf_S~Qy9UYywYQW z`9i^IH&?mK_Y#`Bgzo?eqL|OURa*A}vdvtacz~S7cFq0as0Z@R2f`Ir90e)>egXB) zSzD@+`31!LF;C#M5fv(50KN$ws^u64RB;2en!mx4ynP@Jq_>0#oTRBQ6D>X4W1>Le z&LCQ0Rrz(M(H9czfJ@ggOk;UbUm%^~KoI4?A5c*U`N0tj(+JR5)&~LH{jooJpwF1f+aWuESdszTF*BC1EM&|cl&XTRNGreug|9# zE)NyL>&ll~+^1I(;9w>Zo3<4d2MDttkRZhS5Y5}r2=<{L{y;QQ@qHjRL8)zhSDBLG z_Zb3o+>9R+1~5PZwDdy1SlX{Yaj*cc(*0Jq$%Cw%O+`m z2pmy$Ae4a|JN?tq_YU;oK3@PV!#pW-fR=(13H%`_%eh!_z75UYyAdK29N@?+fa|wb z;(L6RIUvxHJ}GfP>-C!hHn<{ZHh6_M>QmW)i+4)^0o~`vhX@DOD zoS9$pEMFh?pDc*DE*uSF0hinDQyvZgB1esZ88)aGh%lkTg*^E2(`O`KzkLxMR@{eV zVGRfW{+)7A=bwWN3S*e4AaGy`3=<-_d$49t;%!wZcnF-V9o!9xrc93)Wa zaKgj~3J_vKpa4SS2?{PNq~M7Vh6f7=YoI`Kr;^yQWXD>%2JM+zwak)rdxo zg7oRsr(39yfp(1?8+2lwfg(do2^UPL@Ic|`OQR9ePnaNqWWpHeT|%8vD0tg;>ut9njv?qa46IcMlKrb{m zo@~U`1_K2jyw*^SHb!%xTFbPEV*aC!J_>21l1@r#rHz@0X{MTP%4w&aS_)S&tBj(G zT*ovMql~E;D%K-#>@cMV81(0(MnLi-B$4{$Fp)mL^^?yJ8ad#A2LmDZ&p68=^a2PN zq;wt%D4alTwboJ#!UlKJh64v<=BL658Kjpcb|07kLIo1cNkK~$P$1h08(08edl{VY zXKgWLkU>{4AR!5>Vx5KPfzG%EYQhRH%y7dFKkTrl5>HHV#TIv{Ffyo+B8yzFSd;3E ztFkJ|J_yBNfd~24iqMd_0=Z8EjIPqA-vOMbsg%SeF#M zmNUyhjCbC8@6C7Le%Ezz;DQfMIEV?`LJBI#$h8cNF=jKep_bTzgmy1bU;^4Kw@joF zMofgn3^eD8>vOf8K&-I{snEd&Hq{`*4n?17K|x6;ZNUa6?3vv_IAqH<%QA%f!3Gz+ z`(B;uDgPe47;JFD*_*FU!3ng*;6p!gS7M1Z2Cl`-F@KLwe);B~kG^Atug`w_6oXof zDx$0+S2Co99J!%z2wQ_m6_7bSgdm|eo>NlJ;J^>hbuNGkAqodxcblT6YXc}yfdwtN zm42B31F!oX^cXn)lxcl{0~^SImMmZa3}B!j9AHxlRyMs0e5(dD0ND1lK{vG|D1AKa zp$~ru#J;(2h(s)+;Vh*wQ*paLnPx#^`~ z10Hx?3}lys4_x3NFL=RFV&DP}U_b>Me2%z!#K8Hyz=A30V+t(j#yo1o0~ZLM+rkpJ zxh2YOL2RTW9|_4wHpqyStfUb^1q)K7f>Cg3hT}{Fx$WJda&4=C^#oai7hn-0M%bbj z_u+#9E(w(>D4Q8&M>UeL4g@NIo(qCTM=K%31ouLvF&Vg{6X@v&{@C7GzNbU+NphOh ztfn=uIT!v*YIB=}vs5usK?+#Z?|z`nhBM=kqyC|Q0##($3wRT(R_bF1pW6oyu!w{m zNKKF^z<@HRBs&>+AUh|Rzy?xKfkFl?1tt*ey;W>m;n%Mjau^%NrfsO9rfHIfnVFdy zhK8A$+%PjUI?Rc~%*+lmHVp3M|9v{6Gov{p&BaJ_G1en%?{~}ImVIIAdDr@_1HoS0 zBxC6Y_3x$rNH?nR6ja+dYh&V=IE9qIfzE|gOvjHc5wnTXP$iqIBXM209^|VLJs##rpfTH9Kt9A(|7&wFL-|3IgRRhi(@`X~!52g2k3E$W${Q-%+TP33Dl(Hs0nk>sj|g%85wP|^(;E$yR%2TDB^ z01C+~9@L1(6IDvbbIcIYK_3C;&;F?X2e97kg#L5ZlQ||xJow(Z@VXbQ$`%Q5*g1@} zRLu32f@|_oj*=!cwRt#O>y_0nHM&{gP^m`AT_JkO`s4yf)@=^kOeW$Xux ze_a~El5YsMnIntDL>L3D?E+xSvXZdP4Gig>+$*@>m4*MuxFZYAB%EEY3eSbgMV+7j zgwmpaOD>Y+vuhb+SdT0}FE&oznRxSA8^XUVwNW(v7Gpcp8*^0#g){hVeU2RDnn`+Q&a;CxM8sd6|ixRFE#@6PWkF`zB5ftHsKIu*R^3Hgj_#>%aU1MI&BG_^7ACv~K1dLZu zf46Lxr7+a9jk90-js-zs0j)r@z#m`C@V#&WySZq-SHa!$Jy?EHQtUd_&0=Z`E3fqD zs$bo;YzSIiYog7{cE16S$Sc@59U2{Hz-fTQ~GZ2o{XOM?At1-WMmf&@b6do-hj>7x`xn z?=y)fW$>IL2!W-2AA486_*^*%cBi}OAzws?bblP~?5yd2yWE->732|-I&A#s@@Cz% z=qGOe`~d@Go>;NhI;@f8M6-|gMY z!RpiUHTH6oLVR{`e2WR$yHt1+Q+$V1ssB+aIw^Xw2crDKcYinx5}*fw;%~FoA^5YmYOl+3j6!()*nvo#ouZ(&pELXC-Fcbj2ljo?1w0p9~S z2?RZ81(Avn)xRZMRhLGsg{#*dMP>Jfm1CneL5xQs=(xxZl^;-CugLoGl(qrzD@c)l zhh^3t6iKh5r+YpOKe~4Qz`lWl!@iC5m{(kKfqU@>HqnM535Ts8#bQY*Z_&l2!$H3) zN}&2bV#yVH2bYq>Ph+9Xz_MwWnbn#8Juil6#7UP3r{TA=?q(; zg5U~~y(tm7MT!$ZNsMQU6FF9oRz`2a)1v3c>E2EVCB@*>lyvNqCwR-$A}F3}Bu z-0$jx8_M+EVzJ^~jo_u?qdWrncNk3q;pteu(FNx`mE|n`g8gdft5O&^L##`o2>TMu z=a{Q+i{cJ*n1ykY8;g^Tqn$$1_nmkab%gLsqm+uPp^c{_>_@{GDuNi=uSx8R)F#5z z%?0@kZTZBCJT)rfV=>Byoj#xzf*x~4qzhvLI7=YL|0r!n zOZ4MmkWd`la*2Rl%cf~63|hiu{i=bR%EGTZMX$5uoAI!gMn&w3!q6Y1f?w@n$#cnR zU_YiQa&FwFTId+~xNuk9+=C3Aj*QvJfIPuh@WpBoCxPoM?V62lyeI7#P zGOMa3U{&<&?+iS`0f2W6gF z6|tSgLCV5v!PM5liZNw1V{KgY_IC722+ovMOCYBrAm}?o{SP!ECP0Aw_l(*TS%0%o zqFC0{wlo&(z$mRswC{MMcf4fns*;NWDFl*k4-$_+Y^z{AdQjPnC5@UetR4D@? z^4Y@ITx`{l253W?YJ4>@4|abrERzWw-4$)%8aXF4*JN{tsUxxFLAd(!kjXr&Vvb8i zCU+|nTcB(poLFpZNIVzDtwpWJ@~(S2qSxlD|<-M;SYP-H|um6reS9#?dtw|BKF`;>?rJ&ha=a* zy~e|lG4}nC?IT^6ftKPNiJ_pJ82w3%LPGBsLQi}**bbkMW$U3x0oPz^%8RH=zrx|t%eCxpb+#s_WA236K!UD4>o zuR22WozBI;!j(f_>%g=^RHD|xqqOWNK|8l0>afV+h|1x`Yl^6KUTYIlQCzKt1X%De zY*;(bUOJpl5zc1ga1Q!to-Da=06zT{ywfz;xHf2cc&O@Z)TPwENzA#(OQVdJ#Sx~5 zw3`C zX3@MV#QYrAxEE_RvlY#Qk`H6vNp8uU8ZFh{tMNj!AVg-ReoaBN*YFS^2AEk`AQDPYQP(;BxJGT6PQnPsjO<1 znp!{-nQ#bP;v=W+q=>)@73|S2hHcGSlYN7w6@(XuEcY<218uOxDdBgD*G8(=$0pWC zHfE$pX?qY}%NKJ}i)V4|@*+a0-q7e9C zwyw|N|DMAOC?aGRU^!eN?Sr@8&bR(yK>kf2ISJy3Qz8gJ;IFG7Xp<0(F32s$_FWVN zhkpyxcAIb#B1j1!o!lnB*#5|mK*YRrrHAka-h^T!xfzZ*24NH(C+uPDe%};n!bUBN zwHg911u|A8!Jx!JcH{l$1u6GLm=S~p5hQ%~Bw>)e6*1ExdkUEQO3eET7a032NRo;O z63hr9llyuX`%r_6eId#NVU`2oSOh7>JyYfb!^s1i&3z+&fQll(LH^Li_RxXvFvAE z9PcY4z4#xCg7$rEPedpXEcp+Fz*ujJ2<6!aCCsP76bQ8SZPr`kcM2YeWIyAC97xq71>B4fE-pjr?m%qU9hMc5U zpDfy597bOpXI}`$o-ErUCFt*&OuiS+U*1(;H0c9cs!v_z0Z#mYPTR{nOawckb7KdT z6$o_4qw)-G>Pj31Ef4Ho)0w!2c6_RNq8EL>qK_a2#`2gx-{Rlj#zfNeJr=z>t@Ax! zzQ8JS-7l0s=<>bs(LZhOx_r&P{D=9McFGMOuN`yA?sxdik6>GdY3yqf^-K!EK7z4I@J|r5>+kr&1VY_u`f`1|h`m@kJ16FBU}^YkxNuiPQKo z=-X3*!c%Y#l?x?GoH0tgArcgXWv>5}XYk}+eQDKw`j7cJp&Ki;eV5lt+i0NG9s2F1 z-j67sRp8+1h*yCi-X5v*@}>J5O3$~~9tFxZ!`HPdlyXC~v8mVcE0m!aloV`~-hVF( zc5h2DZ;Rhv9X=kPA58lYbT(bPJNXIc%G3)QA;i|0Bm4r8MqLQcw9)S*kD^No!2 zc5Sa0p#X1&$r^1uc^ja~_qP2)6}QnNKX!i3Ks`SpffeJFFMlYpzX>9>D^ z-JyQEa$1I3X(X+|W`{ey1(l1r0GqZ|!8qANvBB26czwFNJlxv)=eH&M36M5)%ZizC zw%Ruye}Ji7*=C{r)vT?fx^`OltWL>Z1XF>#4;5Pk&p{NY2b3Rq$IUnqpAV5i6(llF zbPy-8ZnzdFb4n~H(iE54FEdL2KHbu-N*2j=$lOs(T$D*~Moj%3Dmp1i-|Qv&0k`Kd zu>V`zJZt~AI@#LMF`{6^F7xCxMG1~I=f85CyNOQn|6-*=@N}}3Y0}|w3M0pJlbx8q zQ)i^rlo`uMu=WYSy3E&IR>Jg&Vz;BK$S{3N!t0-*;Je`?#42)9RhD7DQ&m-QQ6W%K z{4`zG@hm&7l0Zat+iQ_eDlbW0f8D609aPnbjoaQJJjWE($u`cUgr`nNFeN*YW~$nF zul>7z!Bxk$hu#cdS=Hl&$UJnfV%R)P`bOB?*+x3m#69U|$@#QOG@dm&i%k~02N^z! zwfFI6nW=}apVmvJ7h!%rHLhAG{7bUC5s(29n^;vgPE5fXYO*l7Dbo~Twr7#5oAC8l zqNSM$(7Wj8uU|}`k_~;vI7Q+msp1M_{gIk>%u=Q5`bjL~q(j;NB&d5@7H4EXekzT- z7$s0v<~sOPY{X};?nO-1x~H+FHb|VTcYn8=%NKU>oMZHOhP&*U(mqH$zF|{Nq@MnD>8yT^!1eIQ zX0a%Xv~#k_hnWqLxwM2vVKVzZ>*hDtb*C75heXRQ)HHPzZ(U-i7vc6BnYz{v;)%2! zZ_{ZJyDarc(em-<#W3GasqUf!J-4eqLcdqrZbGzVs6DN>?r_!D8aGM<8TI1JqUc`( zY3LKKexA^m8d1_t;v#J$53(n8cR>Ui7KsBineU1LC4PAJrUrjP*~^W|(ajWtNcVDG z7)_#F$uT5rVl@)E-G%Xr6+;+Iexd0-_Tn*H68|XNhUrrl1RN-a@$BW<61k^zcQRPj zQHE4pcF~&RlEj~Dr+8X$YQtL=LS>-F`Wn|f?5n_B&r^x|T+{l>11#?bntE9&;(sI{) zRZuOCPd^x^ue_QX|?$yy=n%@TORNBlTvJ`vpt0# z6=w<@nGRk-?xAQ2Hy^R$GpuTU4Vi_AmgQ_vuXA2bW+}@Tpp+aUYi_~Wz6^%8DxYU! zek(fdPqAm!Qt<^Ns!mIe+E8?5^zb4q=ma&}mo}BOP^VIKfH`(6iRvG-x{_SRGOe`H zh1%zY08X(J#w6{@)IqfZ<$Y^|xAgMJx`b@ipb%+|^r@)Xq!Ou(@-O%dioUkaIi}|) z+7YAj<;aPp!UGjbbJ}X{w(RLn3l$>&JhkVG2_ym*N~F}{WsI!DQmyGl(`lChyd#}Xz}MjN#k-6Qc7@l)M1+mThxnWp>F0)VX@l9l|1 zXHxlmRjJdD)pZGZ*F2ex3VA)C(gSmIC%CBG`J9Jff(@6a>)JHIKadnuN?62)&%@O(U-~CcvO$6$i zEbl@bljdgk-n6yGxC$L-3p-3%b_vJcFr3nQ9!w)#md33K{-&0dbZb9a~vDgdTDFrD)-d@MvOOs6BvoD0a`EQEH6XA_E@i(oq~WX!eaGD2C4 zx!x=l!?+u>30%tLe5~B!Nfs*1Tq^KOtu%(T7wehHDlL7ibq`65nigGZeBP|NuSu5s zXk2SeseT*N>a2_%U_k5A-hNw1@Gws%yEaz&*nkvC*VYbPn{)DQ?80=`XAvJ-CVXs- zQb;#W&3;xbY}mRFt<7I1yLF!1SbA>hY`us(cRjt?`S-1C!P2_-RPove(dush6MO6> zhuY6`knP}>v`A9xJ4BkVClfBY5Ak)0MEj9}#mQxI;(qVFDX#392>r0r%GP_WeBJx} zpfGHykAL+6`(T!hS|=w|GbiS?iH9(MY>Vn|#?sJ%)JNMX8F^<)Zt{KXpthNeuBl3I z4tYs)&wA<&mnQ7y^3^@YKM`cVDBaZCjTWdK$4-`gXXmTT6ugu^jo+jGyz4Wzt61Bz zeQ00X=&|c&F@bVby0zgp>>K_Zny!g!DFj!LYg5AkMH=1^#zW#aA zxAXllt@}AA&RdGj>N)m|r!@7HJEIuyDVK5$)i(YMBR`+B3aEQ6C&eRnRn=mTm-lY> za%+=-x97gq#O6YFdqqmySz5RE#*daqfu_PKC;i)1T#qh~b^etpsh5^~{nv_bZ&zEN zy&rbfgPcAn+b+5pe=O*`-_m&~a8?+*Ds`4(u2n?d%L<_)9N{+fY3rEqL{X zZ{N7eFCEH{($Ph(%X6B+L(s>FZi0q^(1rKfGfm%h<5f!54mvB~tJJ9+yXoF%V5WQT z3A+Vv?eK#$_$bD@C~m7gw1r9cgjPi8sC2)dy>xzh1%CGCr9bO9&@N^*>HX^@%r-BC zlgN84Bh*IP&MnG!>eVHbSr z07gj*D#;*ONjXu;kl&II#*$%?lAs{Th>oa(m-DIMY~7Tkl1S!~%F+7N(L5FDO5M7OGikvc>C0Xz5U=7zsE>{qq(|LE$buom1D>0qhAVSI!s#H&&G&mMk#h>z`sYk8D)v$WO_tpp)Z~? zgL|m)2>r2V{X>b&LzUx^^U_WIvPW&QlX%<{k>gWm<9%ckiD(leY!in!vI`wj^Xs*X zuMCTka)e%TxrK72>2mA3GHc$08)siOUMITh<+c+iW*H|pk0w$?<#kcz4^$_kOysp4 zClAl$N+TyXh0(SWcI^@e6fY*h!H|sHfU*$V*QR_)M z`YWmhZpGLW#m>DH?0Dt>-R22>kb5@i7xV^8k}&`}B~hG3&(Iac;1!VvqMjJ10TpHN z1MR|@{R*;u_34Vs;R;W$QXoSxV1J4b9sJxc9Q>IN`)$hEduC5}yxI`TNR2LX0PG~- zogq-;C9cB2VVaz7m=?bkLq-#ktOVccOnwxTV6;|-C7&f>S5!Ld5o?$glT}`Lp{F~S zM>$t+l~fp>A8un66)S2Ss6A~fwlK7cY`{UAT^D=7bvQ^`9`}6XR zsR};hil(zlNvbG!2s)s2|c zjrr6~SX z4^WnbNyFYp#dQF+Q+LU2Aj`={rj<;-H*;yNOrxPg!&^?yXG6mcV|jIasRvIwke$qT zK-%MNNl#WYgj_Rxf(MR#EIo6%M{BvHa(er-QX~dhl&Q2I-~6Aq1?Lt-^(@?$IYC~04BgRlu z)pB!HV>Q!MtLaJ`V5NP3vZH9NbAK(rbFSNE4eXK*Z8;z86}mc6hV9;H$*kt0`-hPnnL8zr;g58g&I~J zGa#tN5Zl~&_(e#IE?V_pL(zF{do;-18x%nfCJYr3Ne(9Dg~?EkZ=1P6p|@|Udv1zo zZ|Vqb^g=~wYE1zdI;+yMWv7X10tqJN4^e>u$V~@pq!~K&7*Z6AP6+oR6jkA#i|RY~ zGp+Z0uNG7ChJ0LTLSg<@;l4}GuMcPtV4YVMprZY<&x6Ip!p5$`cA>b(BySZ2fbBvh z>Dqc=U%(w)XOmwtxdEe}E3sjGrdAV^-rE%-khgk4cwN{ohakMZA#iXZQ5go;7>2Pv zmI^JyS@(o|R}8KhhHzDil93C{iO?G;!iyOy?bQ!_?FVVlcgu*+3r@qCT>Kqp(E+vB0LrfIQZm|rNWS~W1BI7Sc*7v^eVavu}w zYhhIyr|WL#2;6SAK0O~f(I)BrdG?fJRQ3*ygPc;7GYojtEAbEwwZyx z5K&)ue}HMY86vMfFnn<@g(3*(3yg5JFw;jhw1BsSh6zHBiim=Xd{J#eqx1$XNCWmu ztF2<8VTJrwEp#VYlX3acM`Z_5dafsRlfc606P}9ol33$P|I=Cvb5TQ}8|6_kXPB@i zupNq|Ka8Ym7UpsQfc}gMIBf)?{x+G>jOiWkAi%G!NtIl;+V$JxMF=& ztK_&p)fR@!*7eIr-+d;g2|{NGEaSz=62vX(i#q5V1vV{@;`&(Ze zdSM#JADj&h%aJ!q@C6Dz9_t#OPMBGhK#@wLfep48YfWY(#0YHm`$a9b?3}^XzEOvZ zag)gxk<}OL$ib}P#}^0t)kK#~{2OJ+hb@B!bwuWt5Oa6=uu)f_v*zIl)Y?ZM=;VLs zBpGBPe%7IX21r44^fk8}KJ#O-=$;I7hlaV8TC2$)ek!#tO+E9cIUJBj^6av|RzJDt zIPqIH6$V4$ejo=kS->+2ni2my7Fs@8q`OQG-6s$Tj*_=MO1=O=!(s*le`8y{k4JAA z22h)$W(@}BEuM&gZK`eIwKgr-r{UzVt%%)@zI?PIvXh$Mge(1OwbOM?Y##dgXHc>$ zaw$P5t(zq)wXs+)$yzk!IsszUg=zZc5p&9ZeMK#ucji(tO%;t3=SO%c7|UNQPF0f@ zVW~%cSBMp6Ko;5)rfDk^=F5`ggPXx~z2xBUl1@u4e~Zl=2)muX8XPEobfM0%_yM|E zvanUz?!2PxCfaxojDa>;ddip zrJSivo((fwblBRX6CU}NTy=L{q32v-{zFosupgv3a?^xUy|5mLJ``lR)|0rN;5gPh zxPZu8F)9WZP9DklVUX9jXdWCzZJt%%1K3J$A~9`>ixK~H-?wm{7K0Ds%n+>yLqv0L zuDY&)^0ss09%|!j+Xph#Wcr4hKQ6BR(M0V^afV)DYgp#?o&7S!)^DKyIr71 zlaEwG_l*h&>Qj!H|7<@a-B-C`(EPl-NBpb%@!3kkp~v7liSWg62u?x5tg72lWAGr} zEou_;(v-k!nDFg;u4@a}Moq%|7}IB9>rA2hx%Cj$>ILy-5GZP9P$!5a4zVjqeZ;Cc z^clMHQg}oevi3|l^gB3oVZL(Tbo+2=?o@3bxMbhK4-GlA4$wagEQy+se@S$+D_(TJ zHF$M5zc1nNq6&MREOwN6aA}J6ySK%NwsXC(Jsxs%yua}9XZd^ivHJTw6yw9=w^%`4 zlT?gII)_{kkl>rqcUdEIv3O`Nsxt0@jGXkZAWRotHU}EHbONaLj&6kM z;bh$BPp+yB$~eLy-yJ@8M&Qx*r3oN$n}ptM&6MB-A$D@zS&`DFaw%VES(VZEi-bto3<0GfO z(Y;^Cjncn|YM%DnJM+ns9M{t)N31X7pN5lUTXRDOJ_VQ(!jw>O3-%Bi&9d5dVt$S4HeoU~s%NquT9k zET(kwj78-3AbOAmA025Fa+>L~SXBT)oDKZKz;+VT~q7q#dAr4vs(Ys?>Bhv+9oLICncM_S*ud4a^G$f;m!UA~* z8owJN%#4fJ0QcJ&jI}h!f6T@2Gs?aD2c#UgC~*l{dvS(FA`V#`w*qv#aP&oAW+bw! z|GmbA>=$=4@CDyLSl8hKhz#m2@miqgC826XUsF7#k@H)% zqInXc$WZa{WFiAC5X*z;J{Y$=t~A!;2i_USB4Vt>#~l(h z)ri`QH|`rKlbC`AsCucDtgt*rmLw$rYQ(>jt4J3$tvXZOls}9GA3AaEHi#IyflB+T^04s98X2V zINZFWKM9Ah+ahqg* zdT<-UOPSZfHf@`jC7`d)5@a@&1**RfG1~YHhbC_lXft|^`|YZh>2y4pTqHGrllkX> zaZwOZse* zGej4Q`|a_*%LDRvJnFcZ^&5m@D9FD`JwQzx;-gOsr*NrdblPwdr9-n>vcAe$1MBrx z*-BoXW91!s=dw|&^1yL_XhNM(HBItK(^DhX!|PRZ;gakWv*;E1#1>{W7igF_=r!Jx zEYm#MD~+PYDaY9t>p$wF_kQyFe-N*ktg$T(d; zyys|>_GEUcu--TY5;PSA46M<>S)m;m{p{EQR5_37#9inA>^#x?cG)nt`Ourvc)(=m zeQLe+vg1a1G4ajs#cSb7dZ`8FrS_fV3A?|!7!4;sKXq>pTm~Oa3fW$m5ycUITeH#S!{-`QGU+t_?(b$#P~@%_@$ z^3wYJ;O<=8#Uki>sq%KK{%)`J=4j;nY~|qmVC(W`>+*K{=Kk>R>HPlr?(zBQ<>mS1 z<^B2L>FM$5>HYY>dHWxa_tTe`SJnwuWGKu(6f#NBdlo%1F1_wBD6cOJ`-^m@WNrSy zAEF;t>%+ALLot*hK(bbxO>WyI<#OGTx}vcZ7UR)OsX6MAP@4AIm67_AsT{!|RC4LW z3As=jrA6J*hO&tQ#RBOp>BjPf3e9?}jZyr)HtSR9_Fsvn%9VPHxpKX+rmD4O^BPmV zAv1Db&&QjSjj`sMt!{sKBo5$zHm~h*8T84TD|UxszsO|Ew$|;Bktqy*`J+|1UUAws zP+(rua6DJ4QlUT5)@bb&TxE5zo(4HwZE-r?oM>;p-1@k!_XkOL*QWeRc))9-qt)rK zHV*5-r=#WWbh+MoYjR_!4YctEs-E)v*824Ks6gQj>gE0TaJ4&@qwtab=gu8&TiCM= z*YiJ^y5elrhsKnx0A!j{NZ_^JE>;lyxf>qodo!K5KT$e|p(-W`@^%cnk1L0rLN^gC?!n-TIU2r96hq*ewK|SWtkn6 z9cFtg2OqllDfbuq8@Trj5d5=O(oWW~-yR?gvJwId!05V9;#puz(Mkop4_eO24W%zn z%kk7)$SoHOqyAas5JW8mRWr(iB|43D%Htw7IoT=xlBB&;3Pxn81a;l3SBjLD6PRRH zn2!9mR9Cf6=Ym(eiZ1+Tf*s0tj0X;ZUF;gHFb9+KoV6YmuVuBi)wV0vjrO*#)|HvU zf(b8D)uR!OK*m#qW~I32JR9ov;!eAvdlzvh9A{U3ADMMC`jLA`FYAkDDoD5x*3n}4 zT28FA2?(%t!6C?ATFm{7&1a78cvJ341LdY^+WFO%pTJeE$>Ny!D6cAx&2&FfSR}tE zyxh#n{>;*uO%}qa@Zcy2t+&=pBx4<3?GmXQ5&Kh7S06=rAf<ph{F81v>3FzkxA~)U*Jf!* zrA@v5FF4A}TEegYS$ygr^V_YA_+{$pfl_w*{aE{A$1#J-H|V3h&KCatMoA9+_F()a z^aVS^k9PNDspiv5#|YLx=&SW1XL!lW;Lw>y_od68U&v_=9ARmR3kcp{V|y?eIh7yY z>>vQeBp31LFqJEa-Y6Fb1Hj;pkG1+Gh)gyQRm}MVcIdRgm&63Xk09{IkzEMqc;0Vt zZ|>pD05kM1Jg@kmSA_KBDqON~$}Mpxk%s#^LVjjcs!J)6Ui%uTmbfF8J3PR=$r53R zUO+S?ImnuR9cdw3@Sj)`j<)M4`^W;aL&+iT)$3^Y@dAp6;UWI#>lpv{l5`}gVPTS+ z*l^jx&x9kx;@mfJv5|#Tv{EC|+Bfm3^}(mv9bAO**Fk%{-DV;a(b|TPc%^~{&RFD z+Vi%65~EB~LV7kn{kD)!u1s2EbT+x|wumLFOx9d_E`9a3m~)~`-feU)`}wv+5Tjf% zOnN?_-nz6AEVL`NoKj1>) zv$nc=-_$ZuW$QM!w)x!D1njJ|50hEnL2ZT~maBG58CyT#erTPJs&+1s**MXDXj`7B zc5NQpxbS>vhhWsW56Nuaq(5{V$<=r+jcwk?|E#`>s_{OQ*?L}m=z5%}@qHNEf<8ZV z!(f8^kz^ror0-kP@}NM%aR?&MV-I#TD414u8&&7AmuM0c`g436)9bO160C>>Jd|iCX z_#O?<(}-epU1EvsKE2Mny~seEI(#>9zw`||=Qy0Ow+?o@m2d9h`(vD$6o zRR86935?kU3X?lCB7IpNmT#&{nK(1!d0Cl`ZfYozJGat#SzVrNYHFT1xAl5igJ3?0 z_r^y#X1uH)$v3wxOX#GyZ`b)6&23A?7z*X5?vV%iz!GWPbzRh==W#Vd&4UtC*ngq=1jDqd3)A ze=^7-Rt(3vd$9|}LtrTwKDLe-RQIKJ51?_zYzTG5Iz?Y*9&+Bc>VWnD0pVYEq5eah z!K%(tAIXpDRXuemhXAD+01BJLHq!cXHKl z6w$cjk=zC{-^eWuyk@tJ=oll&nN*Pav?N^-U6%RKJ^vx?5&wMvqw$~OV#yE^7`Wy? z^-;L~8zS5kVhT|8*VZcc7ieNBV{YmDc=V`y^XlNrq-%R)tMsx@xO^WZ6a6=8s24CP z_<|Y>OODv0$o1Ie){r8i16Lu)m%ktFL`Q)(@`oSd1nqvoyVqyZg9LVoOtrc`_Wd33 z8`}KT?KbeTi(3AGG%}0L^f#gC#=z^(Mf+6uWeK&UPYRKN3MLNN7x0Ve6v->z%@-hu zO&x@Z1m>y<^CK4YULW!!+4Hkd;@!z5Fz~1DHYa#WVZZSAHI4H}Il@wh_?icJmrDc? zEEBwh1zZ*cnBx%W+{Z>d_WyPa z!+cEZhhOT)HY~{DPGA5-3oA*$Lx;_$=|&=mcyA9Vmhh%)@y|&Cz+GYC+6SvsVymzO zSfl_n+yk_h1#})=*xiB+>6}Avu+#|O2gHX#cjm%$iMSf)0t_|6k=??>Ir)I@1XnPK zv2%b1xCmU@h=fO;q~VB=qllbko;*6Bm;|t>l>1W{u-bxvx*2%+H>8FR=t%qLvJc}{ z@lUlR7JKlY-8o=~W_T@fB)bLS6GtRj7#2+)aB!Jt9mZ~!GiqKl>g4{Xa#i;GMI^9y#;OD>jb8P+2t@gD@RlZ*9om6!*IWr%`xIgM$uf_X6= zBK#Tg-2RXL2<_!n^2Ic!IZKMUA)-ErQI8VQ9D-?coNV`md0~%e!IEm8izoy~t0#!) zwUXjJi)(b1Y7~oT|AFflC88BL)k_(W{=}8B;v82G$cRYG=SnZsN-y?EFHK7?Z%waU zNw0oN2N7q~ab+}UWi)wYw4`OUwPtjzWOO}cfQd7Exib5;G6y{}hto1gTQkR3GAExh zr-`#>xw7W9vKBqEmeaCUp{-f#D_NUQSrFpv9j@#>t?UDj?Dxijlh*9BmF&+RFgW4( zNGxA3EU~XVa;~)SAGvbwEOTC-a;}eYU_5g$mUH00!yu>UqDaFaD8e8q0-Tm{)LHWI ztN>~(IS4E;b{4ttpI|7S^FDIJAYub>+Hkb&^J#$q4MTvEAk4?nJOtQ$N?g$OtRLy}@cdVq;`u__3#2f|px#<5y0w&KRrQ+zKDFE%_W0=$<#Y8RVu1609) zT@c2}74`{BDKxkoa~A^8$t`lWDnSe=G3Exul9Z`U19q`XF_cP!+c0*)09EWVr_s`= zlTz7}ve-6&{#6OacCig=Nft?&GE6zrr*dU*iM)1&FKQ`TKv@tyrhYD<5w%PSrUFT* zJXX76kh{#Uy!>Bo#R;T%6;w93TGk81{-s#8s9hDCR%L34=>NRyju?zUS5)mt&u|RbO(aBJk8Wk6`vY z)F`FZJaebKJ=dI&fG)HV-y4UVRxqz~K`JAlXCNXJwH9Zs+C&f$AX9TeiA8dXbxB!` zae7osi3Wn@0Rbb}2uwLqrKh@$|gLu%eNZPS(Ky^F})vwRB{9f3C zGIeY^^+YdNBwh_tJSnfM4J@P}Q|!hw2qv*k;{~W*Gow+PwC)^;c&30d`eisc}c zEX|W@YTslrR;?$Kd~t>OvjSwVlN=}oa%oH3ZAN-zD|@ z3iH=XlmBr`3~4Lkr&=nRR70JH7i=tJ;+8Hna6U|?WiVPWCm;Naom5fBgn001H) zA_@u)(^|g(KC4 zqP2vhwL~H{MZ#4@-`9N^{~clZ8p{M^0& zoBw^~|9s%(?dRj;>+9p^>+A397vK{R>>C*Je@#%BPf)l|P{jX{FyElicl?4v{+s{n z*Z!~G^Z)&IpJ1SGM7)1wivN3jMRZ(DbVPQzTUEGibENfPhQm~m^K6;NaCvx7RZ?ed zesgm}ZCiH@c&vPIsbqYoX>q@6b-#CQe*|(g2|1kE+imbWi%1Ho0qUYbu*t7v9Nt-LojyUV6UJX^QqyzN6)OcbGb~ zKAqRdK4Z*HCP)5_Yh=H&>gU#EXZyn?gcMqWR-@SNesVNt&GFH3&KUH71Kw5SHG3oXa_&3&hx^oNrkjtlj1ID-5~_vHIh zP-uJ>$CE~i5R5e}-HagYK~9SN@?W|qU%bGcM?Nv%Rvlk?Sb(rilU#-G^MC7}!e}&_ zV=@;8$KDYECK9f!L~Mtw_l|L7i81e!#wgFz{(SiBFz0wIzdCX^w9A5dtMMhb@E z;FRL0=%`e8cGAq6#4XaoY08hH!jcy)gawRTsf9v7pNSbnJ_H|{0AGJZaK=2Y3=8-g zJ<kxdIwMSHpaT$TCf6tM}0!}mFI zoC;2P;+9j`%Yu5JEjLQZ@8?oJRN4L|0G=@qFj0VS6uSzpnKM;`_ukCkzoi9Tld?pRVOz(x7B!NORo1CooUm%q*wdzMTIK<2RmgFb-`#6E8Stz33;j ztG!TA!+gC-`mW!8JuTtKbR++D>h*52M7;BM-7V+7>wjbKy@R4$+ip)XO#@AnqhtZe zAX!8*O;SW8D@~3PB_on^&Ilq&vIL1DDmiCR5K$VM9GjfmdC<*Ke%{R_T6u^0i>?@^n?)mS|m-7W?F6`er>EwKnB+Ct|r353PMV zhxc=DV%-F}tn_3@`SzY&Nb7n2j#h6*U)KANAx?FxkalpNIosKq00 zSUZ`A3PU$d6j-mfJzW|%lrLuf?&A!3+8IGkj=?;LXKx4N87dqtOOy`{hjp^j?MA>f z^2ySBJFn~>t(bg@Vp!Le-J?I4Pt^$6JwCh9|84jD-?V!~xh2y6FW5b|?fs->|K0BK z3=R6z?upQ>Fr)rAyXWX_#?XJVdvad#z<=329`<|H1A#YHTI0{AKsh9XI_ayQlG; z>i^X4X+Hk&-*(Ucj@=_FoI@}5KJ=&8$lGB3)Oy6y%rw8c%(z^tEuoMw6Op*(50)oR zJA&7$NgJDa$VrYfjKb%e`kPnyGM{&zf9H5g@?kC3(Qn0f?)*p2r}8eop$G*sx1ek7 z7$08;CRZa;1995t0ec)R&{vr>(w3J`-t_>~?c(eYs+;B+xUsZbn`C zTz`S$rS_~vYmxixQW>AC~GaGsD99jf==d&N_Gw}QA zt(Ak>gqC%^s&}~*a#-py`ItWwdcI9q9=JwElNF&JO5s_@-JgEMyltn8I(mGkxGF+w zfv&e@lz*qn?d7(AP)`#(8@~n|!L*n$IJM8hUv15S%;rp<2w!yjKKa;GnX^~&?sK1U z!aFAcS(ixb^z#GBXYMcGYp~-5m6xTLKUkK1zYu|bb@aIJ{c3-I_};|F(`Db0HQlza zmH}v?jg?>uq?!sVOqReUr2XEkz-MF^3s@~>!3lNDgPd|t3-nz+W`8FBjLe#L^Y~ds z@zXjL?!5pIHVfCOHNU+$Rtg3QoglQY`00S2KR(y(XWYsjEJbFh?wb!xRZzkn#d%H$Q1p^G_S~v zjK~*Fk=fIcIVX`o3rD_0RH1%UkylhnMpRi-RK;{uC}(u_KNAvi0N;N8Jvz8K8YEniT!Q&eDR8%{$H_snzXhsz!?hh zyC>Y63Gthwkaa=VcnpH{0?IAt_{bm#UL<4%4q3)<|LG+OwuRuSCRn;cD7+y{2?UuK z1o1#)cnLuUDlr2NAyeg`R83qAAb3@s$Us1V#7JV1Ofug?6A0f(PIe|p%uQk-04G%^ zC&{`9(sD>bQ-FpLI#pA!`C#&(==z=bXUprrv~XB-sxVM=|1n${l2FApQZ=U zW&}xQgcxLmd1pjqW<=h_P*1ZHzzHYGX_1lT78|1Q}Ug5FiV$bBz`Cx@Y3G#AtA@jM!bl^ldh%Gk{2aH8!2c=NUqnpW* zM1gWK@`+u*Vs-^YU~s&1K9^x04>*?wjKzY=XBEWK!XUUg17&?t5TE->V7=guA(YQB zmrd*S!~VQP*#ZKW0%JOG0z6+=3JZtsHJ)4{ofH;lLXj;PSGc+GXLTWKGl6(1mYok4 zr&DOoBo?$U3y_Orq>{e`&D~L0K8J z0$f@dML?rkwjx+wHv=gE`@Vib(A->JK2x5CN~9quf9V6+kS#yADQ}D-NNcTVnJLet zt9WS$SsjIZv8z~eF6ZofLpWbCrv_O}s_NjbT2Dwg^hr8atNtEUnp2jr#9FnMkU+9lO+r`$nty{|o`AHgp|OV? zoh4!q)Zoq6uy{jQ>~-EyXVvh(d&A`m{QyqN0hh66CD5HG3g4>5$$QIW0FkhNLuj8^ zyq+Xu4~s!B`0ElE0MY8uI{-UJb&vNXd2urZ2@6FDOC`yFla;d6-({(C%Th(! z0(RR%RmNOh)>KmguBH6=f%+redyn+)8|!Nu8$2*JeDK&<7yek^6mDd0{>aJ_Zewj` zZ)5IgXX)Z#{nXLc)5-3clU;<1U9z)vrn6h0+S-n)9bfgb^Nba%IPb${yW`Oww-uB)%Hd!VsG@C73!lC&w0=dleMPpY82d z{;|^YW3_i~tq-x@Kff`surY+(99h~LTi%{n-kMz5`m($^wY>gyY5DuoJYwYHoXfZujyyGxDRmVk2O!c}{jeZ$tbzet+a;6JUEWFC{@ zmUsIrqXmGq@}c<;lIEAS(t(aiZ6%hH z+EP0Fm$i~+yPRoLRJ#1ap=~e?v0AN zFLoPmnwHBpDqD`$H>v=VX0y7J%6_w^m!o|1?V#AkX6>jl&sH5^t!&j#Ta|A$%zA8W zH6p@zwwo5y?6(1urhL13vteWVJ*t~$r{&;_{SIKQl<$1JINI3xgn`eCYQ>>;K(&E5 zD^Tr3;s8klRpH(31gw?aE*k5K-EIcY&D|d6aNfOM_H>86KCa@5y?%hC*#oQ<-u*$5 zDTn+|{SjGwzJpOk>L&+duq$gtLwxICLQ943a8gI_$>C>x>oC}_s?_Mi!j?q8-j<=5I0`U1y5Fyl#07=7H zd9n~Az71F_D*UI5DSD2lOBvRcr_0%%+ovnJ;rwTSwc>cTR#IGfwqDV=eYR2E!+*Y6 zHw9QLO)Hh>+W<*(zSD{?aDf7>m5be8&Z>*OLGhi7{ZSQx%Y#Whr^~}>>#ED6SJj*PaWLid^YUb+3Lt5Yfoc~FqFfAoSSuDCDgdOB zi$&eriYJ8%gvR9J0M-iF5EVo-ctz5*L3~ib%tU!06~J0~fePUQNE*Oep=w5j3dZCS zS;IQ$W>8@ON%JpjC4_}ZjZ~5Euz=}=3&wxa7Vp1^jJ(IonzAjY2>$sa*+k5^5ZeW$z0KaF);3bMtyI*#(12^lvbU6 zC@HO;cbv~9Q=MfiDgDF6m_Q({1|xBDM%Un&P-3PAJzsL>Q1;l(JX%c}&EyxK9mYg! z*9L7}Yn2BsmSfv*+;tw_V|vfa89N*~Y*h9jbJNdS+~meR@f*yzhv?2kEa89=zb`P% z3qj>k_L5) z!_h-fqg59lUj$ZRW*>mfFj_0h7g1qhJ{ruVe}q3RQsJr_5H##Vh06mEB(xaC;jgeF zEYaMedMg`34WoMV;tl#Pft9)uc?Jfq02dbZkAxTECr6Er2ze^_=vVR#=7ujdZ(N^5 z2T`;D|9h)JPV-RwvCjGC=~!%rAEUT@MDw^eKmyWD!{8O$2wnf`VSlhAU_l(B+hdg>Y8%DvZj3 z!MfGJFNJRk&UY|d1(D~DDwzHR3lQZVuIF`lqb9-%S3 znX1;S&cjP?!*IWyx|1ucGBz*9>g0@ezBgRqT2#Et$=UP}9PegA-iB%Ki(9P`4RwJ+ zL>Bt=w1YO?Hydt4Oi8ksx_&e?HGU_0NUuOM5_VKq0Q6k5v(e5_c}kw`K5-5=pIz|i z7LJ?WeClNVeT*thC~3^z#j}IqutEG|*9RgqjE=eW$DZ}+LXJ)$%|A{+J$I z3#6~agpgc6Vw0XD8Gp8(NT?0(IZ7AMc42sXVlY0jknW(DAWq`Pm^{#1X6Li?-)+K#BZLkztKQR4{A88fOWZ4pM@GX1JcUahyKjwVPYt1z zB?jBvQ-d$*Wlg%L2Y`(tlLD<1QTXkHu|v~2bKrmCLVT9X^Xt`S&jI{s%9o z7isVj16$qim>73AsPKeAb1^Suaw1Oz!xxd;$`%Lbd!GZt*7bg_<%DA|y;@O`LAj8) z`E{)Dt6aL5^q%`B_)Pl6KO=v2a(=&nm-+AH{2%i^0Rb5pOhH6MNlZ))fzUu8v?L^S zP$)eqDFZ3#H8L_ra&krr3MNWQW=cvHDk@fLYPSDTbhPZ(=-HVW*x4Dc-(cnBXXg~* z;*#XKahs1@R)9xQh)4M*FHD42O_Wz%luuKP@2>c-d0(9Wz68Ivq`(6yfrnSazOa$Z z%}27LCURnMc`-8u@xSJMD<#Q)=+BYgQu&sJinN)^Z4;F{#xOYpn8HJtvNjB+rK)xp zrmg|gP=jfztKQR4*SdH2zP8qV9qlV}-bn9(iGdE>=%J~xu9=CUsj0EK*&_=JlVA4$ zQ#%_ods}lyTXPqCOE(8APbV8+SNrGgj)7h-A>OXxKCY2IZZW>DNxm+bzRrcuoXej( z*915>1vq^Qbm|Lu^5wbxLV*27fc<`e{ZXL($v@<967=sn4stjQb>5Bi*hqYid>K1k zlsR6JH&jy$Y~K3cRS$e@80h>s(9_=E*VWhG+chxIF)-LZINUKj);>DfKJm5n%N#Iw zw*mr2?-^wKx5ducz?#(|-G1<=mq>XkmP?tEE8 z0h(IdnO@uZx(YmYcYStcb#8fSerW-@G`F<$ePv~CZEay~ZE1aDZGB^NePe5VV;j)g zU)tPR`%hZkLM?CYE^Y52xAzye_vd%6#&#s?aB1&&dH-Z(|8(u(bmQ=J`{)#PbhdvC z;O^%~r&l_@T9E#iIy*T#|2F}A{2%&reG1I@zjd`xz4$fn|26Rcn*0ABnD=G=Iq#Fd z{NJDVuPguey#L?x{(sN=|8LCuz$!H_B;vV75YrhVoa}Tr-fvNX>5OI}1ri8S14e)e zFEi1~=@=`RLX>ts>veh3C@YCw+9KuxF8y64L7B*?Ean2*{aqsM1R;uYn*#bQ3y^{= zD_LT0C$E(pfh<-MS?V-0c~}-tIAxy5Mz(`?aI{xALoV_E5;-rdFN10mumm252-#Qa z5?K~9NYSw5zo|6zbor(xg{JLYH^t9&%c62Anok^S6}}%Vi;Gg+b&1=TUlLi7ypb~O z9#osXy{ILQT+8zwe~{CKzEHWj^DfVC{buIHV$mJ!vtc;de?iVCT*ATEV(*H$PiQd1 zSPDn;0@WTGV$)A!;7e$&*jONfnWhhl`f#w+3X+3zj&yK&^f3v|m_tP>IEZbvg0(*< zgz;ds;quvF5_-^GBhx=DyDo?cHpB=Ksb&#<0xH-423*PqF>pBrF(1Bu5Y*6IkHc#d zNK}}MCsv(D>@;KVNWRQ;CPA>K~y<;=um*1k2c_9B{r!JqSRK$ z<$okgQ1CL>mRdcOhi0nDL^hb!3ezZj)T5ymuE^4!5Q@~rEX=#acfKJJy6w{l*6$7S zsu#pl5DB8HcZTtO8F*|cfkkSL2=XfbW;6%3cu&T#ZT2Psm+!=r&|IR^n|{yus|+it z6(Z<)eL^U=VxBrR4alDj778}97Gj|x(iAzp*j zC7MWQgJP@)LxrYKxy zA7ugWxCW5(qJ~0CH{$cjGjltWIY|iwuhI6A)rA1$d_|sWBIA2Hl+_w?eb6D{i3Uwj2JC76%{GF25;Nolt8P3cdGXR4?`VrxYm07=5op3$ zHLMvUL?K(UN<4-@%VJ0wfZ^-v6pW`G;fuXU1Zy6jl4tjZWXRu!8YeUd7TrDIklrP@ zpKV6jYt=1!LTYayj{S91`RGQgG{<8SMJlxncs1>1)L6jq-%C(oKaaPmkb8qxRxt>0H5@@V|5jpDE&PU^*VIxn0 z5gsOa^xY}o+pSofxx+hH zHtIJ|=^(+WF{Z@3tgvLfX~v_7=_Y(b4x`Pz6AvvHnXn@LBfHg{x^#EnDhr$Lwv#el zbPflH)&kQtsN`f{Z;W-zi-mE-0{1peR7RF66@60>duOGMaP&HnK`4fn(ERyyO z-1i%yQBK&vd0csQSd+PCye<5DoP+1B`A-u|{8R6jhDN&=_+FAcaj2o?6F*%XD2N@; z`oVAjyL~(VB;JbWK2urz2Y@E}7azHf#f!y)L4#5JANfdpe0&fHL_k0Q27?I+35ke^ zh>3|I5C{ng2^0z?B_$;zBO@m#r=Xyqq@<*xqNb*%p`oDzL`zFYPfyRlz;Nx_HAY58 zCMG6kW@Z)^7FJePHa0ePcJ}MnuXAv4aB^~Saoqr3+}zweJiNTTe0+QY{QQCf0)T{o zm$0z#&6_txL_|bIMa9I##KpxWBqSsyrEcAlmX?;eeOp#WM()lXd0ANnSvf^Hd1ZNd z6?p{}1qBsFMVO)zOi4*qSy@#@MGXd1Q&m-0SJ%+c(7b#1?!9~Ww6wJD-@mV|t^MG^ z105ZmhYug>>gww0>FMk18yFZE8X6iI85tWJKYH}&@#DuPCMIw=+|<<6%*@Q(+}y&# z!qU>x%F4>x+S&d$!>-rm8%;mMOHj*gB_PEO9w&Mq!4uCA_bZf;MXK6Q6@ z_wexW^z`)d^78if_VMxY_4W1h^LuuMeEL6s{yZQcATTg6C@3g6I5;FEBs4TMEG#TM zJUk*IA~G^EDk>^EIyxpMCN?%UE-o%UK0YBKAu%yADJdyAIXNXIB{elQEiElQJv}2M zBQrDe#fukNSy|cH*)Lzd%*n}l_3Bk_Zf;&)UVeUlK|w)bVd3l7uZxO`ii?X&N=iyg zOUug2%FD|uDk|Q*c~e=M zA3uF+ZEbC9Ywu|9=g?A{C-95cMJ$*gBfckp@^$qj^>L2LuAM76( z92giH7#tcJ9vK-O8ylaPoc!|T3&3#Be*ZoB$M8v(wXa0FwlgufJYE81_#W|F5CG z6XWvqT;+EhmWo0`3y8zw;DbY8q^2E_lsCmL!iCs6V;SUR1`{^(dgIu_Q*^k5*}I5- z>b$Ls5M~+7;2~)sdRL3k%%(EVyYMhOBeni;ld?fdT8Ogc!RWb zB;>n=yt{2`NxK6#t&HZnmR*~@f{o)f1LtdPEMB|gyyu5=&A4@F%FLyjH*>gs`WNqu znyQCBUC%l>n+mB$1}i|xHNqB{M5)tR#PW`ulvl&2wITwQ1^;$i-@yEaDT!h8|I8Qr zk8d_DE$uaWdKSiO*IAf&*ja=)*+jXoOY(Bw=I4?Xydf{dtt8A16XChKvy1Wmy0Zfd zx?ejwNde%_4oK&gpx$j^!#g6zK-OMC+)Po@TuI788PF|Dn6#CujJ5h5YfX7;EhVe_ z%GUQ)Y_wt44^%C6)GZ!rnCaey>uH(j-+!!s|B-?AqhCuoV?!N4kBlBZGS+?kNYCW4 z0UWrV!;Q>--O$a=A6u9|wzM#@w7j~iU&#t?Z3QseX0|rw_O=!dc9u`TDC?WD)(uG}gf+&i+w?ol&BlQIfq;ii2_5 zlSdhjk29T2vRvRVT}^Y{%nIDiOFS*gy)7$!t*Zd}+0;CAAO}Eo zf2ua%381%ufE;UroT`JItAbrCL!Q0~^{5Q@tc>uki100qd{z?uyf`N4b!X zsKS)kg0#f^%+&m>jQpIJ|MtZ$E-eMV$2GOJjqjS?e{A{C*4p0L-r3#J)7#nI*8>;= zT|>h?BclUjW22MfpQpyBzfR1|O#GOgn4kMRkNAv0e3_g3@_lY<_Q&+sA74Lz|291P zqwgD{Yi7Rv>q6Tfy4u$P*1@lp-BS1W<(?lay>qL77zpzlgTJ1R(?kn>7zUfszq};eh-c9treJ!h9T$rb@68l2a zX%p2(Z=Gx?{**u1jKW=QcF=J$SFR(|saV_QP9XTDn)4*DX}g*2XG5MT0Rtm*f&#Up zxnd2w@iI-G(yf^~OJx5`_5bzTy8VyA|6)6b6AY>o>Iie1i*Y}xlq+c3rRz=n)-+EY z?#3X6*o?qLcHNVeWjGJkh)<*~7XvWzOPi z`7|D`qSYwwLfE(xr4kNM`3IYf03Mtvi>X8*tt_NOS2CJJ_^_G6mYu)MiwrOR9#>!@ znHG1OB885QfhDDpMN&UsGmhnA`nx7=HQI;DV;>l>1RU3JaOV{Pl%NeboNSfr{#y7BijX5B=uXicM?` z242$KHZ0_Ml`_9Zbuas?HZ}T@^H>+P#iSh%t)*Xne@W4iAvBJDQcteh~ z>~HT6OH4IwY>Q24Ac=%6ud!9P_Dh>rw~gx;I7w`es#kqjTIF#O9bzD#YVBd}h!J6M zi;4ZXAhKC~=c1FmrgQslL5wkt^@T>yxbyyyI3i(xxR*UkdVhHCDY>SwTYZH_&lfN9 z;j!h;{gK}5S^FbnD#6JVqjKi68tpS7Y;h9E?3*LwkaWM{FGk_PlQ(tr_ymQAxdsm< z7IPIyrVsYmMkJAox8rIiM{^&gkB&cOgGer>B*)!;Ix;XKy$Y4ND>122Fxmgyd{9Gt z$-;kF>V#cpM|#cT@!e1N?pKO+k0+3iOKvVA-`zO~cseYun)=v#VAzjM`_@^@-4Usk z1h&%$qZz}l13eN7vG>=TYN5l2)#i7%I_aMa?G4Mi#Y%3rc8qKsRZkG_HcY(UI-RGG z(Y`&0^t*R!=|bV;!uK2H$mPk10?F;e#oE!0^QLgwBbC-FTlT*lVJP4TqsO$xC2?-?>N=-^iLq9n z%P+;pC&SMpCkQyw+{(b|c9UC8ghySJM^lU!aHN6L?Y<=cFE3h9S6avbIND@x8q10T z_=$$nc@Dk?yY}PyeqY z4=|<808{$vyt_K-ejjtc5ETn&dn-2wYY#^oFGm|6CtH7K+dvn)5Lf#!H~UC8`zSZt zXjhw9SL=9Jt3+4JWLJw+SBnf+^X#YQ`R-=LUS_4<=H23nrlF4J(XRJn zJs(DUKMnP^_4jr3_H}mkb$9gjw)PKv8W{dGIQnU5{Nu>vhq398ld~Vc{6&-?KYjg& zTE8x~&MX2YHop$6#m?WD$@i7+xz!%TTF?A?@BBvZ!X}_TK*+6rA*wo#!sdtqBqpBrO$=u(U1NuVSPvh0?EWE?AUsWx9HUm`r#*Xvj zQP;K7_}rEq`l2Y+b>GwnU=uR>CB@Bmgsp(G6*#pEJKbo=|CQbOua2<)j{qk5E}|47 z9Z^NNW5L9HlQ!nDf>9KmangJGpQ&!4C(Pqz7rDksU>uR`Pz@~75G^@WTMwhox~U3O z+`=|Ek*yE0l>T^F3!7L<@idvqpE)1I>VS5^ClTtzz$P+|I%t3C^@AXoNN5nHz!-8+ z*qa;75d>rQ!4bkH5*RfohQWhwSP@(93Bg!nU}j0|*&h9+tX?6colpZAwItG#q9{Ak zhx#tKRA`G<7X0g3x$@fFP0KH$Ze%HHSjTt%HiX9v=#s_O!J6BeVl4BO`V_GG^UM@)^2pZPjVxmZn)*3GC7X>MQflk?f0nN z(5CAyET~p2U%75t(5tbq>#uXkt5h}>d3U8&{N(%StSSicTCZQ<5Tdcl4(#rI0v`uU zF_sqW^{;0?h`Mec5z|d;?l967(KuH>4FNv%(+SM^)HA}8c>ZDH|bNP~|@e*S5J zAVI789e!`*t;6A$;P2KqZxWqvbsJfTnZv#(5zgtRVbrJ-r_S%S@}Uvdbg}*asW|KU6DC^5AaoM5UC( zehcLNW|>NrlxPpm#QS9q;yW#ZJ?yoE$HV&MxA%wDJ4d9y!K$Qyr_s9IKKg3UbRpR5 z_lZb!+M?jO_(96f$mv|~EA&A)u{9C$nU6x`MUb`h?0q)+nFzS}`urUXWmM4D+-_lr z*9RQ3LdMo)6T!H^TlU6U^yC!?>1G%CCpy@4^oocbJS>t#-fHT0SlCHJn}YMx4n{Qh zCLW1*My#zgTiCVidD%|W4qONu-PdV6iPl|WFN_+$m5pwS#yny+#>C(c`?+$xyCl~g z7sI++yTu(@N&IkxIBLlUs4ck?vM-PXUY!NBBTcO~6+#>$-sa-fn8cE-LNUI{Dwx|x zA$KJN*u@p`z~;j75%5Gt=YxEGNT%e68*eE&QTUSbs!1?4WPEBx?(N0hSi9c)yh9k~ zoOjkpZnP{gX*U^xmcnDKrIi>Dsx3}8$71a%_EKy7=E%XL;K;gs9JB=f4JFqmJ418& zlDhDXi*S+*KV%kS7EkPlo^m8e5xjEJBch-k@6kN*@~FB;DI`+KuX8_VmmG&hl2AB~ zEXI_|ceWVLcp4vo@voiqF)$*KV*qepM1c$H-z3JX_D=zK*Wc0*=hYDB)BGjiu>ZAz2A0r#_auOW|4P6r z`qwu4Y68%a63~?v{Bt2K0+$mtlNUGty^#LJp#I)QTPsUiD@*;Bjj|N*qzfArX&acd zjp}V{H5p4aS#xzcGYxsThQeb_MPp4RLro?9yUMzERrKz`^t4pt)4uwadEP9b8&EYed6Wz#Mj;Vxu;98murNVON_5uysvAb zw{wz*L-JFb6gTTsH>-3vtIVfX+3r@k9#*ftt;_vvs-D@t^|!5kZdV@w$i5-azA?zY zDcGSY?7w-q8Ef zp-)vK?R6vFO@rMny*;g6eO;e=d*AmCyzd))-#`3eaI|%JynS?{bNox^#B|5xOy}os z?VrE5eVJMuGz5BLdz^ zDbVJ29p<$)U1N{Glk>@Yb8;;t{OzA@Ze%%EJ8A!r;^D7vEr-TG@8gf$&~aD1YICdK z7%hY+21y@neC9FzT-5DWr14d_;_=ljH@&=Ew{m;oLsohfPeK|OL4%@Nc9U+g#tq$v zjaLk+bOPiQaLPtTx7_=J? zp}UUJPN;!)!ypoK*6B#Ry`Fo;pq5$^@h~U`ryGMULX)H~CW2qwu3KA%NOwwNV+k%o z|6ow>zm@0!7*xYxVt_%#Cg#h|UCrlg5iH3ITR*C*38vjR#E9A4+a=iFb@a{+;VZ4d&_t#crtS3iJ#Hr_W}&6(I!hUDE~KuN~=&; z-45n1ftq7aTV-v%X|&tmYedK=1cLZLm=P@D65}woc){@qwiw>G{A?&XfI*F77w)AM z$Y7SogZ56h{mGzeI1>sG$>Gr1Pzd*3&(Mld?V>hf?Ym-7g?bWY$9tKdLC56lu}4Ze z|71{G_QY1cMZ96$%p!!kX)Iez@wwWd3WI`mbxC64GY3};s-h9s9y7i;jkB*g6g#R@{A0rAcHnF@ z{Fs@mji53ws4p1%8t}k%?WTO1Bf>nP9G6)8O##>MC}r`FyJE9n10jMTdGtCWGP|K6 z6n`?P%UVL{5_lJn-7X0nT0jF+%ooZ4_Qo)PVQY$@*tt*Qp=C~|?-wRy(+1YbUWks$ z%ae5gCFy7U(dOpWZP=8^+y-MMiys3z&n#Jq4tj1QgotfU#^Q(JVM3eKL{y?ECW{M@ z0M#FDZco|eURcMd(IIK>rgV!GCaLVsvoWqDgmI}55L*U7X~NiHthdSfberEM;$bQa z@p}_l%3?=PA}ly}S>oN`yXpEC%0l2wwTFyJsZ|=wY$RkvhTIBtleyiJlR~MF`q{3H z4O)q03K3CXbBmdqjS%kXgM||zX?nc6Me@BN9y&MUPcy=$>S%{-85C%KphZB~a`MA2 zQj64Guws#sv0+bPL8b#Zz8tj=#F_^C6(5+BRj)76pW^|Wi(j|oc-xoO?wstK%)rtAI_^1)a9WGm`;V~jhnu09S&mbk9b#0g{&_S$& zf6H#BFH2a7a}6GSFEUSCF(~=<1#cOe4$hH^@C;=Cn`b@E!(BjnS!5 zWE%2uYQ)MBXNJ_5ny0F03)SGoZWY)XKQ39vW$RWR{KnJBPG0kEl))3d+p_~h*9UpfT>6Gz7uB^E8olyM%NLi6XXgv2=RZy_r_a%om*}xe^e`Gdcz)4; zdfvZ(-jBTOok#b~qdy|ijmzko#mkZ<^y`Joys68VgO@J`FJk*I!Uisb`!4)DFTLMg z`ZgZ=H>^FYS@y45NG%_(Xlkx+{M6Rg+1J}Y(LVm8^Xu&J%;?O_>>jW0ova+3EFPaOonNe? zul^d~eFc57d~q;;zCUwuK7mF98oj(6IXfRZJ{~wjb)BxZoGdpSFV>zeR9sA#T#gl8 z4!=YXW?l}YT@J*Z_QxIeCTw-5&b6l^Tap&)B9YaBE7bujRer0LPOES17Rzmal-hlH z{bcy1+i1GuSgPGlPIWxv7CenXH;Q0}_0;dKEUcJAlw+>zG>!*8hg^SPMuMc?I3Qsc|e z5Xjf!FT4jx=(V;)vHss(mjAL4_a8ByU`w<9Y})MCn|r(c%EceJN;3J`|kPg7*9`I{O=e~b>FMsF`o3XDxeVe!+X!r zub+O!crH+=<*OKvjL6y1RUz)rMF)FXWBatG^CVdhqGSAcxn?Pe#Lk)qxi34JP~a$pb&SNCOuZ_D#l~D3%ZK& z7^Ea9U&VO%S^BPGJYN%&wXb44ME4_s5z4UC5{U8GE~cBM*>c2LW~14@kDXj05Kl5?|D&T>m@ERGC+){SxF6u@w~ON1!6ql znWCI9Zf?umI3UIok{SiXcz(RLyNdD5d?|nX#g4~1>^l(Si5=?)Vmx$9_P_`=Fj3vj zQO>L0%MZkO5|_Y0jK}f%6ClRpyI4O1#CYxw`SVmZ&d1q1)-UChSJtjoZaCI#edH0S z+8wrcdUN=rT%i1Pdt;~M63ru6%Kg70tdOwsp*~c`L z+KMZn^&c^wXN_nqtCH9wdH=>Y$5%0)rg^{fAuZmM)rt5!{~qHxpNN+^=^QT&e{4M8 zhD~1hZ!w<7=h%$KyPgG);cb{$V4Q!(c-{|TQoU!IJl`4m72`3AZ`~iG@ASHZ*k}8< z7>~>y#4)*4+pO4PASJ_e<*yjeou3TVLERLe19>c%eXn9XS?Pg1)tk(?(g>`p7!P-D zAc;sX1!^q**!y>kXN2=pkttZg7}fq$_bSFC==` z2^M*HVm#?pLyDrA3_OpWq-BAA&Z`UKK@iKt2h>$V%GRKBsInw90)Ak+4c+S!d_fv+ zE*){_AhGkR5O;MV{7%Ybh*MoY<(y5snxGCY-&G-Qwk#!`W#~=1q##{c7z;^XF9F9M z^GQGfJLgwrzm^b^RS7&WdS8*mi#f`-f=S_wrWYv0eIBec$x7eXzkYk1x!mD3Z`N^t zkYZ92P>74qXG^ABhs`L%4B`-tWa4cJqObK@;Kd<}<{A#7uWAiaJ@M+dGfeg<)zIO! z?$ccOoW%TX6L{p(@lfEbb~;dqd+Mc1CiXP{+SW;^;LUe^u`MiQljsagx15L`r$Ms- z#tdAF-IFNWC|8!kmLhrT7tIc=+}X%%OG#X;u?P>h{GSrVQsPl}J>o)u05ZPRI%#kq zQEd)g+oJSADFgS(K2y#`ICY6EZe5p7?l?PvC~f}(d#M!WEw3Q?3{;d2SqQjxx?6Z9 zC{&-~u(T?oi{iEfF!NK{E~nH)?BMD)3K(-Ai$e`oGR0@+ zxF=mY#M+>_j~!IJG@8~5gvz7ZC;6bYbx>w2&SbuBJrib#R1hYHbapVKC=|r8aUWt? z&3bK1LOEm(5$Vy1q^vmEB1Ov?uk1;@yD5RgGl?0mH`V)+T{q0L+sblBJG+er|#0!BH~_#tEXmE2}64Gfeo2=f#fA z@2m=mK@2g5K$fdR$sKs^1vAJ~XJi7iPXO5cf{H;NWX{w-27yoKF*IofTpK1OA~cln z^^wJw_79NO*vt2_U&RqN)M`=Hqk}F)h&Y6|_KtBRcCd8f5@9eW+=^IuChgo&b&t?2 zUL&aDCvz+3?0p={f{tsqmR%%gg+iXkJnY7z4UvM)AdzUV*FWL*ZYhI{(qNqa!zW!S zBxTU;bpfj0MP*(c{2gn%uP^MlLWK=cmU0{(%p2Du$n#bg-5%(c;S;pVGR`BvW9hfz zh&jtGy?>7*pwH~kirIZjZY798YV0cyP9pjdX|#TIKJ{m#;Ex91r6p4e7#X^;rG!7M z-PJ=)Oq++?`m!6XU4WeIR6i_>Tyf2~DMz}K;C{>I8O(@{bc%@rt7;+umw%FKx*aX3 z8nH3s(33XB!(S@PI2QM~x{Z7XS}8j_8d>K_{|-TQ>{`w9$Wd#m`Dd%6YDvW>9$621 zJ+1GQsA(|2@jhfl-~Cg^MZ6FpI|2vOQVwSZs?r&Z?~dop-cUoV__JfUsJ9z}t~q@Y z@t7I=2z!Ms&S%&O`%S6Ihn$~0zJVn<`!=bdSC6Dz0`-kq`nM&zHRsPT=KSeB(bBt# zPEoT=LHos3(fN_#eHN`4fmw_``*!HZ(%W;!i69E89!qEG2L*=z-1U+!!jjvzWq!7o?|{fQBVX=RN?N)VBT|3=WqN`lmH8MkWG3VYWVL~6%w zWs{@gV@VUx*EGB48u=QrZvFgXDQ5Je15l#l3c zJ`lxyKo|VLx5tqnFR%eSC{z^FcvORh<`&y3#Q6)sHU`957jgi{zT*W}55VpDLP#zP z+IA+?gA%A>;D-sKweJRClS9F%035vlFos0XRxTkETus>qnxJRm_Ao^t6*}-4GT@Ab zwgf--ins|6#ZSZ^$-QUZtIdQT7A*nQ>kH*U;8&=m-egXrYO^ND3&#z_Uvl=w-*w5f zj)*An3+wgq6nfTWUARtYGHoJ<_f;W=IThsjsGH#1YyFinB5jcW^QQ1Y2 z*;*vB3HZq47)1JQq80269HRDGGYw|zEoVFDQ38|};ax4rM((qw-i$cf81-Td-vr2@ zAZ~a9BvMiu0BMIKG7~Hm9|+>2TS=OhN!t2kTA|pU&N8i9*f;_BcLOX!nPmz*@yGh& zKXnscM~e|kV_-~_s&8eGa^MBvU>fzvVrAfve5P{Dr_z|BGX0tRguTmSkOOK?nXoU5 zeQu$mb#Tw)y4J;4U!SD}5N2Y1N1(E#R^81Y7D>!EUPKp1plxQHieRrcLCiP|61fB^ zN`w0$H)7~uhQCQ3g*|xNjjuhU@iF=FHjX926Zg;tHk%MI5x4t4XbLT z_Mnyip%=VWZI`#42}VTh!?8P)pH-w4wRIFZN=g5~&wPmoswu$;t}gyk8^ssyU#d%j zUM$AQ#fSvwf(rVIEvcnBx?fn(WyYx4Z;2Cx&`G~?F70iK`LLFSmuJdok=@>BnlF{T z0jw_r@K@OG%@Gg@GvnuD5V7atFJKc1Cb)}LXJ3qFH~Yv`3kp|b$j?WTMnlcnWuY8a z#%r07uYwj^0fa5e_&ZO{CGkk);jbD$SFqwbc7BE&BET6dx;-fxDv<^w2jK7IWPXi- z@uGyn{ifuRiL2uIt0=-!Y$L@4DQ`F!fv};L%Gw?!l1h+b>UYYu36=!~bCu=Sj^5!j zgqlO0)wxLDOcQ4I6g*rj_~{HPR!LKAuED`e^XX27sll0ypm3UJq;i4UK4>Ve0hFf* zn_xaowmEF5vF7>e>pT5m7gcKWGTS6D{=L==KIRxO+*<}!6xm~^99^eo@8;!Gho+9o zF@)Zsmu5}NNRWNxXRMqU=9YxqbyU9=uB?@BT~q4$nkxBwN178Tp4u&jRUCA3^?_Q*i4A#l4laG99jzfI03-l+cR0ew{cxRj8T(zga1&GJ^JtoA3O=K6aq zXPai`a%*p^LQvvso@f<5b*O$T@htRqMQQ0@Y{DN13KOTK$esQpw%Y!vU5Q^xmO;{~emCkljo5Lfe}ta`NLc8g4;J#O?s(^Cr- zFCAyDno82Ifi<_Piae^e5~`8~Tgh9)$w%|Zq4=wuEie0|`ro(ktluHAhw2ML?pZjJ zkP@g1)~(6rJfQI@u>RDa?&nzIr!e!OklMxVq{f>g)wU@V?VsT5u0hJH-o0rb_!A1+ zN_hYD9ETrnLuy?Znh|C?9cJnSDk!dTbEyv*g*4`-JI`dgoe}&rYE|Iuw^!?TFKbPl zv7N;?;wqL6XqHy$!MW=^z-)^h2MTCIsuzFhQ-Mn7DL4G^YzT`gp*-@3>69^2z`X=J zFIvm$&&q1R7elt*~HnXW9Y{5DJGW3c!1Q@vsCyFw{kT)FE89Q0;Q3IR#EY z3+d-dnQ1++rKZ6{cEK#4PO!O)dTH>m1gRO+Pi@R1Ru5~82uXm#%Dl{X3v9_2231#E z&H0?sNdShhtImlBPoP^_M+E=y22Q{PHbBF;)VPGA#Iyr%vLJ8$)6PE!#rX;cw$;zP zTL_$J3onp5f3Sl|FadB-3XTR3xX=Oz4M;8O0qvki&p#@91>ay z7#6GmhR_Z01P{)T1HrQfVqFhNa6ErN4(_nr$PHu4{%t&W%D|G!XpUA7N}!5a-8_ys zYQyuOmZ*aoc?gRto0JA~23){q8=Jy7i@$XZJOJ0f>0$gZ1Wfr42=L6iDvoT>*W&XF z_DBGGPy^=Z1DUG^F$`9j;@GoeWmtA^{kC}j=Gss24!Dis|KM1VC0T>Pvbnw6ArT35 z*%!ZkVGA_NDBg5&4MLh@-|X1n1bPtX96RC#UK38?2hk1|{tw_#SK!cQL}gTjL4lDa z;v6y3ohKM4zTya^;!bWxDeOT(46HETQ8Hf64N>DZ?pN`e<6wpm;}BE=@dWPh4^^Py zjo|@Kh2*Z$3>p1X29yc-a1_5MIJ!wHRb^KS?~qFe(kCA1zFH2TyVn4UO{0XLkH3A-;P7!K147bktpykLFJx64Rr4Xk?;FnLH&*pPS7~&e(Ruo?9gqGH82}3gfAEeDI*9HDZtf5Z`YZ zAO>c`1rP8j56}f`V+Ic(^2+l7ePab((DMoL^FcrKM9(}%uQo{!9w?s=`mhfwk6Qbn z5BY!(u;A~MLLv_k23deG7IXG!pZ03M_7ZaiSr7(F@AKjQGBs28c7OLHa|PWV_e($b zc>ni+fA@M1_gT^eI+HYrpZJQu_>8YKu+RixANDhz5C|{<9dP-WpZS`<`JCVRp8xrv zANry{`lCMq9zQ9SPx`9A`mEpjoNxLLk@~Jb`?O#Co)7yCu>oPw1-#$;zW@8cAN;~U z{KQ}U#((_C-}_;(0Zudd3;rPh&>#KMKmF8S{nmf|*q{B{zx~{={h`wR;2-|tKmOBC z5#)dV=%4=9PZ5XH{_g+&@IN;IW> z;X;ND9X^B@QQ}036)j%Gm{H?KjvYOI1Q}A~NRlN@otLkm3JS@r6;l4-Jr9UCR)(i0V@mQ4`1!QHlNFaC`Y zad6UO2i`WGTlw;=u9q^09^H3xjlHGwChpLB#L(Ln4yV3wbNK7!&G&SEUcGwnz{#&? z*uK5??7Hjw9i4i7U-@Y*r5Nz>93J0XIBT$Y(VXgpcaT3~R?87tM=HAyw?_OBu`3Qp`tx zywp-kRh)FvP46qN(+yD^)y40KG?hXpBfT&k48FarI(&LgEG1h!z6A6(Q~Aa9XggEP%fUUV}rc5=xU(CZAc6+ z9$-3cwby35ZMWZsJ8rq>rn_#t@22?yFQO(XVr%P6(^_KpKqt*1+zk9%fdGwjPQcS# zc3U;4B`E8Gw*H3?ae8>uPeB45)N+9Y5wzLS)zmX$)Dh)#2tXs>ce=r@ngYuRr>ME*`eJ>d&41{vS7s zMYC9C$+?qHWZkS<*+I9pmQX$Gq+B4;GuE7&_X&+0%{zO?k?BbHK1$5#SL{QS`~EO? zj&}G76nctXJBk&)e^3xjxEr1EzD7PhjW0*x<5L5ZRW-34FJur2Lm9BQLKe2rg)f9* z3}raOxRoIceS_1*L}$82L@rnZ`4!a=B|eKS?r(MiA_j@LzD^X2ego0m{sJCZk~<^|(hq z_R)`j1f)uDUwN?f$jv7vDk5~P@B{u8a=5-t#QzlTsxF17wD%sqUVGU*+L+N{y9uy7Sou= zM5f%fP{;&s5l)k1Bq|F6%MBXpoNs!joOHRdUh<@l2?=I01v*fI7Sx~z1>`cjho~ne(jXsm_zY(Pb} zY`H*d2_k)*ZKqRBD4B@J6|c9&ZEkhD+v({QBog!M=YF@*cB&PdiRr!f`m{_=36Zh&zmE*+E2N(v?*L0Vq4zU z*S`0~Z+?B|TSoPyrk1(sLFlVr0vFi82i`4z`>PYEt|q_+88Ct+JYfn~7?}!Qa7(at zIp?zG!3Bx1g+)AK5|I`5;ubvX;d>W-`;2 z%U(vaK)_sPHn-W$KRt7ry}af(*V)c@E@qtNEM+?5+0TClGz|5;XCwDH(1%7e&$=+z zD{SK%kA^g)U!g)_4w}$8Hguvjy=j$A;T5fT0?KCLin+Ri8?Hcgs#E=qR!};jmxlE~ zGRcd+B1{vADew{o&F7?+C#I0Iu$U8d2l;r+qUo%zA*)T zPhkp?{>Cg?7KljW0%mqdMGzDa2x-{+g}_eP6|R6XVY^yoiGcJqcHqKd8=Fzco|(B( zwr-t~`)DTjS-ihpw`LEaDPHkjb(iLWdW%&_(vNMUf7Aza}McX-_Fpo)#-4B{v|y8?I}in96@!duvJkM{`VGLyLia*i{U7p?C+1BENZf%LigobNFAS>9u2bi!ND@G;wayAhwV z$JY$#^bWhqh+Z_t6O;*$Zo z)*eLmSLSZbH6CS;3&zPF2lMBri5KxM9@6P;h%^P`_=h|h%^$aN?8kp)(?`1HLq48k zE}>HZ^t-qEQ@rylobI!>$1y+#9Ke$iz<Z$B{OAv%Z$OKLc#P=!3tN+duO|835e7 z?W(!*%fF*LnedAfL-4*4JQ);BK*PyDdBePUi@xvMF8i~XprgN0y1$y!LH9$M{`)o; zw7>&wzzm!R`b)n)O1m;}0tNW~H(#^5w^M*U&^XuAJ5o?M*8@A1VK_th27}YON7KSI z{01ce!!6Xf{3E-Q*@JF)G$lY8jQa+SBe+L{J)42JE?~M`W4cANy>Cz%E}XqYw8L-E z!bD&OJyZgO+rzSBxH_yvlHRQRP={?^Y5a5HEygNRZ8-$eux~(~x zCKyHK8zl-9I_xXHTA;vp3%_b>wkUK2q2mJ}pvClyH+I{_7Er-qTp7kwL6vD1y<38L zqdri0H(b=gm9s|htHpE+Ms72{PN=yY^aJy=16};SVpM=*oIzlGH$(t8pi_Wt)InsV zMx>+1&eObVyvABoK4JbGymiDu2mHmBQLJ{Pz#R-fZG5-NySH~MN0KW@CUm)H?7UWt z$8+#Ly%WcinaF7TNIMZoR;;&ZOvlZG#qdi%Z!|zt;5HQ`$9QDH0}RN^^G83by_31Z z)6+t*^Fpl)!-RXi%p*9i(>gp<0NwjSu1kb}P&h@D!;OPVF@!xo*f`nC8Hj7NNZW?0 za|J|98Pq$yI84g7!#F%7xHz1Iw<9>VBuh!)HmEF0k0b@8RLYwvMN|9_RD2m#{6UU< zwpX;rBQ!vu`$lM^25!U17OXaXBuwW^K5XNI#MHKDd&YZXx4r8;a?82NR7?Ye#dT!C za2&^tght9d%>M85%oQ|0k-N+V)XdSG$&qt5l-bO}l()Qdg!Eg?c{5FvAv(^i%>$%N zhg3hxq|Il`K?+>SfD}z1*t^Z^xsD9F>*LJ`+|7{;x*9l#b0atDY(dbBLdW#Tjs#DA z)J@)`KAa@AmD$NGv^Ipp!=HRZsw7IX6w57Ky)pd4rzA_PoCKs)fJ893{EW)1Y)`aH z(3APfi`zZQt4rIQLrBcJ2-Qz+3k0$}%AR|}47EfpR6W*yzr#ab)7+>3(kfd*W#qkmFdwGUD!O8Qy*|lYu#7oOqrc)*qysPKSjJioy9?A zO(uLc!&6sqWWj%}PC&{@D|9;e+|pRBRr@4EO?}mqA<&U~L#Ra69oSHosYBSqN>x=E ziBp*d4NFXw(Ale1Pp#Dp#nqG1)jGt(TIEkq-5C-cR`n=WmN`}x)lS=-&1C%8B0bI- z1=!03TRc5miZw{*OwqR0Qn4jYvYkhB)lP?0yfk&!Y?Dm3Wz1}IP3QdA*px~06I>|m zywI#z1B_RFEm(0iQf@QOlTA?@%}#wRzrFjriIrUCyH4xWPI{|>zdT7nE!Y0Ajl#+k zIrL;!;1pOrO4&#a%bD%Z{1Zx;En0iy&rG#9*ZWzhyvvr+)t{xk1ib=(^S7!yL=F{D zn-x&3RN6OWTK-I0qwLT>%-x)^+N(8>to=XX8_r}^r!}C32jp8?l(x_`Iaq8qf>m6h zqeo_h)*OY`nnPS@E8lyRM*N*ecXZsbmE0s9O?k^Xcmzz9QQyqsw)nL+dK6z}49M^c z*}ufbeZJ5ybrKx!4ya)Lo?GiBslWfaDzdZS@{T2H|Fwwdg|?K90~zQ6U%W~95k7;aO8 zt;LiqU43OPT`Oh?{v$)#UB^^-v>>QYp}bx# zro>F#=YHN}JG_P|NYzbbOGpz?j5FGqfw(eYf{M0iN|R#aha2c%6;?zpG(}zrMy@sM$=sMGrhYR$v}*<1+XlN?+P6vBp>5Tp3Fx%RUY3py z?xpGJndzhsq%OcUZuka^`#eW;V!G*RxB2Om$!Mb)YNjb_qi!*mRtT78>gZYOvi_qf zK(%>3nNgbsi<6tHZkt%(2Bx#?ySD3lR+_F}ny&_HMp(%hmI0m>@z(POr z>w-A!N4sY#_y;VA1xJIlR`_eP2yDXU?9Q&U#wLjVsJrVXezd!GG>6cGM*Ie8h=Gfk zgvHA2lGtp|rtR9!GSD7~Z(ss~@BpZ51rH#I&qM8KXzhPc1lG2Q*N$!37HfdA?dOK> z&mIZgE{G=hh9)Qo;cf^$AnsKnyX1!L3Gn?++W^ea4a`{ei1ebAu>v97>@DTU!DW~!7zVd(AZ-7&AFHZ&hi^6C@5PdFCYN&L1_TvH7=GteyFSFdLh{d54#a^41%^>3N^O^@tdm zk{SAezl-N~8mLKn_@J_Z7?*Ncn62mfuJ`({2m7!W`>`kcvN!v)NBgvQmxkf_sy~mb zcl)?+j4`46y0`ng$NN{2`@NU>rRV#*xIH*M48}`|bCBj==u#$CB?Sf0da2@-Kh!SN|D7|Mo|T^LPL4U;p{n`}nv2j)4FB zkAC`JfBpA=j{t}=0tXTl`0pUXgbEijZ0PVI#E23nQmkn4BF2mwH*)Og@gvBPB1di% zVDco&lqy%UZ0YhP%$O%h(yVFoCeEBXck=A%^XJEcLdyUxYV;`5q)L}EZF*29)TmOY z%5-Y=D%Px8w{kuHI<#QduwuuOEo+u0)wF8Y!aVEtE!?eQ-Nvu^GB zHSE~3XVb2|`SA@9iFflhy4c`F+#yB~FK+xe^5n{wGjHzvIrQk#r&F(P{W|vS+P8D> z?)^LX@Z!glFK_-ldgvi=_ipbV=Wn7GAE&Qx|33cw`uFqi@Bcr50SY+abdSAvAc8^t zW|3nAI`|-j5lT2Ag%w(OA%^GWmLP{6RutcSH)J>>i6xqNB8n-hxMF_>diWxY3xPbN71J^J|LcxICy+R#d1r@fO4KHuefs$)pn(c%o}G9a z`ktN<@i{1?jXL@$q;nQ}D5bk43elpGYPu<>oqAf~q?L*~*rg9;`YEcZs=6wx+=V(S zta+8{(5bE3dMmEEe)?*xy}~tXL$&HUEV0EJYbLM0Dq9w?3<-NIw9!gC?TE=Pdu>>s|v5Y343)eMfNH;1{6(HQ_VB9tYS?#;#dPk6apLkF~}kRiLk;Y`$TU-4kM=p z6i_@P4$LrTEKW5|L;-~qBI~>}&tQ^lGSFKx%%*f9G|@!MtBf;FI!!wr&MGa>(!`BD zTYWXw)dszFQ79v%a&feXGfgznbdw4yX``}ADotB6%POo`!$j6~+kN+dKXbizNM0WV z_6Sad0}svNlrst?k|-|8DCMLRPB$Vpn^Cg zk*F>SD(Q?9&eE}afjRB913ASkt$g!ME3-^7GUvh91gcHs+tgNESDQS-q{>dn! zNZ<50-NX$JII`HjJ@+bd;mYpcxB~v}u5i((=EAE#5%JG~FwODT=(|euopd6K{ile6 z&N!lX)6LS@}cg2~4EWW`FE6^!@>${*s+UFc8gu{QUSOqFd zA-xff5EbKChdNNWjulc4JbXY$FQQWjaR^}qglGp5rgMf)%%cX!*&#AsAfO$_@DAt5 zA%&t)gflF`2gYGy5tL)ZcEqqhpQz$<9uW^GJ|c+HA%qu4V8mcd=Mz2b4i=`M#x=6B zjcmk%m-Qh+-A#PzNeexx!e4=Y?2wP9okh1RLT|hn=9} zac&q!01;7)N91KEkeHk%_y7&i@F8)cP>f2!wIH4q~aTMB+eb6Aw+WW(LZ`@r+~sox8g8?ki>a{_>N=9cGPkNk9fx|noy2< z*dRc6P=zG7LrF_o5|hjUVCYrX3QKbB_rgDr~11u~92FWZ&4VKW%9|jXZ(WK5aqr(Sn7U%vGMCd7Vx+$J;GL?-g zq68k7qsJu3(VN9d1TOyb&OYw06=5jOH|AL!@Hvn;i)aQSY8i)e1mgrFI7B;)U{Hj* zilKI;h(ni?!ga8N9jTn?=|q^ea-^`Y>_A~EtJw);oI)KjECe~)0S{gfXP@<8!biub zNO&Y64!5jFJ3~s#9QGj{$FQj%${|@Lxb=yhK*BGHGgL!_@rC@XA|d8sh<1?ir2GuV zPkq~tGajdlyB#hvKhax%l98pcEshakd&W=jleoeKZWWbVoJTB|y2vHY4f#m~+qPqi z#-Zsl(_0K*6bFjPnPPc;&_zh#Qn>w$?m3YAh~Fk>zW!9)AwR?DUF;Iazf_!}d2M*c zpzb$^0B(m#xeHxw2DPaC+@g1R>ra(pr>Pz;4>@<&&Jce?8a~wH?jmU%N~qX5P<>8S zlY`awmh&4~9RZ5XNli+mIH8HSBXOq6<8MS2$E4Vu681=(AAh68EADY|>{wz$oO1+w z=*I4=_ zxW$Dqv6M?(gB)u)It#t)`tqtARjyDS;6P$%kFydhS}|Hg}19=e@=I~5_YajjYwSWZdiz)Xm)+M&0OMedz!~~A|HS-0+=Qz z3d0zI5`U=E=-`Wl~pEMqvw>1tq z5sx3N+Y`qCbs9SHde;)6;>otSQiChuh+kaey_*`_Jql&>t zr^S7L(r0h?-$WH-^si4Gb|XiLOuTq|=j}|SK7zOouf8vlc>d`JeA};-{!fGY%;%rK zivYJ_ZLj}vn9g7O;0y#aSYl7&}E>B;A~qMMDk59Gn%|kj0sE z)ZB^{nvua10@BeQNMHzVla>BCSyeHeoIM>fZIuQt#|T2;2f|?9ZHJg42l$L#4mB9_ zom~Wy;2xOZ96{g@`dHJQlMqT^)Ztysz1Vg99pG_};33B=q!MDK5II~S7Ft^WVA={D z)+;Dh7~vWtsT&a4RK{i2IrP>IU7i+s9ND!4H@pK9!52S4m^Z;xG<8!qJy;zE(fX|) z`-N8>_S1HS5guAwb0uH;Oc7l2_A_Z3GIs8@PzlY5OH5>;5V zb(6B;9~}i(A=1$enV)sVpBJ$k{VkY1po1{&*B~ZXa6KQ$1)wPw7>8ZQ0E!cL9Fz|c z=Smf3ZIPIkfU&|b!AQ%~)oBIc6$?NT0ESAQ8|Pfg;wDPk!? zS0f^2FaF-Zp&uX`hY?8}QrX-w(Ucj6-%j}?$cH|-ZC8YRNLB2vZT z!^HzRSR04k{(<({VjY^-uo=h4@nVOyktiessing%Ou=BfQFjDdonh799V4K9L()ak zm8~2e&0wHK(hOQ7&1sq3F$a&Cqg5qilxf*GM%B%c<~&+iH$>!Bsn`fYV^u-pYDPyZ za07C10TXP)H zZK!hoAfHPShgU`cd&yf^wio$@5gd?G@!|C=FBZe&DEw0N+3ZF zq-&O7Ld76C_FO}vQ#>9AXF}(orRmEBp`1=9bVSe-C`Va^nf3r@4h?5pz12I^5SjWQ z5Wc`cE}3(7;BrQ%5}qkK#oW7KCw5xoam2!3c>^nu=caCIH<;%;yaPCFLpSWz7lI)j z6#_2YfFYDa5bfG!nZl~UBzVbWf4(6R#NO(Sz#_CmGCaW(n1VX8o3s6&5Zxak=THSLh zSr4u$%rb!xIqY;yB&7n4r5cA9G}<_TL-lMbIlKaShH9vS12=dB+xDb;F8&rB@j+z0 zLo+zi%PkT!q!q2wq(9xNvewcbqyvGbWIMcrH0Z60&Y}LXXdgD5bv;-w{1hLKsDT;f zZz12cUZ{#bB8T2qa1Ge7UG5q3SFI-4NV%(6M%#lW7`>iiT8^c6B^c$pq7cENzk=Ki zkr8~E7aueOHHjPZ321fskiT_X!>Sm^LWjgE=3<7=IT>J!k(d!4nV@0i67CKzoPx(< z87{C~29|6$E@N@zSVWRoi=i(vU7$0jDFl*O5GYxobt%*~851BOb(#~Er7D(Q2l$XA za)g;HBuCku0VSw|J1|4#;oR;-r2Cd*qax&@!s(h)r-`{4q#D}S{!(Pv8V3`o138RC zJNOl+Zo_%H!#k)jH~>(n@?4-~B6Acedc-KT*f4-RZtb_{E>1n0XJ}iJ;90B zcCf;HuyJV8I`GpvY{ND@8b7J93crKsxWlOiO8JrC4F6&c&#tr#pM&VIQ`t_%f`|w$jC9b1ZaDhhQlfjhXvIB>%& zfWr#2?FvI43@@@R-!gE4%|Y zXhSTo@G7^1D|7)aXR|h2%@IRHC-=fZ(1B`!vqKm&GAD)|Cx;j~fh@ps+q!}iwC6U@ zGd&wjH$TKTBLrI(vm77uIUhzk*Rwzm^wr$6L*(;8OhP;S!a=Y>B&-@d+`?5 zLra!KA4DOP>S_VNLEr%@2s1ngK|^13L{IcTfV4=H^gr`NKoc}f$Fx1;1VRrdJovyr zyn!dUgEYhfE3|_$kbpVsG*A09P?G>1oP#pNLM$xO5&VOi6?Hj)Gg0^SPX~2W_w-b^ zv`e2drpPo{hc(X_bVGD9tUVe$Ai+U2f*dxqC*1zDIkPnpxHVj30YV5uI~)TE;B_z{ zga`bCT+_7=@U>ra^-jFBSSPk(9}8JCL?`bhPE)ie3}PJ)fibheWK(uK48cFdK@+e7 zIPlXkpa37FL(;y0TWj`am-cBJc2_Gix-7PA&-S-8_Cn;dAE3h=$1yxC0kHn|3tV<5 z^!9H9>kGWlJ7fbbL_%Jpz&}9NA1i|m@V0LkH)=ySBiVp!3k_^H3~hfmcpHmtD+EIK z>M^iD9KS;lEQD~Yw?YsAbs51y;Pq%DL>`DjUhj~5H^hB6_jca|Vu!bY5BQ*rcS0aE zX14<`{DVTD_d&ct6Hs(NGk9LJz=JnI9R6?iKM+7DG!RT{I^X6IDyBwjK_(ABLqVKLur|wIG2~&rbAzw^Nz5<{mir7%Q&B=RI10ppD2Q}3Mjc>1R!3ZT;psjoV#=gp}HO{llJtk1f2 znEI>NajG}O4Qv6g_qwnDI=EK`wiT>$#~8dkUP2qjZ36^dz?GF$BVhYyS$Ujy3adexVybC zOT6bhzmG~w^gChTJHW%rz7M>?$4I>&d|n8=!XHY(H$23@N5V%uTrfPvqshZxyv7s8 z#BY3ASiHyQNv<;l$cMbi!$rrRd{>mb%DYF#w>->~MasuKR3?yHrC_?yL1LyCAA}~V=OavXI0xtZ+?gPO>00QoN1RzL53}6f6 z-<2P11L^<6@^8cDFNNu&{?bE!Sls40rz)*5C8#c+l1)XzUPB}M2LR#v$wE* zgzB6Be6M~SlQW*U0VxdsKL7&oZ-gTB{zTkBhTvmPepYzDoQ9)AVG!*6{3VFQEp+mAQ2l@y!hs!TC2cDLQ+uWWX25r1NNLL5CJ?k!k5NPDNH#J@sTBR@<^Q8OwEspKZ5k&YbeP>%U043?2ghb>%k8!GVK< z`;QO0AqABeY}AhWa<{rV#p_|Vrl5Acrc)2*b~&AM)X%o<^!%S7?dqS0e?R)Bo@-nG zN;CAWuKpWR-@e)FA3y;7CW(LiEAYSs0}N~-y$S&^qJro$=q?0FIk2yS2>eDr0Wmy~ z!-Vuo1i&N&`sO|kC#26six30p#Tg3|YX->LG6k%Eglp)K+p^ipw%c}e?Ka^4$qF~J zbmI-JA)U!gCndK*av>wDG)Se}kkB!>+l(WrGaAV()66p)GL0$JpjvG;lU!>_rLJbG zDJvnj$w?(7e?u1NhGpws|!P<5U&SS{YQ}rUDdU}4FA~>kQD_2QC9&c z{y9()0|!(wrb+f1%+wiU^k~{^O;Ra{ul~fZ$A#*YDW-5=iYd;vn)v5j9_h?;g*^-M zBhak$G-Wb2(q-t7n7DmGB;NUdFNhdzUDHW7g*5ZSj8AvX*AWGfh^yRb8S+3AZ-%?cOM7d)F6;?Qk%DXnpjn0(o3PQsUEOJqA#n~ zTDIZVcQ_m|<}u`W@7C5J$$TrAV=cXuSIByglTz=C_zpM6mdZ6qN|6o@stRxZD6ia` zeB;hsbDS2u{R;{GU46<=!j1>=;Vjkz-@$tSXTaS*FR~97b^a@gW21{R#ha8)4 zR<9S{p@R$59{6x(4`)zl#Q5{r+uUkn4{CAQ0-eRMz^L)MzQ7e96wp6{wEk7Yfi?tt zA+BjP8$-X+mR9=1+@7E4$GNThTZ5DYi%Kwc!7acQEOEW5lK@5bue&iM5@4FlAws4% z#i?sHpA!l=-eEa!sG}WBu-^wksFBV!#bBuk9aTo>rHa+$Vp@rp0lTs~kKqV!Q>x%U zHf1}v3?Vv0%iYR|=N@tx>Scx^&kh$79Yuks7sp$VL7IcR&B-r6gmM1gLiEs$1wqYf z-J>2ut~kDbl&@BOp##|tLOnp>LX5{c8yA<>G__SNgh6qe0P1+HErIDshhv>XTtgEC z7Nl;ATSx(!BuE$5aBpE+NLKjtKr2b`Zl<$fP}<=QhM0ka9;65xB9V@E#KUiSH08BM zc$yNn>4Z`-$?C$@LLtGhCj09X0>8DtLO~)vxi|9zJHcD-64!PnP%XX^@ajh@byrMzQm^L@QuORX3<|%h_M|fgO zfP6FLL?lDHh3Io{lMEa!`3XUtlm2WC4iOi{7V|uZm@_bs z%#fKgNlAx@QS44TWTuEx5pQ!@ycHk_B0vA&g$_%YqprRYwY^NWAXRP1vRZ`~q&3TY zT_ecVme!5ETyYYuxRv|b=uSF%)FkzzE1Fj7(gpG;CZszlUo+O%bgfIE8{60+V#AIItjh==jA(;docoDh8yhgxE-< zB9fKjW(pST&5v?J%YYqehCx^bkz7)Xff^*IGzA_g{;1=r*x8Ibms(3um(v{T5K%oc z8|tB|140Hh=&D^!1%SXe2>>Ypjww1Hc=2N)W|)Y(6ao+~wg#;9U5^=sSgU}>I=wf- zPmWYvElKS9Um770N@#JM1HW{n19QnGuW{E46G@gI<;ifx>CHkWtP6WmDF`a0EO18> zxrS=;AFSx$XCpe%is0cKr^RA7&^yK$w-d(KaDp5e^hE*hm?5pzh=@fBTO;h`bwsfz zkC7Y~r6rjt0cP@(jZ#RI09msnZh|+mun^Fycp{B(M-fo#7q6}}iv;lxukg}@ut9mp zKF$c0DI(f2Y*^EE(-*1rvY{PqR{vSr)dtG5O|oT*K$IH| zafCaTae|3#b466+s5-HFD{F(W*%ra}y5Sw~qTG?*^|trD@ttpd@0;KK_V>T5CGQ^} zMcWTe@n6(|3jk0=jB%A?ulx&6fzPkqoSFE=F+OjAZ=B;D_xQ&_?(vLUoZtsvh#A0f z;%UNT2~TkO6c)ZdV2$xt6B?^PBI5oAHH$61BVSm>De`Wg51r@+3Hi~Ho^+)z9p6P~ zh|sfba?YmQll$~Ua;M9dzc?#JHkM7Q6?Qfs^-S__Y!5@C{kDvVI zH~;z3pMLePpZ)E3|NG$|fByN;pZ@i?|NZfwfBo;D|NW1D^#kwv<_-V}kgk>w`l2rZ z6>tITjq<3k08vc<|KR~4uma2E02wd?HIM-zj{+^Q&m_w5m zaOgs?1z|95Fi-_)um(ep1!EB7UN8rF5MgG}27xdL{q68Xum?3w@?6UZm5@vb&*+5k z38C=bh;RwxZ3n6F3hCnqqi_qk@NELG2(hrEtS}78a5J>93(@ci1rH3%(3Hlo4dE~@ z&M*z>&<58q4q@#L@o*3A$OJp04gnDezmN|j5D5p45E0SO{Ll#lu@O^n5EBv5^e_@B zkt62N5ixNA`Hl@M{t<2_u@gbDA}%o#NwN7hF%+Tf6H##$84-_8uoPi2`c9D*kxUgS z;st;p2y`(Bcz_nka1>)P7?)1&IMEkN&k)~CB!r+Ubif#|aQTEW8dp&5iqRR*#1sebgr-(CsAH2Ymhu8l`a` zhw&#$t(1;pf@q`~t+7n7(IJFv^fm&ycBZtX2qn<*P#)4PlnaM&N2z>H9tT9nCZZl0 zV$v$Ynsy8r`LQH7jwj}T7N!KV>}-()az+TUAZg?v8$!s2Oe9W5nhc^5>_I1OA|eF= z8J-A=#DN_Ccq(U{P9yiuB0N&&6fz`dj>q)TB&qV_hC-Ky0(CBIAZ1duXp$jpG90nP zW5y7I2Rav{PJCwsw%)M5|LGG`j4AesOL%ma6dYdDu^ z9gpF7x++wL@}{(I=<-q_2qap{tsBgY6eeWM;0lgp#jHLOSfr)Q5F}l~Q&<{=tGGtz zYK8vhm~ashFf-}%--3dlzGO0NBTxqIHNgV0Sjb)c1$6|}6o7+t@Wn^?1xNhlMcZ8>z7(zLNsfgGDE}KVYHieng1F49vItK)NxW>}H zj?!=pj*`Vf)D1CtWz}x9i(WKo5@OP(W=6AcD(mw|^(`p+Q(rVgpG;?UBuHcM$0end zCT5Bv5Q#FpM4=2S35Nm>mZ2Dmffxu)Bcy;#&xArP6d5_qgkUQi;bK#mV`V(_h>#MU z&S6rd$U{w3cXmicyNWI0apGK*ijoD{zK$>_q~ZPpdyIu^216sa3qTS=exzkt1pdT( z#;!=^6G=TaNq0ge_Np_RRE2B=KeP07TvE4wWK?&H!3xWeCdiV!G$^8=7+gjQ=FCjd z)J)Wr2QRcBGISx0Csd-NPTvAhX=+lai%)?kc$5QXhH{_o6ytO7vNWMlC+UQKmJCp7r8-#aFQDX~^fPat=Jd)mET#T)UP>bF{nsi)#D} z^Ex$N+1B+&;!2rRCl2RH{fS`rbyfS7pbYGFQZ=C{g#63drlEBXi{!~S0wbP=X=vt zL86#at+PeYw^1SUy9N^$0r8$vZ zS&?gDlt1>BB^G33){+A{lxxCc_qlO(c3129AO1vEOf%PT6S+cPnHXByW(xwR%T$(e z*WEUn5Z4)`>9go!@~J_?XRR6rqgtvnljy2Ct2YCy!TJNYnyWF>tH-*I#5%1Zu&m9R zD$$y)KWMGx8UWq;txa<1zPhgEg0A^``0g67OH!}@T6+Atuyrr61$!R}8?iIzupL|V z6nn9y(dYI$vN@-zNe;7HZ~$jOn7^_qI~pyUv)Kl+MH})w`?G}+@k;yWG&|`0@3mn& zwq<*^X}h*<`?hgAw{?5BdAqmUZ?z+@a|ju@cki@OJGC+HvWXidNPD@{&bW^|7Bfz{ zox3EOySj1NNTVBGryILX<+{ClJiw21zz_cq6xNP7RJ0N8bOJr1I=3yBpD)$4zpW(E_^7{Fx*^i-BruuRU>G#vI^rRU!=<~ z{L9098~%aJt=uS_lM2~<+)~RT*wr%@{=IFo9M1(jBMu=#vl4I31kfQ|&<#S&r{D@+ zVH=8kC=z`MBYoH~;?XxF)fXedDZQ&P;>hBHUpM{A4a3t5f(wS+6>LJf;=wGt<1E~< z9f%!w+;JWZ!B86E7z!clcG4|l2Rq)Q*kd{47&Qq5Q$+fLiFZI+7{q$MNHP7gy-MUn zx-Mz-j15(n%d3}yiv+761vyI9# ztn{nRwevhxevHm5wO!hM-`jfEQrCyMP zK%G8E?gev#V?pJ_K=1f~1x4YB0t~$V>%l(k#eVF`zUOisomX0AMnxkM+qo!x#b`rV{+&?e(R=i1_)Oxy zQ$*wnY=Z@j7{AdyL+FQ|4mZLzN-2&lgKh^luoww-s&seq6-)KAH}r-}={Fe{J}&Bh zA?`j~?o{u~k?*-z|0yM1em@_-HzNL4K}K%nnDq(h_50P<59`4kLXg8GRwd}d43_q5-zaY1AaZ|n zrj_?=BI5yK-V8eCdi*;m=opHBjT9bih$7*_gM6sXVAHABK|1G#9Q+6}q{xvZOPV~1 zGNsCuEJeB@1?1n$BnM~yqmwG;%b5QFNsIAs($AjEQv6d?6eJy0JCrj0xbtYzsQ>gt z1L_lKP?|TN;?xPVrP!4M$_fx^Hm%yVY}>kh3pcLZxpeE=y^A-m-nK3M*jf?t(BFS- zOa(cZ=h;QWAR{69=f~NMgOMlCS=AJ>WXhWlM%wlm!WL(-#vY4ovMD7g+>&A)m)vq=ohe;A?aZ@>Mc2XD z$h9+I$&j_j6cXk{-TecKwe5I>rgjgh>zuOk_SRm1@5Q%ZPv7{NRaM=5BaI$(?~z^ESFTcH=512NB{wpA9Wraq}+Cbkk1L#%yrTwgjz{(&i@q z(O8Yd`*hb{e+~9O9E&}2*=C=O_FQ4-#-eUYuMKzHa<3OPa8t81ODo6i&37xa=*E$| zEPcuD+=d^Hcx_{oop$4nKMr!@Vg=jbZEs7CdFGhcJseAyuXM#LCy!40D_7Vih`Q^t z)UCB^Y7Tqsi7yU$?Y7@uq3j`$1Xu=fAuvjESNepL*!yI-DgP3xm4}WNv6W&mVM5G(%^jE|r z>TrcXY@!q20z)NA(MUC9;1sWzLnc0Pi(HH%7Qd*TDw<@7U@YVM=*Pu0HZg=`G@~0! zBF2(zVUBdHqaE*v$2{tBk9_Q-AO8r*Knik@ge;^X4~fV`DsqvGY@{O}3CT!Ga*~v+ zq$Mx8$Tw=THgP1$7A^qFP>OPtq%5T=Pl?J@s&bXAY^5t-3Cmc@a+b8Lr7drX%UtSm zm%QwyFMkQlU};nyhw064x-py~8Rt9iiO+oMbD#X|r$7Hm&3MYQjP(2< zKo5%0ger8Q3~i`Gg(=X1ezBk*vFAfCiqVW}bfX;Ys5B!g(JNB)AQttgNl%K>l&W;4 z3I%CMr8v@ml$513t*K3Kiqo9-(xop=VoYbM)1V4PjrQf^;Wi0$l$J*uV;QnsNn6y3|z2){?La7P!6G)YZw50L{oKn4G&kNN+#lh<5y9xOFXV=8n6{3L;Oi@9S!C-{ z;}b*1vpLAoY*Hp$l}Ki#lT~@`Q#vBbHih$=18mDI3)9W~gfk`XT*@&!lMuK_gndo< zXHmvGmE;bkw=X~mSR`1KiO|Nr)y>O$``P}$+&s-N5v^!OpVQGNCKRMOq72HW!yQLB z$RN_uj(Wg?6^3eTickBGFJ&VZtVjnue(;Ysc%mKfa0M&ip$;$k_!Y4r2gU!fh<3z- z=HdBugMyvPVPC)rm2Jf~WO;{kJXw?#F@$(Sx$SCS;IKaiWwKMud{U%bge~% zFS!oLDb_iVK`C~#gJQcqCPF9F6>XJzd&(7NZ+X!zZb}#o+~US}d>2d#CFotwaYpY2 z?oJ8_i?ZGq05d2l!CwAO;a`de<$Em*O8y#RVRyGYeD6%(Aeh(6p`@?D27LK%pxfSj zcJQ53e`kOb;_rtaJ-AIz_`0ikl>U?m!X7H0`MnFQy|V|{@I8-tXWReeme6uo7BN$|@U7zo{p9@dHNiZ5n)(g6SC&5RJ0y!{27yWlMhI0mby1dVQpbcY03JZb2S9KE*+vLY=LctZ zghq&GO2>6p$89zBd>7Vv{`h8J;+A2Q@NVLkW##p5j)!0JMTd_Uhjy2E*B45RcL7=E zX0tb7grIoVhi9oEU$ggaorj2_mxzj&2z8bUch&?h@Q8}|X5go8nYf4n)`^FxXKYw| zYS;v&sE4S;Wox)*ibr3C=!$v5U7px@-GGOr0B)=oX6N>21x9XqXK0o;if~waq-Tf` zmWP2zdZRRn)u)BW_;|1wVXwG*)?|Pn#(oT_E7=fX!7^y8qF;xQe~Y#aw(tT57=VP9 zaOHT8rVxPFxP_-UN`^3vg|>z62ud}hU*1S)*!W5er#dVr1P2iZO~4A^ATB5X2WItJeVpx-6MEIUxvqE=azYu*Li43Wm2h@(Z`ltnTVjL z3R(GXt`|z~wuuUJmvO0=V`grvw`P2JWmDOFrKFdEIAE-}VT?IoeraFCXnBVTc)O?s z8OC7sHhaJaZ+zH@tp{-ThLz42eLz{4j#-V>#E4K2K zkZBgD4lTC?qJW*Ml_di4CH3G0PoSRaNdjyz38nQ8x^R*xc_gj%KRx(^K?qA-NQ62W zltYPKQkQm5=Lq(I4o~)V(N%_`RAfrl4nvl0N!Jcb7lu#fpilOcQU-o_DSiqAVV(Dv z=QVzc=>mN>m@dGITvmMf#fpnZnZajZTX}93rZABQZg)qcr{@BYn215pqB;5v>6c;9 z2&6Vz1GvYR!Kb6jXkZ;`Fq}!6c?JNSosb zO0cP%pap3mK>(l74n}|w)|omD`H=0f1i-}$H_{0w_bwUP4#IU1c;J!esTCr@28K`$ zjY^;Oxr467l0IlkFDZoL0ESGblNYdUKUr;#P<21)tK`6K{7FhgDU(T8bxYY}TUVjr zHK9;xTi+RajiJ5tPh;AyHt*A&!fLMz$nu{}9n(}s|f!Crysfj&0 zcj;P6Q#Ob4ntA1fVibtiI(Jw{-*f0 zuVZ>=AbOW)ntlXZdqJSFY+6l-=8tgNnsW-9a|%j%il}>5Tq z_b6YJ z@d!Hi3al^=*7*;NuxlE}ai5lg>OduI0I3>R4!Y(XzeT>~V4%?fg0Dt$>%s<{5OJ|E zaiZF_UTdEcgrBpjpRkm4GPz^Zm8(++h1FKJN=BgDbzPvecHE@~w7^})>H>t2b=9S0 z--QT3cEV9;x8Id?M6klTO1ILwp%Vsp@0Mo{)(YqbdEAu-AWWjkh=}DirP1iaAl$B# z>7_=xW`rkhj<;r?h`ARA#e^VVJPL_We2G;YW^mSwTr9ZIcme*1zyqB}jXC_4$(X~9 z>#kQutpWC41J;b?I*YcLd5ai};ku=@Scm_{!=r1)vbctUd}qKYVW{_vl_!~V9LL<% zpq+qSaobGZtGpwdfYxiAXm$aGO1+?IsrpmR)+t6qnZcB?K~r#jnJlp&eUnP+wh`dwwa zW@gt3QPz`V_@IA#UV@2xz$eDVIHG}DVQ=|;$w$znm;S`H_|Ib8!6YZ`~8ewc)(he4fr+0iN%?vgD(Ml=} zQxLi56`2)l3X0}t0V}%>tP-80GQ6pPmaJcj zn#nOUXniWyHIsjxY=An`Wj*sV*f@Vg(_n)NoT=n!$zrP23@6t-tJzFS+nifQ)zH2a zv3)Jr3B{+lG}p(%2BHQGc74|gTtc#xH+&=6elyri_1C==*p;o>{Ir^etvfg|awV5> zBgoiAg4d-)1+&0cq%#Xtu-Qa)*}Rn5uT9(O{xp9+!%K&4Brkz+8CMG}SlU$N*fvz# z#ckZjeOAFeMW>D2&F$RJ-BG%&+}Xq2&~4qi`V4ptKGNtndp9q!>D4&os$;v-JtC2ry;j^Zh<;)jIc zGW1g|4&yN{ZjV=~=4(XFl z>GSgFmA)*A&f{aj1(!uSOn~X3F6y*#>7z~(l5P^Rpf`JSS$|{duMX>;q&bSfFvbEY zAWAo|0x$j&{X!%{ajFTwCw%hoCxPuXktiQA36%m83{UY2(}A`@ z6}N#O1cNO57aBz3B2L5XX9n%ho*vSEEYwaQygm{Wj}qE0Qo;T~!|v_>VC)(Mqg@-!)L z?JVyv2NM!Y;ROG(CzEh``;jmJ69dxz?=rVmtG5%2cCvEYHA1TK7>q^A!PO|e;U~EDkY4LLO4><&#P*+#~5#V7E z81V{XVq)AN?@Mp>_wGP&ukf|e_382Ta`W*MLlV~?B);zQBf<9&-}WW(_7zVku^{tD zkqwIi{<)#|v{C-^avDG~_$S{qDPQ>v!}4_k5Tt_<% zjX1&P-)vSi#GCcaZmDf{B$e8u!BnYvMz?9&rEi<$30AyF@pkU+-(-VN{WPNZ+Egb2 z(er$LY(sc*Prd+aQxCfAu5rRR^S<*lT~@T^kZra>Vvbd|$;QHeXpGSr$o}!enJHM=hDRS`9CC&qgFKQT9k*!_ zsyu}Lbh02eOo1D!f-a|_ZUXA3ph!B+Pz?ntv?f5&1aneKEB!1iOv@PSv$Lp@#jVdg z<3pSt?D`8Ty8aopRDwL4tFFR^NR=SE?#k=2$j-5>u*_6NC$WD(`Ad*?0<-j3WZzm8 zJ*8}7$V{Hde2O+`Q;6s(lLop^TT-5Sw%UmNBdX2W5<=8Ho&X`PLhuGM(Y`s^?Fc;L z6p`&9o0>>L-c&$5jXk44nipVz+10~YndWq(iDrFEGb%){qlmbj6jE1UgcRa;pltq? znyJX4<)DmifU{t#3qiOuPtS^z(lhHB-9W=i} z*H+}VB-|(kM>AD}JG>0tzBBLJj=HZ;M&EWDC!N+*GX_M5Iw%>@BX~ip6I?O4x_8x5Y)pW~Vk_=cudJk5v zRRP=lc;uJusbm8WUH4E1hoTMO*%H3oqLWj48RG0>YZM4R*L#k&@T<3(ApY{pOA23t zq<^@4Gatr~H<8tEh`mMOUvIULnP;;-sho*g5(5+gUt_=J{SPY2<5Ovvax(XbODYBI z7W!O5J>?b3f)P^Goc5O=%W-w;P5AuQAU{d!aD-AE;u@zo zHSHjBKv-Le1Z1ZWrcsSh=^R*!$d%AZWptq;-meOxyO-eVAAZTp@K6OS!}!rwy3?ax z5M!!74kma#^5YsE2_^>8}XbejSsREb(CBu=o-y z`UH9agc;#{skiH}hLg%eWxI$OOaDDmDpexl)?)a>6W-7iXCufCeMF>pLPlx?>0vxE z;7kbMIh^ z=Q?ABSHMzP7PY#`Cd643D%1N|QVh5+39i+d#612F23=Cpv9^bN-;L$GFr2`toW{f7 z#h+OB8qWV6cq&%4h{W95Olo}?#0QS#gVuD|37^=n4WnLM6}-WMXb;Af6=!Fql35li zEe|Lp+6n;*S|W+Io&kj|J&EYfdU_3rLM*3N%nM!(A`4Dh!SR62d}geSm%Lu)6nf>T z-mQW~>yY$Xgji9sZfLAP=7E*R7;BTE1>C;YVrV)TfGn(^9);vALH?1fS z&LE;Ma8=Nv;lfyQqNOhhAC|!vc~`kKhMe`di>%m1HAR||OmBAVANrCtAp8}wnblg= zq<#+9SW_`NNV>$4{;@ZMoXnYFhbPH4$^Lj+ZR_(LM5{H%4z`Q~h)TTAQtIomo4qs zVyNYT4miw~Qn6La+-QhLT+Ok~3QVCQ(>NQeFnCd&>4pFb%>?<7r88Z~{oKBx8hUr8 zlM5dUjYrS$s#r;l_)=d?<2m zKD$}yTzUQO(Zy~p4_;?-Kd%&yEKn@H+5gb7hEWrx|K@&{D;PXJMKtUCJ zK^Tld8JxlJazPrrL6u_u!K$!99PB|K{6QcL!h+zzWPyWaD7`2chJT0xVtB2BNJ8X+ zf?^m3CUiocfkGkN3LT^hBFsWB{6a7cLjvqVu4n^jCnL~ zB>|txRz$-gfI=e_#vw35G^~L$EQTg52r~qNWq`(flErBJhhi*- z89>HmphYy~MuPreMq+@1UW|fWq`^##3Q+__cYH^9j7Lr68B$!rQ%uEGTm~hiLt0cq zSaicuEQZ9WLMo|4!dQYH;75WmM|&K{NHjx$EQnuR!hBRiGh~&6tVA=6Mkr9iHkb&7 zY(>b3f^HlJL_El&>BxV$NMHO$ZRE&al)-g`iguJoo4iS!%)^?j3Q{z}Qb9$6P{o$) z5h-ZGe|*W;!oo%5#g<%wqRgjXG)0F5MrgoDiHt;w9LkI=NR6yYhtw~r1j;8Ih9!te zV)RFVG(}1b%7}!)nS6;Yh)cPgOS-H}ySz)h%uBu8OTO$&zx+$U3{1fsOu{To!#qsH zOiaaGO#a4fOvijo$c#+MoJ`8B%*3R}XDkL*5lf+5#Hd8efQ&{gtjDR8N}(hOLzK#< zWJyKDN^k5DT}+I%dCDQ7^o88?!M7a3<;+k0+|M9v&Z&6Ltc1vxv`A%P?hw_0bN4koWc7f2>;|y7>!XGJ--)? z{t5uq#bW5pWf0DDq|R_0Nd?7Ds5r?7O+z0NPHLn|tDMTLltUct#a86aU`$6GRf2Ow z!vwv~CKOI3cuy>4$PW$2N_@c<{f8QzQ8Z0cHT}IaT`B;5Bnv$jvHZa@ZPPWqQ#{R6 zak5jDQcb0b(@GJ^Agoh8EmT82)Z63JM>4_{6(>MV5oScgCG9~%MN~_@R7`y&Ma|Ur zt5iX%)g|oz|s-)~mRJwzxkguvTyW{ni4k zR&aeJY_$ruXaaO?f=}6o?i*KkeOK@kS9lGfa;*xOY65m;S2Y3!JSz)6o78uVE4r$^ zt^y5hqP=-7!O1f%$%7{}lh=e@sbj6avba~bu)lWY89aIm+*wY6B{Hh8J>|I&6N^|C zoC8`Yn|#w8DRbB}gIJU0nR@jUH=DI>n1U;Kg>C3ojkPnb=vd|aSP*i!f+Z%VD%qHY z!PUx~mMy&jgW04#mYEfun&pD~a|O`g19aewmsm8+__&g5JQM)AJKB{_z>MDMILoN1 zJz}}JIEF&N44+-Oy6}s*t&ASATeNjr3l!RrXb%4X+zIiG!#g^&GhF^_Vh!rCy4so! zy5p|)m>hT^k*JlvrF{v1;*uGukrzt6C3z<$VHzXhkt7ikcxsXrDhSR)Ko+_Z*5eX} zJf5WMYx?q+N0j#gMq*!K4M${vI(*^2;0ds$Pkvikc_}s zw7QU9sL)#3dcel9x*biz%qFH zA%NpDN&G-1&ftlaKi*5P&>%TLV_OmKBR&e2y1=83`wJ9aTRg*+Hx^rwtHeBRoj!&y z8-}$H3JCxTE39IW=l~9vSSAosT#*oDp2H970T^Jajjt=*F%CX*3S)T7JQUhrqWP>I z39T8TCn$pq80oT?*rzT*vLqud&6C|sHddCBWqajdv*_KdkPP{fi;Z=!H?|ee;8)1d zojRU@jm6^;MjaE@(rhmRYEjB03V&@dn=P5qr12*M()~|ksp?u=v9?`NdE(mwC<(M9@fbQUd{)d9j zisB)bz<^#=Dc%+yVQ$cj_L2-b4&Iug>O!k%yohQ#qrxAyOmOBPWklWn;?bGQa5u807VY&=)I{7%4$59XlF%jaBJOBG@P)aTmY3VVE zkNL>Cv$H(VR;q=Kk5Ye0*}?H(jQ{q|Po8`T02?J|bI&$vGA%RcS{@VO1}86@xomsTc5@C!dP(I$wT zy+6u(1^r_{3U}rkwD1iN)&or4=1czZXI*eHc5xY>aWkgzYlZO^v~eBZas1?Qaq4g% z4{{8KS|0~;AwTk@Ra7uoawczbCx3D%k8&xWaw@NKE5C9q&vGr_axU+3F9!q7F>)jy z^LZt59v5>mPjhfJ^BqTXHGgwyWpf>Ob2zW_WR-Ipr*k{s^IpYs8rO3^4|G}e^BJ#G z%RF>MPjp3JbVhG{n-ul}Cw60B z_IE^f{Z@8nkM>S<_V0#vY5u?VKcsfhwsvgqb}-a-!&Wl^72EjV{|Uw3wI zcXxkxc#n5^pLcq%cYD8ge9w1%-*!S!zZ4wb!O!3V-sM(r-}r3j_^zmUF60#KTZ@d(SdTyX{zQ2ow^;k*S(JbI9t8KK zUiq5ER}RqGCrBe=2919WjS|N}zs-sy-im|uZkPv?g|+9+p|T8Ad64JBGbRX|zxkWZ zoL~Og=2elOzrmo#3Xv^5lKsD<2NRU-Y?du$(t25^ujS9y3YeOL`+EhepIfcqXr8Bg zu7~7-C0dw%rmGOY{;{`|%T1Q1g*cgyX_`-qwP=g1-P&H63y)Ld)*joCTO^rFTi!wD z7Is_Nxm%yr+u`;>@qOIG9TDelkKNd=!ab1bUfk}%mhN%fu!~&2ncP}J`N6L@&DD|4 zZ4xL8-2on5BmolB9qJlkUDcHmeRka(f?cJragR@YH2#d=HHhGy;i^_Su|;0uQC`hB z=3wdS>FpKk?OO6bhXA8N@vWs>>KMCr2vbvv_3bpaN@)s#UyB+TY^tF8Wti*0Ux4^0 za3H~g1`i@ksBj^}h7KP>j3{v;#fla$R%BxeiNQnLIQrR^ZO6YzwpRSh!zzftL(XRW z3wctbE`f9YR*{UkZKkQ38ac9Q(vjp%mNZ*#V_EX#zndH-Zow#ZD%Gl1uVT%rbz+&W zUcYvY76{zV_HEp`d*S{QG;anUaexE+Z8W%U zu!4$V{7)We5D>Ez{{UUf>4sF2f0J+x)Dta6LDj5BYc^e4pd!=+N0$a9 zjrFA21A7mw{W-Ki&M9DT?kv!?<>tA2IFwZlXk!lLm$kNY9fqt0Y z%t>DTOYsdOfqWU>6w5rVa>isTb~^w5{{I6g6<7n7u^(AxA%vDiptREyi6;iwB1M?RR-AIWu_jP&xTUt*a@~CM z&2Il7H(HNFDmM_0G{SfrDMR+y8I8sLsK_@(O35T~E@GLbmRnw>k$%@@N8fhcX*Upj zG0BG}OzD}IrXTWo^kqSjY*Qyk*jeY^n-Ovs;X`9_8K|Ix5_%AUU>PV;S!bm+5nhd1 z_+W|>rdZ*GYY7C(Vi|s@;b0jGM52k6PKqLhr54I1aswIn*^f3RvFbm}DMB1`L6WA> zk-O2M3VuN5TF{fO)@a%{r72Qms>?FlEPqZVgy%o*r9`HfX2u8Eo=xG@rnYI8^!`yn zH-UHNA>d6b=6Tjt>!(A2IvcON^HO!_SBXZnXo8JO1S+P59i}0vbbVOjUjh@%S73ty zOetL}mJ0D?^tN~!LBbI$5ONkn8N;f#k`f!o2<_@|u)o>1*{j1Yw;Ib7!yL2B2T_ZV zwAA)l61sZ2DIc3^dP`8w1Fb92ncH$(-+e^O`R>e5Lk+XNUf~Ooz6U8dQNtUuWuaeu zXeOemb#VhJ8=bI)mxpovBi4NDv~e}H@w8)!MW;yCIYwiWjHZut+Y^@o2Wuk+~jM+8w&+EfyVZm`dfWl2Xzl{=IEcL901O z=0Fc*A4wr`1Z}uVRhRTc@Qxn5@c&I67DT{5bhSZQPt-_*!C;zcgYm$1b~}xo$Ok%c z|D%07^B7X;_cMsiV1wLCO!vUUJ2!ccpzCHxkFS0p^Gzdt4=|T=G7Of}?7rWSsSyVBMSV^K7%V@?k`lokPiDFg$rijKjw&;pMoX8bs zk-LNbz)oFpZ)#73$pav+81 zhC12-4e;(-JP>19JH+7P7haEf%a;~mU+!9e;@4o~jl z5$)(20T97Tcr*hB0>J|+ItdRURI*TxbXqj4Y0Yb5Gn)&!y*HW`h;u=wvKAG#OfEDju(h#V?gXf`1G`9qtGxIatXS0))aH*@(qD z>k)%Ipu-)~;O7pt>7r>igqs%KC`UW$(IaXUBi>{K79{@EArffs9YhcaBTjmUbHL*V z%REOr>N(5n38V`D{6`(mA&(unp%d-=#~V5^On|C`7r$vo5%4Jx4}26r7-dLEqiWTw zVl}IUs_KG{T97z2ffe9r&|I{K3}tpN9dil9Ne2SUz}QnmIGvUuVnL2hQcn-)aOgnZ z$rkf*$U%Ha2Rv%QORY|4s@p^?V;k$($MT1(74c?Dvq6hU*i(Bc(ku#`Dm|Ot6Q^(R zggfd%3nrk0dYo0!Q5mF*C%_iAH~7aY*{KX0K-MyfRfuGB>)YP~7r4EhNG1vJiFO!4 zu4NtNWH?L9R2sLYa81!5a9Z79gc2{RMaNkOBL2+3w!@qV$%8238r-YwHX*}3FMHeT zUTv;7BAK+FUJ#mD=7x&7+M`~6r8`)9;C8e)bsnSwN5~VGumPVPbFqL74iK=Y2QJT>HKwp7CI%UV_@nSCE4n zIPHT#)d>!D@b{exm8Xu`B4Bv9!me{^gg*Isy;OpjD;u6;l&fszE1yxs4*@KPhVTYa zsN)^CC`5~!i(fWKLZJ2d!CU`<2cyD67uOXCBFL$XC!B-71V{uf>k$OMhDQ>(e1|l6 z@Q*%pqe1o1#R;$+k@O-&%Z{Ejr7O)W{z>=eZ10e1H^Xabt3di7TNpK|OKs{?qdL{9 zUNx&*?dn&s#YG*Sg*{uY2w5UjsYX!X7rUi*4*sR z=!*cN-Z5reydkpgg0#Ee0w1`*15R)r{+l2MKRCl1Ug?E9TzCl|2*e|9@r%E=;uw$G z#08@9je|VojQzOCrABRPmOSMu_b|yGA2z=^&wF0Ro%@_i zIR|>si*ED^68-3LggI4}?)0br4>IXc@6XVuZuP5+QR-NSPSv;W^{*ox>`&{uM6S>Z zJZ^&tVqZJk0i^Y|XNBzfYDE+3-UPI41B`IrJKrhu_P$ShY8-Be@|uwMykk8{tWp+3 zm?IS&)oOx(Zz2Z86&<7n>hF`s$ZiGT`OkkozJ*VV9rZqS#mgvw4RQQIWVH~)ZC;FX zpan`^SDBw-o=cjKeMP1U`rmtg3!YaLf&^mtDO}+dX}3D+Kg0Niu-+i9*VV~*Z^m<3 z2KOu6((bJ<5sU&K_`)B))r$WQF5uA>74hC&&!&q`@OMMs3m?H~I#+_QAA83j^Z4qr z{{~?gV>Mr^=!W}%jsgDePmORJ1SQV^pK%~$|&`=9m9#n)N zp*T|bh2QvnUhM zA}*ca34{@f-~R6@A)}~;7j}Z#ppCEM&K}A}K|td_2uI*J-*JRYas&`FvZ9Nq5eJ3IQ{>Ed@SwVEP|@JtI<^N5 zDa6sFiF?=%)38gmg`<|p5iG`{EE3)l5=0S61Yp@>*AOFOJdAk_#6kMsWbC5cJY+F4 z4gu27jhu|*Rp8-tBLfkM>J=iA7-Qow2?AYCl(-=_jwI;hV}N+cJ+jd0j0p;D1VPB- zx2#FG(9X8RiA$PE2HlB%@C=`HhY+r0mRylP!s1x?<3bEkKt>@%JW8e%q(F2E!nl=J zQKiDD{z!^ES|M&RERrIzT*UXMNh|ed1?+>St$?CTjX8YOZEw{1EW| zy(WP&B|y^VQ$EDj990+oB1~`=9n?bGYz=>XVPtsKh15lcLP}%djow^I-_#*8k_2WD z4r<6@Atuh^6hz6qp&_nl}Qo)>6Kz>meyx~{%4oMRu$-0@ffIjy<%)82!aYkZ9ata zTo0x=C~I{{A;y^Gf zpgt>7K5HWc1F1^uQc8vZl&Pr%szHP+LX2yc7-6%}$h1<7nMy^u@`o&H>$ZBUmWXP+ z!lt{vE4wO$y&i$ZyPybfOyTB`tPk6vgiz|JeUervb3?8q|2#=1zw3QD??EX^{D z$;Rwd=q$mOthe&3&E{*%`s>dEZO%dj(K_qMDs9N#E47j<(ryLM_WrCvj3dHUEtNiO z*Lv;O=4Zr8tZ9Pj%u=nPh$+UBsk8oU@}XS@y}};d>$y%X%eE|=wo(rWZNQeR=fP|e z!tC6BYr#hC+}iEgV#VR^Ek7RabKor7!s6gotAQe}&tC1x!fVhn?$bU8-&!p{N-on{ zMZIdS<{ED0TJGgeu4{rWIQEalMdZPSWupz>|M2JN?2FZr47(Kc=OUTx=s@9KtM;Z6tl zV(#LOui*+Vbdc)jmhR#TF3-*{>SDzEX0O$@uf8@1>9TI-{>rcWs;<)NFZ~wq>%wjT zUoZM*u5>7F=$>x*>hIbnuD3Sk!VYf+Yp}x>@7Ss)__}WIr7h6{3ja<7?I!T_YH$6D zYzv<&0|T(^uCUY=t_cgU{8H}In(p}$F#1L<{+jO(+wc9dEaXxv4(F^2@9+&LaY3x_ z=@xC|5-0;lN8iHm6NfO=Qt$unuLLKt-Bxk@_V5uuaQilE1tYQg4lxl2u+(O61lKUe zuCYa=?-v_w_*%vjN3Rmw@IqW{^8#EJx9iHPt_xGK`Cjkt^06EHu=R$o4?{74z^}Dx zFAO{I>W1zWzwnEEUJ{ zCxoOSga#eWoIs0?f?rNB0mm~z+wmfAZyMXOA#bxdQ*;~SGdIU{9^pgvm&eUMz=E=cQp-9aSY3J{Sq!y z+palhvQbyCHJfu#Lop}!GWjxdV1MsZ>$4|=vF<8#Wk)n*NA?w$^F}vrPE)osFEdI* zHPmV{C7U)_Uqvd%GFf*q7o#<2dv@x=by43o8~ZhE&o29ZA6-Z8wwCm6FGOL}i*dh~ zaxV*WOHx`pT*Y>u1wpp}H_<-c^(z*)Vw+KQ2bXsX3V08acn_S;PB#TP_hv6q5e}>X z6t|9&HFwX8TyGS4zZ-JvTz#9gB+KuF#z!)8gt_ke5Xc@sGJHLrUk+k!KAgFEN12! zx<%L$L>!hbm)%6#5=B~-ME1Ew|ICh>qljL*iHXdfmGmc82_Tjb3_IkE#pjxp)FqNJ zBNBkZ7wOI2=#^6q0+0BTAd;JKAyKp=dtj;^PkfuouRAFzXo})4JTA!9--p5L|6;ti zpG`=#3@!^_@=bHZ=f>}!bH%i;q>zr<@RNk7z0oA~PK!FdfG*n}RW$hBljlGMk0`gE>gEIVkfv zXsZPn+a*}XWmwl$c#k!7zxCHK8*kFTi{|af742yi@4d_4cFfy!`>^hnwHlbP5FRxf z9WfRY`86i0H!`L%KDj9^tL{TyZ9#EEMM*_nX-RW=X?t~bXI=Bh#=g&ugJVr&^DWaW zA1BubCRT^1=f>w}Cg)~n=I7@Z<`)+imY!K$S^#*ySzZQMSXrA{-WXZl{j_-0ws=yr zcv-e|Q?hbbvUXp&bzi@G-*#}{_w(-C<=xcH{p{Vt!rlGy-Tm6l{l@Lx*6rQ*o4fs+ z+rz8tx=7~tDD=KySv-_`}>E7hsVc9(7#>4hrcf1>-#^syMOq<&h6d( zU)O-5-GN_CdG%psU@- zv+s{THXjexANSWE_m&?IRv(U59)7OepRL|suHD_P-`#E8-EH07ZQtF0zq{YLyWhRL z-)9+Nfdhd(!9PINjDHA(dqJZ!Sew@uibf`tg;R&tDG$MFxja}`Fc3>396=zNKwiS1 z#rGrPbA8cpDwFY0mSjWmNG7Mn@tsqG^4AXnfe3_n3Cel$g)Y>(LycuqB?<*n*-}mA zsuhKNFV7?!E9PqS_|XxZn<^I?%%^gYMd8TiTkN(Rmdl%~S32B^@REmHYUT>uArXnZ zR4dl{!X!bjzO~kE>H18!@_v- zJQfK4I?H8*ANOgRjy*O<_F`{}b+viLemK$BFoy&qY)>-HSKetNp2vjzGzcr+&IAQl zPeKhAOBns#OT4F}02n;!$H2UhpMuyQ1ZF2%&~fCw@;<*v1-r3x$%i#kgh(mQl$0Mr~251 z^mmC%$88g@7U7e?sTa(1k)2QC>Vm0_9PY*f&=@$Wxs6P;ii^?mSzi|^N}QqcW^dld zb|Y_6jT4~t28yJCl}OE0jQxN>4X%}5;uV>Iu!gGA;I5*_px^=z8B=P zUsD2^h+ZtFwsbGgo2De|EQTWAR4%Ui72%BG)S$EBYZRNaJe<}MM2phrBT`CG_F2}q zW>|w#`fwE+r|B=6iw-K;ITRrqty`Of^zLH8BrIDm)g#+~ew2_fI?0rvT4bEn6V7g% z)3d^3ODovXXjZ8hs)fOCY(N+9#~O{BjK{8@ zl)MoOC0jjDAoaLMpTzt|5=gX;SZEiMktlz;`@piywXYF4G7(c|+#cU;&`mT`?ojyh z@tD})o@WVX7?MOlp+$GTPm|J^Xy~Lb9EQWrSA{xDp!{7I#bp%V;yK*)Vh;h~9vHGm zE+obvCE7*XPe;T9%-!;kGD2!=D?Q6sK{D6zunVX(HIpBX=V$rX9?9N=+AaGT?$la3 ze{2#_a3(;+Hw709Fa41r7 z>{iPH9_7uGu-J3(67?nNQHY%6l>59B798oYFMs1w)y_$P?=&)sAyp6|-*|sy(JS;_ z3IC7_G+1_t&G1x7f-nIULX31cQ7yU*0S*Iv1anhJ*>eb7%w)2QeQBjg4dRD+>@x8Y z44;~$q&tb=fnqp#VejP)VsD%loyP)L>su(3H@>uignz}m~bwQ4ME3FP!@O~s#z*Z*U{XI1nMJ6 zd7eWj!8@F3?zRPF@llE)-`0KN9JIj{yhZukhZng6pH>7+3+T}aG0E*q{b9y)ksU+o z^ZDEM#MCV*KaaqYv&RKe8pVMpuD&S3(k+u4o}65yJ0|nvPD2qgQqBqyDdI7GCm#p_ zi((f{yTocKxtK6rE+k6lASjGd|0=;jGMPMhyi~PnF+B7O1U-}A`yH*fMM_MNu>bGx#d-hFrV4uOVz|_D>5D>8tk+6TcCwvst zUj_;tFi<3D7_ZPVNYP*XlZV2qa`1|B3yAOvi|~nw2uO+w$%u={iHj+TOQ?xUs!K>|NJwi+%4kW+XiLlL zNXzL;%jrqW>;J+)THZiP-cVBBNJ8FNT;5bn&P+tkLQu|HK;D*5-ho%%nOokCSILt{ z#RoX#NI=a~SlwMr!&OqtSzgys*}z`S&`!h1R?Ear$IM>;or9r;ld-jn*?Tv0J5LLH zZ%YRsD@VV-uy*vdaq_Ws^09OFad7ccFbcXAJK@d$MF4EjZ|t7ovASBRTe zDDX2?5ASdfp9oK%NH5i3?tR^IjIzKy2-twuo|2Em;MA>I0+AN9j}48nR0 ze_<5WXA=9xG~ugR%7}UTxJA~a)rT4Df_aBt$mHhqc6o zw8jUw#)q^ehIS-{_GE;A$&DB(j+`!!UZ{>)Z;an=P1x;7Jm^X~>Ph+8oBFdq<76QF zWGL@+wB&fK`go@4c%kEP;nVi~!1~^g>+|=U2+{*IO>dNxk+Umx}#@5!>&d$#M z{@&5i;mOJI<>lGc)%nfM<=x%&{r&CZ;~fa}00P~EK(`>!4G47o>jroW7)0Q^httQq z{m1*|+lSFB(BL`f(@#+60qEl~sQ(o7r*9&VsLlQKH{XOhi7_dv)*p%U?3?2A&}}Xy3lj9tpP+Y_3vmFduvGER9VyhQeku9%a=~ zt)E8!bc!k7Kp<=5_(b!Xt1ghxp4UPuXW6yRG+L}Nxgjx+MQAJ)7^J0A$c(JaSLQFA zG0GB{LlCUU@m7e7bwZl5&mG~(%YK8@mwhbTNs_LGXyH#is6ORzP7~k!#1~oZY66GB?4Ld;sj>K?H$fl9K7M?90)wGi_k+SR zU-yT5UAi8C#Id&?h$cq05rnB`z7dRLR=N>F;Ig+7N)ke~8AhIBz8OwcT)G)S*StsS z*9e6H#|dR0h=@PfbrZ*-TR9<$iCsSnzw)h7h9a zjWN(ZYD{>+nggdHjorFF5$yGpHU&-8%ImJcmTB%7g`~8ujFzgm303U3L%#5t4r+vD zn_)+WW1JWA)f5E3tIMrv8l($)n4mF&NvU82ixgVUK$@W#HH7^w0+R*PVi|4`0@My$ zj!=1d3CN78-LeAGUa~@<(xcqMG64$;1)ubh756RFgd;NYhv_Nv$|pN`n$$85#65mb zrF)A8o9(YiWVa8F(Uo((i8c)}AV;LqIi%jbmImjIL6U=f_Dwx+KvJu|k-`5~7v-q|-wD~nXg7Qxz2QIS#J&)jl!ARrlvd!B6r zJSB7#?sS>;EkA5m*&s?{CS_^{>4-zx{_zmg6!J$eG}gzE*^-VaqW*@WFa7z^ENl`J z@&yHD5yR#aJA8;yL$ZiYNQFmOD7B4GG6nl>Wzm{D>8je6)T>mDVBsmzF$eo|q>+*3 z9FW6a%^NwzTAa;gHIZjcWfZq#2|KuKu3uU{4wBUhsKs=A$NPx071a#s>v=4`$af@k z{<`L&h=+%5xB zzU&W|!yh{@z`=hN`@c3Vbnr$nJ1}an5;%Bt1Vqf|eG3H-`FYbKME!Hq0t){>_pN{0 zx_|@#<2QfoVZV2>f4siHq$YkY#&ghZ6YBvhoN zROIB;uV2$pQPa}W(lIbFFfuYSF)=YSGqbR;u(GnUv9Ympym|A6i<66ooBJ&f4?i!j z;9EXnK7KKN0SN&?NkJhgL1AejVHqJ2Sz!QCIT0~=QE>$^2}N;9B}pk2DQQ(1S#>#i zO?d?^c|~mnB^^a29VKO5WmP>@HGMS=L-l8j)HRJXG>xCp0x;41jkbvvfR2f_j;Xe; zsgACxuAZr`zL}oBnZCh0L!);_#^xp_7H03PEG%rStZZ#;>}_ow?d_c%99$e6T^*g= zot!P%eE5)?o12%HpPye)P*4b5MMXu$#lU)_ICFU_Vy1C4}bhP{(1cKvn=dCWF(Fr7=8D1Yf7kWuKrtahFEfCg8%Z zQJQL^gvPzQzgBK3lISg#;0l6LIX|ZIh`rUPrX$fBgO76T!!G$F7>u@^GWZQux`F#p zi`!R9%fnh$KM{POi$rprIP_`sbku8gfuBU4qQ|>28xOdq&fjVG)$e{u8>x2K_+e@R z>|QORg@uHc!#^ZS#W-uOILGY24B7B?-gJS86uqlcUH?7)JE|96?;p!mso}EUAIvqtjyYr%k#jRm zh%?-Jx2!opJ2;aBzza2z_@DuqJ8S%Zm2VlZXoaxyUK+lj1%;oC_nLnahL+GcG^NjgUR-&1u_RCdy6 z{4EMI%21Oy}$G&BqxEG!}%JTd|T z8X^(~67oxA6l@e!TvRlCG;~5V3?g(467&}UuP|PaK7;uOFEB}8yd=eZ`3e*3)k~~b zSlF+yabDr#zQV(Mg-<|ANJReX6%`pd?Q2Q~N@^x*8WtK_Hd;D%T6zvT`Zsh8ob&*U zTnqqA+>FfJOw2q?EIiE5@UpNz!^-xSjh&C3LxAIrAP1+=8%`lkE+H;%VIE$Qw|vi% zCL}B+A}S*xDK90hBrB&Xub`ozsHv!=rKF;xtg5S`s;{bMpsH@Drf#IB0bs1IVWO@H zVEPvtnx-0>W*S=m0ZlD4P3>p20Cdc>bE-3+?d|R3aWilXv`dH${cO~Fxy+a^rd2L zsAlb3uu$*yp*4;pxfo$;qjSDL|{{#;4}TrvVoJ&zP8AcxL7|ixV^d6O(`( z0#djBeQoz(>*#pzYFH&+D7ho7?l-yUW|Vt6v%B@4WM``14N(|(M)uy)Tfb{m@`?Sb`h+t5>AEE{3lrGa%V%CM977-A9gD77`uGM4B)+jg_~`s%d8q!5T!)l_zxgM zlu&!!=!u9;$}lK+YBu?RMYl4K8DnzcrSw4X4QqI6wvO+%?4Rs85xZZK$K z7bDC){01XDAUy1s_ulk+{SO)ICLf96z1rJ~6@a1MjuXMM*p8QYUACPd!{Ki7TbaYv&w&+!1(b36cK16VlJKq`QP zPm4=Hhet^ND-U2IBw;3c#YRHL`HF&vl!A}!wJ;f_1UaQV1(iAlwZ>~24Qe_yT1Hg{ zW+f(8d1f{_R(2V-H_~soBsqDcx!=n2yp`kSljjpq;1>i?6cADr6jl@xQ4$tW78O$w zlTZUf0%;j7X<029S#4Q49RN9b0NvlnE9faG0_ZC$87L|nC@C8%s~9S)8mXuntEd^P zs+p*&Kcl8$`Wtmkvw!C=-BQysSJARi*0NO6vQpBrR?@as*0xd6u~E~t(b9jfYxv&K z*w)y@&e+u6#LV9GS*{!b-kAe9nOiuSTRK@-I$K%0*t~bMv-fmx^mcOb^>*_N_Hc~$ zvPtx|Nb@nx^fk%yH_Zw(&kV8#PV7nz_ehKiiF;P3q?p*`_=L3Nl+4uhoQ%x8tek?J z4@G(TB?X1$Ma7lHB~^u`b=hStsbyUW<-O4rU!p1oqbj~eSALDH8ceGCl3v}HSKCuj z*HK^J+R)I}*x3F|Q%BQ(qq(!GxwH8!*dG{`uiAbHf9(-@eX_3{8)Gn;IFJ8W|lQ9UmK=7#W-THa;^v zK07=)|8-`0V1DiM;?|edoi7{vgWErbc7G1}jG}$<3M8cFLhLNx&i8&>Ol|ByIKYWTgpaw6%aKK7nU{QdW5E%vk zxwraUU;S%k^;;00>#Kh)uKr07{_45@N)0K0$pRG?E}#t5IC#{!__TNgwD^Q{1jGzL zQb_!YnS_*?gp7rhoR$1F`)leqwDdgmOngi%0<7%9Y#bsSoMK!&61;p;eEc#3f^q^v z@`Az&LL!R7B1$4+%A(>b;*x5T((2MOnliFlvT}d|{N`B){)-yuD=0myffAqwfE1`0 z{X-3YOTqtdHTavCd8eWEjHdQGEgf?$9Sa>jOI>{{eM1{VqxVK8w#KG*e+q(w*|Qor z0%~Ak>11j3tOnLLuJ3JK?d;thoq&1_sKNZbeS-W0!h%C0LxFP(BVxiMVk07BBckFW zqvB$sV}QTJ*yyCVnAF6$wB)2`TauZZol}sTS6om~T3A$`TU?VKs9SYAk9zYGryjtqVs1=;X-g)Y$mU#N_Ps%);y(&}v>;Sy^9S-`d>#4%A(H`v<>^uAje3 zE}-JNxVXH$y1u@?xdwX1&kAt+i$7YlziKX^;CimO{^%wDhdS#INqAO+RX`0eIi(~s z49{R_bf^@0tp2R3x|w=h=BxDA!zuh=tOAG^~ZBV00Wc!7k@7H zmlPN;Ujw}OJ6ONBeo9R2XI|n^{RRscSnq#;jZ1}%_Y4j`6)rv%9sv~|Ar(Fm6(KPd z5eXIXD{2x_YEm*9pmw2nO-D&ZPff!>OUKB-z{11?#4PL_9M35WCnqNt7Z*1-HxCaF zFE8)gw{Q9Q`1twx1q1{H1qFqKgoK5KMMOkIMMcHL#KgtLB_t$)OG-*wT3SX%Movan zURF+CPF?}H6crVfl$4Z}l~q(!R8>{g)YR0~)pa#hjdYaF^%QLk6da7?Tufv<&7^$a zNd%aSg;gKVaJ(e_a@t(BgdW{;DbGTu07i` z4!>c~cHqu%6i9OvO?Hw>aFUO6R*7-ejC9eBa4`&XF%5Aw4|25%bh8a`v-fv%@^g3b zb$9b|_we@c^7iod_VD%c^z-rz@bU`u@(u>@_73s(3Gwj__4Nz&^9%D22oDI12n>o0 z4vq>9jSdTsiHHPjLVRp&VnRZ4QZf*`0F_D>5VZUivHUGz`KO5GcfwLrQ&U?D#4L67 z_4N%64ULVBO-)VBK;+WW(%RbE*4EbE-VQ`Bot>RsU0okPe*E<5Q(s^I=Pv^T178Qf z4u2i`HZ(jk{B88x$k_94KQ=KoHaRvvIX*EpF*!XsH8TY;Jv$9BGxwXBnfaO7XBL68 z^42!Cw|9Uwe)Qw`1PD|vfR+6_I{C*C#{Xv(^1Su`5v)AdBL984Kd<_~mnH(Pa4WdL zIE8^wFftjQ7o1}L=TI!@b`J*%Y~d(e!-snIfIN-}czn_{*!NIr!IX*t+xdL4K)o5?Yi-*h`xrQl0uy%~1@vsl;G?9rlOVWhaG*u+>8m-(x zh?@72EW9yv=?=56KUssyKeaXP7FC#8H?*}oZBGe2nQB3dvcI!?Rga>RH0ZKj(+-P= z_kC%noK6~%h}FM}Gr_=Fk>vrZu=Y?Pj0G!8B@lPlJ)MPsZAwoeDO|L#`tgiSWP;-i zro&yScT2Ls_yiK-mqqwhFsOl-0tNxF2>*yOAR(ckfH(sh1_lNe&=5E{czAdO1O!Ax zL?k36WMpI%6ckicR5Ua+baZqK42%~qUSMKkzI^!-3kwSy8yg1)2NxF?4-XF?AD@7L zfRK=oh=_=on3#lwc6hVQ+;lYGaA^0(&cnKl6DIvINAvl?#IN71tIbqnjVc2wKJDF}ZiKLYPVG;ww;d1gFSdIDW)5>rYtYf=hlLdx5?RN>f6>F6Bgs636xLcOR$ z!{}nu=o0go66@Gf`}k6q#8U6%(xB9m@bsdXtit5n{Otc1dePn84M;{$PY<9Py}iAC zfN}uo?dQ*5zC7#4=;+Aw%*5j2%-Y(*=GOAg&g%aD#^KTCkK^s1C*M!ccF)fDE-nu) zuMV!Rx36zjZf@sp@5b)#2k!4b-ajho0`j=kZ)Mz!oU+eL|pQYDk4?u%e+j>2k^*IvjvyCT?;(C)Ej<6n*Yn5@c zJ*jdAek$@NdlHRu3@vpn-e4L{pZ#{o3(A%NCeI-@D$I8rwxY%!kf|yZtr4=K5rO4O z`UT>*DiOv*jIru+*tZLxQpe^=VH13w!7wTNUR10rbD@SM1*yA;B6ND*Pa!rqv4#|{%e0_S2;^H+2oYWSBvxt( zVius0wU(~vmk;gE8vQB%UPJsP5J1ZY9^nN7;!7lC9Ap$cVAKE=nAQ9>XYjX{&F^u| ze++H@&n+9E+W6hId5Qg+5TBBakcyg+l9`Z_lZcX!m`eB+wFD`(3>l3AIgK&}t=el^ zO-ed#DtaAidOcbOeL6+}J$e8p9R_A?MrKVW7IkJ;RTeg7R(1t;4p|OPDNZgiZXO}t zx4-;^kf5Njppb}=u&A(zn23nDsHiyLC?tM4ivKNvP}R~?);3YpwUpO;FRSk;ZQvqd z>?>jxDr6BQU=`1Aoy={W!DpKez;Bx_XqzT%n<8eLDD^&8-X=oTCQ#Sf%h1Nd%+}S~ z4(QrAI668yIXO8yJG;2JxVpN!{Z2YP{w3@Dd)WE^u4VIQ&*oRZrlO>ot@o)tLX0T0gOe@FNpy30T745!LMJx4h;&=_n&0An@^8PQj?(TsBN?>|&@9zHa?*91h{^aid3>eV- zHO%>ko49|t{gZ!>HGh8sR1d(QDbT6;-K+U`TLF9mn2l$F82pte#AX9?2JkclVYP;R z!AOKsY;gAZqAm!GdQqhl`8_fCg1~$xFlUfRY5aB~LAszEjmel$}t* z{Ssc-6>;bTaTw?~Bw-+mNDylRh$jWaop{9(y~Z3l{WfMoA%0Xf;hRRxpmt!lhG&bi zYonM$9lJ$^*tevZ7;fWnv6q zWsDNyNmEnEF)%1HSFUtaZ*(_q^)l}A)*TF#TM5+u8Tjrp!18CX^+u%KRD$hrf1_xZgKKD!yKyO$3r`n3X(xR4}+=isYw%~vfkEl794{P=n2S!zA7Iim{E%$D1 z4_@65J_GkbLk|%Hcd-Na$wPM;Lw8x<9`eQ>il#uNOOKVCkB#dOovRN$tB>7VkDa@Z zT?dc72aiMhcawY9b35ltJ148(kJh*MH@0`zx1IrRp80jZxVpZuv^+n%xG=P^)Hk;> zFts)O<;QT%)llZ$aN@&A!oyg~<5bqeV)5f<^}|-({Z8}ae%r%Q&%@ck!}Yhv`_adT zG0@}Wut+U>>m&E@{h<;l(E`R(P!_2uQw<>mDikjPzMUfouG0Iq>n#WVN7E6}z$zq>zreB6BmZ9YEEKRiy{J&xZ#PG3GQUp%Z_J|0|y?ttl` z=j(Z`{^IxB|ML2954t-6-5!A+4nUwi5a_Sr>wg_00*0@j`>~k|;s+OrdAYuH^;p>?SjRwo$p@ze> zDuccV!cs9YWR3wiB1lEykc>LJt)Xn`mgnK?0gvzQ%o=zmki=#1DJ`|SQY;!eLT=%~ zNF^a;(m671_1}lnG>=y-nCCOt{h&$jWzzD>iP%9iX=tY?P2Xw>t4r%RH;V9RF%MoS zk&PaPW~=Rm%XYS&ZVe{VVz+9(6CJaXLJeYgBw?3-(ox=Oc}*ZPHyjD7;NpqHNDH*K z>=f0oghfHw!qTH=u9ZUK-8es71BQrvQ%5e@(4Aq?!^Wwoz|NLL)*r=jwJqSG#E@lQ zI>{iDkh6V8lpkcu5)$iKU;jNsbPIixECPcik>M;RQ%fQ^jbo^S?26Pck0$mxe#MFh z@3X%v)t4p+L?~HvOgMCe{s08Ht}{s3;v<1i0i|&9eMl$J%;Rz8-)~T91lO5_VFTa_ z5l|qSOh3JO>fG#4AtX@R&UV`=+s&aUb%kOuvOij2MBD2|N(iH;jO zW(j&jJDTOJ>}9azsvD-D=qvIF6#Jt+Aufo#Nns?5usRqnP$twTn%8!lH>lJ+jecLS zcpD7{`O6Dm$Dy&#gUg1wtFSjjwQm0fN3Z zk|XoR{5Df=)=928g8NnKXjEKqv~h@`M{a06ylWc`pbQaWUIlnAySM{1&c@uTg)qwM zo6VP^HumN!>XGBl+gSbd9a>M0Tx-=fqi(Vd@E+qfZ7cMB_y&b?m4xiaGH9zd?HsiN z1$R_Vf)XfB&}AR4kMDm*@QfPaO}-W46|SOf)pd=Ci6&_%(X}kF8c{Bg!qeeUj-n!tVGj$ zOe%bn&|oS1^+6c*9}yN$LO7RE!5v!(@^PT4K~l_8!)_A&qwJN~B{9Ig zcp6iiPDXS+IKX#jPwVkeKmsc{DAY|J7a~(giv3k0grq(`E~=3H>1I$;>ntHn#F&Eh z>sL7smWa%ULMm}dsUN}hNu@GHG>R@ms_kdVwf)Mp#*)KYi!8hioka|;U!^0Q>r#7= zikVoqDBt0nrwz&!v!s6gX2yMs28vZX>~ z4&%Yx7r^u+l?bJjqQ6vQKF$agBV##mcEoft!DFd}_`%n>go{F|;6h2|p~(~kha!gP zGMRn2sr1E*VzWwWIoBa&x`W0N0c1;bUz+KByqA6=Z_AZZ@jCTcF3S|Mjg$(cX3Dv( z%GE~7)f9tfsyr?$9`b)-h%*x$WOAqpUYeLfHO}y*Y zCJF|J)ZyiwUbTvsj@3>j(z>fGP3`m948rxpx<42SJJykH)kIEx$d5ROXG9hff96b{qgZFoFA;rQu$(P4_t_?;)_nbCzhKo6dxvk zc<$wN0*zj{YEdQ{!*CSOn?4ehx-fB>O^nQ&ekwkDiU7q(9;4PybRXs@Xu7Q@rJFXa zW20zrIwWa0W85c&(6~^UZBqQ(K?%9~gw$`_uW8fqhWF~2j#`b6zqAf5bg`tY945}8 z)c3NqousVMMoDdxdYVWBYk%(B4oAoBh+RxW)>c-eluUYz@hD^TxsERVx%(G}(=!36 zXz>eB-#9(``G;X`L}?Ra2gjJkLQ2_vDP=PJ6!SV64Szi1$E_ELyackZRk?zkdSMaS zpvs0}Q$^p*jaBn8R2oOGq^@g_1Yt>5N1B_9ylCbMEp~Pz1dj^RxSJo8YcBNPfJ~ecffNuhL6i_4YGL5IKtblks8>3_GONq)lL04Gbn&r~uyTfd{1PEY=wP z7goOaDSqxNFTrn-LD0H}@s8;81SQoJn7uz!Iyxt586~aC6aul1a`k6LTLmx~4$>8} zNb&~#Lt)Mw>k7SE@cHko6RR)QVD7f02b{lFSuK$@<9E$wzZuFLT}~6e`)+{hnc2mc z;CxgJW4DeB-C22Vxpp|qNPRb2_4X7mseMyN{(bAY-euRhZmaxvveTQm$^ymZYJbEpr;E+!1jm6WCz9L8SAFq5w9&ELZOXPc0Y~Zw zj3^HsFXS&PhItP3IBo~;wLMoO^$s)B-yV8vyW`1%ZWtK5MQ2|H4elNBs(VS=ZJG-zD z&*p2-&Jee;Wap1qUN3I6hpy=d#VsDjVN!)S?&Dw}{J_%Dz_!6$QgZ!C_Rt>D{9e|g zfVJW&iF+e$n|`ok^Bvc!@WW#=fUAN=;1u9n3AMbyhIAj-+TZn3+4FJPr9U44-(>R5 zIrCacK~UMFR?~2WNn?Pd41N-D9D5hS-w5$@!;jKF07l*4O(FoQE>t4bKQPs~d)A7$ zR3z9n48#+L;RAs{eF9Vc5`j7ne0^N2KmZ~pO*{7l)eB7@%=X}*?5XI#^+&eL~p zKx}fx6wd`qv{8t2p~;j*37RvFT|so_MX^H07Q!U^ar!@|xmxYVHoN-oB&T?r$GwG5 zZKMoPFp2Xj#qgO#6*b`WKD)6NV7%&0kx^Wms0l;d2y9prIFo9PF!k3kzrq_Z)kbwK< zCV;KOfP=v?z#*sGf%%aHLXd#jqb1gzq(du!?|>m!!lB&tL~P}v+Mt0uqy%<@V9;5G zlOQItC#cXqr&+PJxbN>mL_-j78vC)m^Q(Rt^r99;6DLRU7K*dhP7ODCbu!2N78N)` zisQ^ndJtS3C%PcOyC4_x?iwaME(L8qMHV)1=e3~&wLj#(zXrBHJhfZ9IK~w_G^Ifr zY8&uq!?0>5C>(|8doXB~$#IwzO1n2wX5EkvIt64DbZ5h?`W1*C#MD zd+@l_X+(B#hYAQ}ez3=J&}1ZqOdfg^7U^H%!9r`(tM=0EPY~0=z(P)nHpeqy1yHKW zA{h1qSTw;3Z;RFiB0tB$l(nWq^l;aO!M|9?Z&U~jF^J?y%YgHPOI(Km!JQO?DWK$} zN95dQ*sHk{TB3f}ifo7&=&9%W~@`L*1 zc(=f(!37-uk0v-*#>E7?+2GDb$*|)XLSR4%Q5Ngt8i0Xd2+fhIR*caRqK!ophGSW# zR+87#f<2rDVONWiYXA`}07Y$yK!KMoVh3RX2E!HsdsvGgUgPdt3B%>7_|1L1>~o4%RPEb*CvR%Al5k8D+sub2}ZHpL@0lRpn@1wxXOy%P&iFvmjwez zQUAyi;~fw112kwVHfX;E_*d;HaT2gsm4J>HI@gSmRp9<4`ZD z)7Q}uh?$^bs9HDI(RCa z0YudPr`Y|feDeZ~w%#CZ6lDW&rS*dRjzTg&2plH3!=`pd>a4DX2((PI*|vJa_J*jT z47@lPxbXm667cQufcC@m3inQic^J63dM3mM7U>4B#X6f4Sp6Xw%FJxyusDPbc=q+i zS!~PA$i^aAGeK-fZnW;rJ_w##_s8VmY4hm$L3dCc_eb4QZ)QI#gUpYsy#u>9sQY2w z5Q2_QQB6AqkZwo53{E~dCov$F76hFZAI{1e7&k=RIC*CSbvpC z_cSct-s;a`)&f=0ZN2;ly|q!T!IDt!UF_)ph_qnZ<=`)C z;e0kC(tC>6{orBk!1%4wG41O6!oj9VvcPL$?GeCsa*L{QB5UKo(c==B-6Q>IO5dN1 zS|NP?A(Q2(7J+8(^8Q9ka1P&A#wL`GRi!jaj`|}+e9XlzL=Xv@|8lv*pg`!d4bl&4 z6j?NgIV@rpo9Uf$mRbk|1p9K2P^XH}56hq)d(A|<^p;xJN=0XbWE==dnlwr#h@o#` z5r{!iv?z_hG59@Dls#$04+Sz(y)UifciLfI*};)zq?I8C37xcl|K4ZG%~7`yG42PC zLjnek21|{b*}aiY)M3V6N|H_jRw0Qz#RcA$UUcxS-R%JX^r61iE!?+bBsQK(z(b?I;>?Fj1f$crpX<8MoSR%L$8bEgr1h$E7g54vDL(CM$Or zzsss(e}TGR)QX$`R7F9_dk5}~RzQx_#<^VGPoHq;=SRMtuj~<5-~=!(`ZG?yjT;t%wD0vUj70Ouy8HXUxV1ddCR_I+%lq%eoB^;&U{Zb2QX_^lcRYDY#oa&*uW@{>k9DHsO}^u8z2hC9;~hB(9dY#NfnzL?YH5+y*|F*=-_NVjJze{s zcRoKayMKb-?<|0RLj2Tx8D9dQGZ;zYOC|nGmDp3;@e2)k0-xR!)wN5#r%Q2P zFirlev<*rF-z)PES9J1M0v}Vmp{y;5uO0ZWU-)8-*daxezYv<&Wgh8-3gh`6u*^C$Llbb%B7VBQE3V%5|X-WI_aj z7mAzxVNXyx{b`C@VljkoeV}lawj~lMWOGE)m5ThF>2-Vfsg{_nJ-aZIkauv@Q=8J3X(9kUO0FR!kgW zU1hb_8^ner@r%Q7&YTWrs`NkRs3dfauXOps66-yZPA!pLug$7^n3 zg5;ZOeWZ{1(v9vJ1u$ffY(!=+@GGhtf;wzzue6fvIo7y&YlX`&lfpj1g_74xkcnp4 z2~(t+)Jsx+fEpPnjq9-B1h*iMXQVrCKo@v7qn%T{ED>Y0vfL}_`hrvu<{`&1z9%obaY6T& zz)_tqZP+wTiK`kl-+c(?SR68^kP`F81 zXp^}Q&{R&@Irq!Pgx*0Ei)j1<7GJa3V6f(k1kvZ&6|!$-8uzLYVLA!}4eNt8*e5m8 zHjHS@I&50ju4Hx?-mK0p-F^GsDwfVE^I|kNWOJZn5$i`*b5TfWZ}q8gqTNy6(-B8?o)iWwFu~q0-i39# zG@i)fdhH@Va&oEiZZK9gU?WkHP6xudd;1^)?Tko?0@Z}pA>aRK+2x~Z`v}YLbt-P< z>P3zJ@y0$$@7-I2Vd^oecYNv0@8rKi$MHMnoZ8%t=P15W%RlJnuWs|VfUEu71yX?R zS!QP<@6aA#!GU2;PX@ht)hDJgT{Y zxI)f%nO*3J{dMi$W2Zr6VM8IY6nl!vf&T{je*rr{#J{Q+J7nOo;KwI${AqUh%LE`Y zktZjvV^1B0gk#%eI7d3xaVuW3 zBg|+>vy%VDm@tO{$FYa4|dYwmqY=AgMc^8lOTe@r)BhESQi-@D6uF?HQYpk zrrgOGVWLWGa?pe8fPx2)dCQofF?qoxAqbe~1P`9Vge9D1@}#%RGqTZyCUv9ou$jH) z%~BN7%Vbo?ccNSaDc1l|od6T06g6Y=N-E7}iZppCeFJ<*-838& zbqN|vNXF>FG_WqsUQS!$%bcEq4Ngex^CbFH%2t*`M8#}oHG46mQVXdf5u(rh8OV)L zRbV@;Q%kN`M1T2lidvQTVLq`5B{V|AZv2*}DcuhPrzT|;nD2b1u- z4R(n{7wiN>-1M4(Jzjf7JYw(3*Tg42ac61u+5EQH#U_E^Ix4}i{!6%|8DLyPjRBlj z0XwgyEr}*C4ZKYQzX1xogu))!FwFuxIKt7p9$^uS(Tn=Bc}flkE7-6F3Wrq4BnESs z$%$ezmzk(4z7&gJyk<6YB*yC~hS}tt1~shVx5N!_MQIsAC{)uQX_(%DU1Cz3hPam` z1+rbAEIpGV*TUH|tV%a?mf5v^l7o{eo=Ra*zIqI^A;(R7Hi#6>>xXI1w>)fJCQ2jnL2{bcJYpxuIllAJoPF1K5H1J0vIdcq zWB9`6M@Raka=vtP`ywzsSI|}95Ok^=2@XnW1|ztWbgp-OLre$zr|ppt`bNDaQ?Gj3 z9dz}jB!d`R_j=srKAy1G9mVz_Ml#0tIF}6K4IEc{;2m>-Hw?Wg$zX&pWQ%pb*0A2eb6G zA9m_v-}~POKgG8{ew=qd{O3o1`X@$y_Fvrm>W6>)R z1=PS2G{OCYz*T{b5CoYRf(jNC!E+OY1uNfr5DWt-J#h!#o;8Gql4yq&GBdm2_cDClfmsBB6kw}S3<|iz#a&dAQpA!|gpu1&MR6G%ZE;1xum=}{#dS!5hH8mR zXhvTA310|?M(Dia!^UjX#%<)rZuG`)1jle(J>L_@ax}+tJV$E`247&IWsJsWe8-_E zg%R*X3&@F800Mi&M>l$jQV0R4{&|UhAOcTJ0e)lxg5<|_5J*qV0G@IQfwV^^IDtZV z2~cQAp0I+6>YyioS6v>e!$&xh5lSIjsRLPZO$(D4k>uw(H84m`AE)f~<52E3n6PILM1^35#?{n7~Jh6pFZXn_virqQuL*)XTl( z%f9r>zXZ&{6wJXS%)&Ixo3zWzt1qBv%6JqCtZWG@V9R?1f`k;ys0_%hbcwHQ3C@g6 zeXPu!5JO@X{Rt6Yb9Oo0<11I*M)gJe6;bk2s9%(uKsw){#^ z(8zU2O||SqjRXZx^nh^yh4y?;fV4{LOaZEd%!&zykL1n$oXT~W&k@KTfoxBh5J)$Q&(0*vh@{G!SWdOPIwnZV z)LaEn?9S;~&G?i~w~SB=4NV%AONxO_0@cwS<RsWIi=G&wbR2S((f_SBbA9HMT-0UPUbvID6PcMaAmGx0B*=?wQ;LyKhGf`@WY`p#*m#}SEtOXwXit3I%7LKN zad23Mu!4egSDz(_QgGRyb=MSd*>}}YpPg5R&Dn$v0hUcz47JyYm0F^;*pB7euJzil z1=|4q*wFyj!Jve4RfDuugX2uu6hK#c?8Kv`*G{y6cBR>UjoB3NfOI7YguU5`?N^3i z(t*|4LJ(Mjz(<$G$LhRJq77R3j9Q2!i1m!ya1e-qMY*um+|A|O&h=cP9NPr374%pG z{(|&Globe-EjY||p^Dww&xPICmEGB;-E0V5!6;kOxP;veo|A1`gOgkr3SNu5*xEJT z<3--&Rn^<|i`+#Ia;4Q0IL5FmUgfpk>&4#e6;S5=khuGf(7QqB=;0m_j3kKT`mhaK$%_ACba z%*a5n%_yE31Fi}xK0qrDNmAGW4ln~DhygtaN-qZEHr{|U7UL#v0yyBwPB3G^q|`ai z0y~yuMmEVz*3CBF1CX?WN#4mgo@2vo;}L*ll=S4k#N&T(RcXWwKL$x7Z~;Te22|*Q z2{`00&W2Rz0S?gQMjqlvj$~2(%QjABk>uo)TxQ%{mgCqY1r5joV=l>O z?$!e}j4^>_rjX@S@Bt1;his4qA#j0G&W2bB0uFeB9EgK<4&yRke1nOAZd#(>1@d86xirv9)gV~1rS){IQHn0CIz^S=~MuM zJ#b^14oRW@XmN;RE}+M19?UNl>YDClAo%E?PS0s(>5YzRoMmU6Y-J-2=U9G8U)JSW zegSrN=Nhnpb9QGJ*n>^*0T(cbE0}=`s01j0WP%>(fX)U)h6OFy0T(!fK@bHU*Z@n& zf|!nIiH2y7p5#>~WoVXWj&5n8PUU9)%b(WfWhRAGercH&<^Ip+XrG>JQkZ6&{^W|5 z>6Qk`OP=aa7U~nQ<4T_DROSMuP60D+W5otcp|)euwrr5R?PuO?ko;-b=4zk(>ZJ^8 zr;z1?F6(SCg0mLMwnhYy?1UUJ!o({09~Olif&Z2iXQ&{k};gl3&K$^JH7H3-U-PHOCy z?UE$s;GXK)=53jtaG7@MkYsRAba0{Mx0erHCOf`sPrF9zfp&;UW$Yk@9r^#)_~K4Vny{(%|b0I7EG5uRv@9s)>4aHJ;4 zlD=qAp5y`N?*_+9p|0j;p6M+&aF|AEH*RbX_won7Y2%)36nAM)gyg6O$*5NCkR)+9 z2gwf)aiN@OI=^#}eC8}Ca5^VR7VmSHd~pSd@yi(JTn5QmC;|>Jhw9Gpz3v0Mw98(| z23U9k9hiZ-ve&OZl~7sJrzMZY`Awx zr*2mlVOTeA5a5Exo^q0`bzooc|K3Yt=49U{aL{ICa2IV*&TutPc4cpKkfeBP5A8V@ zcRJsA4xi&|-}8_6$!3n`-_Gz8@9-)Y_i>l_m^AlqM)!1oNl?gv4H#td4#{|DWO9yy zH^77~XajMmf;XUqEZBi}4}~1yfHN3^75H~vc4ru1gFfg43{V74ICXGo5d9dy zfOF{gD98aBxBw{FdN7u0P=^Ci$YL4Lfa^B<5QcBPKW)kuX~)iZhG*@+Wa$kLaA9oA$&FNAaMQS!hRmoCa#UH-EpZds?4l?T_g+o^S}?@LA`{&5sVx z7w6%}|NL)a`d<+HUkcC%h-?A}5-e!&Ai{(S7cy+<@FB#A5+_ouXz?P(j2bs`?C9|$ z$dDpOk}PS`p`?E*SF&vB@+HiFV`kE%?CJ9-(4azx5-n=(+>-H_&xN?m`1#9;%-n@GE z^6l&QFW{?U%Mu=o)~4FIiWf6(?D+A}x`8KCu59@-=FFNm+Z|l^bIZdu6GJX-`ZVg) zJ|%N*?fNzB*s^ESmg*TaZqYPJtM2XlH}IvbYZEVS{5bOD%Dvvk{aN?T;L@j4r#^f+ z_UziXbMJ1vxpTtM(^fBU{=DPs-q*8l@BTgfli9ICh?Y95mK zB8)N07@~zHvXmi<{yFOSn2R&|_#==(YFDF;D&e>zl1aYRqmWHH`6QH<5n1GsN?Lhk zQcOm%4sH=ZQ6M!o@%~%VVraNX=R;x z3OXpEGwx|%pMNTT_ckC`YW)(3Og*Z#Tt7ovdJpDEVIoz`z*B4N;@sJ z)mnQkw%KaCEw|lz`z^TPipy;ypVo)suIcV4iMs8&`)<4D$~!N;_1b$cz8Bt_8@llN z3mm)u3Oq3W!ARzNFv1BdyfDN2f|hH+5wjJr#1&h7v2+Y;yfMced)$)4C+e0W#w9C7 zvB@c`ymDI~yZkcDF&h|iVGy%?Gs-vXyfe={$^0|WLCZ|Dc>VT#bWS-Vy)@Gj2fdTf zQA>R=(V!aLw9ZOny*1bA9{dwoP)|KJ*`HQ@?#Nx^tTx+ii~2R#F^OF^-F1U@wqaMh zeX-Vi`~A0$aId4%+%FYwH{ywdjIhmstJ}BZkxQOJ|tkb^!t z>AzJ@cuuJMEVfUov+i?>uBW~^?fNqQdFkzno;&ZoGj=-EM3D?s>_^ppe0ZJ1)VS|{ z?%w`9^wBpZIP8~`oS5u`XRkd@*vC#d^5K7`JchVSf9dn*tG_<+`L;g1QSe(Izxef+ zMZQ_)v+w-={rlfOzAhKMxyWyIu(KZm7k4n`{f|)%eBl3p_db@y&2saz-t}N|zyu}` zf1%MI1OXMo5vosuse2u1Qh1;5#qEUJ%Nz&A=fN9+kc2x*pAB>Px6>6cgKuWjv$W zz^KMGszi)NL}PWd2*){=jE#2mq8rIb#}4hWk3730AgOq}wE2-iax5ew;T6b5*8VS# zgiPeRASp?UF|v}!bEIerI7xtQvXcX|Bq%k9$-`*Uld37DDO35UP_mM9gUlrNS}9AI ziE);;yd^F_7Ry}nvSXt}OZR#yOsWmDn8rM&%yx;)WynPM zsm(CHl9buprZ#H{&T*1+ir*~fIGu@@bE1=(;%p~8Jk6q-A=qfLJ*RC4Nc{-`nRX;F<@ zyPztysnSfUQ)Ot4zOsy(bg$dQH(hjLyJ?dA(n$fL_6>(n0%>>IzOSQ5!p=50< z+0F`@3L*@H7L2G}U6NO9&W?f`wB0@0t__-8KZ6wa z&5pL-Gu&!f+Y->)HioG?TW~pB)V=yPY{|W?aWAMkm^F8>&~4;Y@yXhvnCOZ0>#Jc8 z=fdf#MRwZ@@B5^fS?oFXc$d440bz^Y*g7`7+%v5ACfHcv{r0;4UyQE>mD}67RjH<8#`{I|l>uPm*K1!350JATj`5Kjtm8{&m!R9_FJJGP z-Z@RXMv-8kEIzZ{lv zfJ^LRJJS}wbPls#sn=dPPm{7~d-6~y?Byv7`ObFkaZrA{U=`}Q&TQVWgMo%(A75|5 zD*kJu*StdVs+rM#CiICrd}SE#Ijxqa@}ptM8#F%}t%Cj@G^)qfWR9Aaehpsqh(kSN zV0&4;#Xh!_CoN+D%392WBQuhdJz!D;H^!?5bDLRhZ7f$e*JVp|kG0)dOS>7)ldiRt zf1GV}>lSXT#vHDf9AUytILdlPGl_?NXFU5b$LHp9zF|%2aEou#>HaK|%beO$tDDW~ zzALvQo@sAudfNTfcAEVv?N(>|;Vq{1qHWDv0n1I<)Q0Z7%TjOMs=DRmeRIFz>^dj9 zJj6*}Z_M?bz|rdb5Dz)c`UBGQd zcGSxZWr)We@KqnVq*04-O#dC*MoqbMeQl#||5~J14IrTRj#bKI&gy4}w`yNZ^kuhQ zpO6Rr*C(HO&#wNTm$w(^HE-0|YtZVc{buG8TxEtU_4Fl|Jfh|N`gt{5_Ej}`kxI|$ zg_B&lga>%>Vd{J1Z*2I~zdpm15AokO-}zY%`NelSee5gD`s6=<0P9Lv@hUd&SKln{ zB|3ie=ga)`zrVS(Pxp6=d)(9B@ZCKg*ZCCwEsXsYAOd1k!qlDLLh@U`tIP3C>IlzF^dt;0fN^SFs=r z;>H2qAPLIg@+F`R>R@5yAP`<455nLN4k0%op%Q9L5xyW&sZw>I5)@7$mMr1D9HAAu zgb-%o{WW0=Vj&l<#1@9(`FSA;f*~1>AsJ$y8IB+thM^j+A@#Lk2)-c~#-SX_9vxnw z9cCdO=HcG$p#}D#75*V0Qk)=CAR#WHAs%9`C87_0pd$w1BvxX(S)y|+q7pVDCO#M> zBA_NBAt!d?b$wz1hT;&8A}QXMDdHb00--9dqGYw={k@_N#-c336)n==5vCO^=3*}H zBKq~BFWw+70;5_96KdmFFk|Ya z;4V?4GxFceeOx%H4j#+FA3XBgzyY8>vEyk4mv8ms zJ(7(#3Rn1P+CgfeBzm1bIwP`)-47`k#-(FIGLt}l4+}BgmpLCrGT4w6UPE%sGcucj zdE`ij-mw{;$d#lB8X7Dino#^&Nao$s5!|fpq&Zg8Mgky?QQbV=T#&UKP!`=meuE&) zK^(+E12n(`G-Vwm0_>e+&OP1O1>iGIT{ga?HD%;7HAW%Gck?(@#d6 z*8NLDL_Sr=ey4fto=A)^T;^mT}zo6%J;e*Vw6LXTl&l=w)BFCSN{49t5W9 zl@?C&V`9D>h*cZVof_q3;F*D)JVIs%JtR4fBg@TNY2xO(rRAlC9z3;W3Hepccf^d$u3fq1sWQa-?I z3WB1sll!eFSN=k1x!u^(ogBR(US<|0QR*EuIv#-WM;Mk0LYUJimy`l*l_m~x_~?geUeDjDX1r0}HO37V%AN?oL7s;2hRjRFuK zdLK;Ej&|;49yrH0C@NH5C?gr9{wpuuW zUp@dIkOjgzDN&6p!5X2#icgcut4fqBg~F;!aID5&lfW8Z#X8}|BF{HGt4lm9ygp@Y z!bBqMr5udY$jV;He&NX;55v0b%06Wrd_$8uDWkrGqCNo4QmmT-<1yxJp*DcW{=;6z zfl3U5t3IV1D6C3M=~E7^Hr1@=-Rv3WZ0&IDOGNETd_x{Itj9)d13c_DdF|eTts9E| zE$x)6)b=cQwnQRuEL67ZO0aFRb}iD%q&LOm(w?l@;_cnG1Rua_;HreGf)m^(o!r(T z-EL0ewnXNltITSy)k^N*N@it&PGI{hAzIm5fu_p?QTuE zLV!<1El)HqUqZk)>;&K*jqKjbWRmXXY7W^d?n>+}g<@^0#w+L+Yvq<|@+OYfHUNiu zZr)01!@4d@H0{$?WAWO{@oHn~8f?s(tKtem)i%J@!i2g)>-3&#^$zRxCXTv30M@qb z?{ck6L~k7&E%hdCE~c-+HthhjM9``#*0zMJ0BgaEMl_41)hcKphYQA*?PT_`w|1u@?(27#pt`U$7i2PNHrn zB0psjw*)%yfgC7m4BxSdH1Z=mX&gxMHoEb=!10?#C@2=P=BRNZ_vHt_1UfV=cxo|W zoN^*VfGUTN6nh&O;wa97awyAz|Nd?rys{Z5@}Yi4FC+3V+fOX#+Wst$=v}JZgKpku z-m16~Yi%VnDQ0hl=7Areu}YwF)W$(BxA0+Vv(IvKYc{b&%A+RB3n#~E&oR+g865Y~ zn>2q)YRVzo(l1$LFI4gYtfGVa{%-KH6FxiaKKFCNUg@C5OfjR{F>fje&htgNGxI6) zCpt74vTXz0@JpC+DZhmG%4~3HG)A8-KA-D5Q*{2Fv$>=*JTr9i8QTLQSlx-HCx7K} zd0I`{G)vy8f9|xf!lXr$G)!nP`;r9~55hMI^}1?VQNwim)K^Tq3 z;CMKQSMAWQ!~FiYZddqj${c~K_8YUdIvbgl2Bq3%*=3pf6Kb1u({x}m#-uy>5K()cFZI6T)nUo|GZ z{*sl~`(VxVB|>@_@9-GgGVNSD(VF{8)Il7m z@(|lFOt>oe(!0IGYe;X7tos)ghpW454!j4jUt%*$xGGR1{8K)F!mk9w^Id^ciL;Yp zwI}bvf9$g={5PQTzpn(xvN2|ZJjF9RI$wMzX8h%Dyxz7fxKHh$lf}J1Wz7Er)gJrF ztGghxJ0Tam!7nOdq;-V~0y=yHA@G4VH+)PGeI6J+(kp#mGClGH{B;fdCjxhOn=vbQMi2~Kfs|MMLO)k`0u~?*8w7s2|)Z4IFMjLg9i~NRJf2~ zLx&F`MwB>_VnvG$C0%ql{)S^mj~xH46FHJ(Ns}j0rc}9-WlNVYVaAj>lV(kuCMDg} zxszv4pFe>H6*`pY%LYdwxE%E0Xj7+88`OD;t_~fmSFvUdRFtbqjbFir6+4z}S+fo^ zo*np+?U;~V;l`Camu}6Rb@ArayO(cYqH>y|V>#$)oIHZieH+AQ&eJ(9(PiBuC{a3f z=mz=8>c=lowVy$U7CoBu!L*;-etg@Rb!*pq@4l8jn|5v6D*ZHltTGYPqYwC?90b8B zXO-!yTCFn(B|1D<5#5$qntFBX*RdBotv0pC)zZO-7cVmQc=PAcr+4|ycLU}r<1~ek zNaeT2fmQCgnkSw9N_CpA>Q6nBvMbQQ0}-rfyR*9M=(_*Hu#;G{>OGc_3(&$DsjSjU&}uB}Mz@0OGRPji3{%V?*=yfAACnLO4&pr91C`+=od~nP`)dQ2zLlIT4H>V)*;~SAY%mb!Gnv%oBs|0~b zPOJKuvd>RJ4RxSD#|kvjQ^zwD)m2%QEs-JRi(|-4)AVCWk=_X;x{>4~i4Z^hXp)W) zUE0r6JxBf$Rarhs70c9Cp?yu&XsNAsu5aRl%Bdwcr9;I&tlD**Y#SKqoUE);(Ix$? zI`%A-CQ5eMd8=$TtY@vw7q4mc?bqL)1|b)!N7sqWs#}xWN+fbeN(5ax)QO2*I@~oY zC5hyvS7R9M1qSL3cwrS+1p^jRlJl=#!&7;^P=^J%gc2d(hfhH^Cj2|NV=(DR! zTBD_89um*`h9s7x=MHvB9nifEd#u4P7IdA}zEgd5ui!2GakL}1XmZTmh8y?Y zPo)|uf>)}eSCqP@1CNMV3goJvO&1Gs0tH&UamR~}o^YXM&wlmU&#t|q+j)mt_wdOV zv&_0zy1S{(Nm3^{I`}@x@6ekMYw^Shg5LU)Sl=He?jaW%@RhG*<0Ie!f8-$|@l0#N z)6Ggwv>$b~PkAlEkl>()zx?sfe?vpvbO2~H?@?rc2>h1;O_;(9Iq)}pAQn0V0V@aq zFC`UJ&LGs4jzo}-4tYRc{lH>2>d9_;)f?jLs^>a{a4>`#8{tGssKR`qP>NL?{?L62 z6NmQ5hlaR$*dF%qzWw|Vh+h%n^N85N>#Y%qMkJ!+`qxByJy9Z2v|_ZVct<^sM}gYX zO&1~fu!s49jAhh_*<9B~53=!MuzQIdohZN?C325=tmGwggbplf3j*wGNjd-|NIWp` zLKxiM2DP`y?cpzVE`ekuL1RZuCMu7!w54p&!3Q~v2m*1agXd(DjzRRHNqE>pASRfj zSHkg?a-?HKWO>U$)l!<(v}R2p$;@Up@tF{b<~6%SO>mBroYF&E>bB`jNj8X*<@Ay` z-5Jkua?hOU3}H7P@=bYS2%h`&r!DOn(0i`4JMH|Zj{12}h2Bx14Gm!aEGN0pFeOx? z6-A&!F)BiWrpBNajSxjW8q(cml%(^*=SCtb(j9?Rr7ewHNnskQlQu-3E(H%uZJJZ2 z#gwPa+Gtxks?+e`RH#MuS5J{@rI|9Mrbd+wQJosqXeE`ZGFs|FnkrS+Kvk<sXRo&Bw+x6~u4F}$^Vz;2$6>l@UdtUV}*SzZuD0w;BUiShpz4Nv2 zZRLAkNWvGW`PJ{y>ib^-gVw(To*jS5T3`b&E5HkOaAFnw;Ef@euM*~_g)!{i2xnMX z6!uPp(aGTgHCV(YuB?gG3DN9Q#49Mi4vAeHV~1{-x-@oP64huBL@ETw@7?Q%S=?79 z%6K#|Ci0P+X=B6UxW_l{ZI5@nKZrCrogQ`&h=n|4qQUWvP}ZL|RjEy)4VEOau^*LB zr@u2RxglxPF`APLXGBK%&4)dsWw9sbiqLt>Fw(MU`2O76&v2Q|ODyyuz3kgS7X;3) zv@(u@j7mK_#Eq+@GBcByXFH?u%w#TFMix!!Mh9`!e9Ch|LVaU7H#yaJW^|(4XuUPh z8bO>krIf97XhUPKgRkb`uMfw_96yNIMYOaU!_3GpYr51IR&jYFZR`za+R~)Obe0u~ z?O2oA)2mBMsuu$8JvSR!)J8SA*Q{t(liSsjcDK08hVCWnI?bUub*=|JMAngQk#E_z zvX33?SpWIoNJO^3(aC9L$5Eh0&R?{RJ#NL}TGHm;V6llE<&{*N-bwy6t`jco{sDW) z{8KZ(E8S&gGyC8<{`25^J@O;6oN+w9Hpypx{z^CBS?8*>d9tZZaGVEy(gs?0$a4#! z8J^c+SvAgjQt12VQqD!@AmV zH?-m;)984%x6{`#UwY{Y-(uG{ zT+s1S_qb&*cv*A&>j3Zj-eE4-xlerMd0#l@BVKdA7oX}@M>yQGj{Qwfoa5ZTKB1?N zZHk{>^wKxI;u}}{ikm;sw?Di3DZcFfj*}YgfnWULwU2k{+x-2g!x-!R$MveN@(9q~ zMvwWnPW-N~>!j!Q25|VetM|t2@*>Uuj*r*YZt(h052w7ZspJn_IfV(kPiOPPtD>E+467v7)}M#&(C%&_pYt~EKl#;uI+4X z2Mr4V2aogc&){Bg3L|gpHc#m?&j!&i{Zx;3BrqaY&Ft4DdkJpm#ua>X; zif9K*j_oEd0kJOrm`>~JZ`gb=4PUMg(+&+AukdOR2vM-z5D*aKuK4gS=x~q_nGieZ zj0jQA2=&hbgYf>!PxU@92+#hG@(Az~vrQ0Xkmv9)mEJDkL^15V(C8qJ{CuzaP*Ck2 zkopi27Qb%~+o}x_Q4p~%`v%eQ>TLR8@#yd`82|0ney$k9j}Nu2*#^)N&+p=V@QeZw z^c2wj#&GwKF7(te^kPvKwa@zi zk_PwC^e}KD+io=Y>>D`}^%RmGO)?=B3>__^9Yu{DxzQb2@f9hMCOfM3PVNYe&;2lx z;Ml6nJkcLX&iUl97XDjI_cpK-4YC0Dk0BWc>{y8xIdS+*vH4Ud{H%@!hj0Z!aUd1) z;F3}#cWx;euo4~64`uKT(ajPlF{}Oz~u5J zPw+3vBEb4G-t>|%)$1_BOEKptFyC_H9J4U*5-#&{GLg(J4+%3F3^F;BV?Hx9!zwXF zb1WRLF-y~`Qgbz9iZoeMBTjSmUXw;M;ItvXu z2Wlq6Q#8J_{ypK7(wy-Ao{v5i@d8_~CyP=MkuK-JQsf+PJ;yFSo0C98WeRGb2JRr8 z5L7|cVib0u1~>p5W``v!((WL%1|~oQtAq^!)lQA~A93@^C5BZWv?F1i`VM0Fylp)GS<7x3n}tXTS~&;!AP#XnIs2v=jya zK~Ea~G!7zSQq2NRDfDqHv^)u?NI5i0NfatWv>caIDPt}GkCEu6lsdNaP}L$)Z);Iy zltu{xQgbvxck~}B^-`}SQ#aLDeUwXsH0qEMaY$7}*Dn!ul9GZE3Y#+VOcWzgR6Va` zR%sPcC$mzY00(M-0@PtwZ?sn(lu~2VQZY3_H5DQ_wJba}S-X)Snbl5FG2`@4C`XYP zvoPhN?<}#fEVXq;xV2j;(=mfJMr8mG%vD1T;uW4?1;jN5JfII6!U$@>Qc)oYZ~#dE zK_9e$1S%E-%%LFo0SkVhVm%-b1cDWMpax!m93D1O|Di`=c1v}3M@5zda-are-~|5i zKp~3sA3A|#Wgr7i)@Vs~20FlH2|^#XU2 z2e_7SOIILFfd`)VU2}G34|i;hHD{l9L7Vq^ZT29F)Ck0NOMQ0=$~Qq{K>l@sHAbJ; zcLic=xz=Vwfq1txQX3Qx+}C`6_aFG72%a|v=zt(nVR^SS2G-z5Kh+>SL0myrAVT41 zDPSOm_jzl;cn2bQwbod*S3!j!L9zEBtl(#L)>60Ue*<`c)zyOGS0IX%3d(m!>lb`o z*iO+`XB*Wfjjmuh5WSWYcl|_nH}+DcAVDjjT!FW11>zHab_OsY48Y(CCijRHB6*)+ zODB{cVsr*BU=d)!Y-_*;${}Pwcm^y$5t3mOh5!y;HfBS3iIuoV^+6N3U~Xd|4Zr{l z&fy=nb^`_>87AQgDu8GsGzM}21EP3xPap?6APlyEdXcq#Ss)G2{+JUWd4m686Tsku zYd{0?cn~0%geMscy1)r6po3Q+2Hqf%6BLvGA#dqc0~7%nvY=|&wS5y52Jl!5AUTo^ zqHi}=SL2s(g_Viv_K8`!2BFgpR%I(# z{WnHqAbK?zm}B5q<(ZhPm~L?Z1CCc9sDN(w_8%&`2ISx$a+U)CcOc%Ojb-+6;kcP^ zmVTDqf;S7T-Rseksit671USX?(6cpp2m|6#IeJG2oO zr5jX?7oweAKy4o`d4D@*ml`2tyP;)(p>12I38MaYN!qkgIy4RYuq{HMUyHP#J3&ub zgBx@X)Y_yEVrw0BAh_3pk@bct_YEdmq^a4yQChx%SCrpik5k)DjXJzvIeCRuAogJs z)L;ZwV46vqSZ5o*5u!o++rViSS1ovEwe+UX8@UkzmVJAet=p_YJbN{mtQB{y7ow`^ zwgdVAiPyozpZAR~Ji|8}gdZ19Ke~fw+#rfHvY)v$+uOZ^SYcsXAY8$DW8e?k*=C=7 z#r4}DG&ak<9Ka)4bhng?!-2%J*Q|e4!HpKMWBGFtqG8XqJR$OdZOhlG4IRPD z+RX*x6oi0;zkAN@lpqWp&*xi0&tMUHLBjn!+G`!R*ITyxAx5dWAg-LZz1+A70+=VX zfn^|i|AE%?JJQE`(#twt7vdGLV0jmM9lBlI5dyk7VA`wQ&PTl=op8T8 zLTB`$F&^G4KrL*|npST&mwV>0S9B z-aZBhp3R*d;4#{&d%YmC`sfQd=oezJ;UH$0TOcYw?!#DD2Yc?LnuKrs;18nk1p*bQ z7f6-5^A+Exqgq_Oe&ta=W-VXrZ=LKpGvUu3EneLq+Ig&D_?V$siY*#>bA7cTx!=85 z6P`c=D3ld^fRd8|7NFq%XEVD&RT&JlU;5SdGJ z;N<9W%Epd8yMChhE7>NXH7KkjX|e`Nl`LJNb*e$pB!L7CA{@1&#)PgBol>-j@owI| zJdJEcSc-=XI9(e%ehfLX@n2Ut+%PipaU7NUY^tKdQ`6a-_hqW)`WEbmN?-h9N>ci%|r{pQhy z)DbpgPaq=H8-BI@2xO2#A_UuzplMcGlA#&NWRp%l*;SEHPD$mIw=LIIcHZ@biWAPU zfB_cte;(8hd=usym;4lLVfVh(d z2{;wXhW?o84aQ!Kh2a+8KY|k4!5;(igo+S2#lVRb#S?6K35thzb_tg;3==nR>v zn&YpeZUpA3|8)Bvc!@1mWxDDzDdm$(UN#y}Nit~~lJTyKual<1R_|o}erDRg`3_8Q z!3I}3?~lB?Fpj|azI$-Q5>ISdyZYKI?^zDJwlQTLQw;KE`y%vlS09(0F~lLS%yP>s z2YfGW64n3}%2cZCa?UzKX>pS{`}?kvKd)x!ebI}I7rNKZ|5)vZD8 z{+cU4)Zju#&XY2`|GjG4l&HH+ur)=qZ=JBzpM|>yX(bozWd;~^L{+svhV)+@6u1N z8t20=e|zHV^yy)R&Nuz^@z4{$I`!s{UHXu|1JC|x z%QKI>l#Oq2btB&W2uMGtWsiRhY@o*=XFubKFBF72!5;Fbz5PY-f61%N{u~JY!hxkv zNFNNJW1{y!z7?=`8&qKi;kUpJ>TiZ2eA-$3m%Pr!4|{PFpz0!*y|rZ!g*7bV?}WHQ z{rzi$H00aQ?3Ta!l@M)4tfIk27^Ep?tbMC%oeYC0L=`4*NiLLP62Fw=(IuxV) zZ1_U>ozaYGw4xoUcCr>K@PB2*;1|6ZGZ)IxUw15Iy08c&EfVmAiVWZz!PrMWw$YCQ zgJb~h7`(qZQi791(nTy6}tYZepG&V9*?%B65kXaK-{c~7glDi0c zVu5FY7IrHP_klE6zFP|^8!U%bn_`JOUNfXRDHBjE zzJ{Y7smr*4BnNqN$@5*Kr|;5Hb-Nz+s@dAbt<@XjUr;n#ZAVz|H%z!HWyQ})X>sMni&Ex$2U|bgj*_p zh9~wvZf%D8^ulXZ_iHF}+crJ6eZgm(y~4!C$%u3NceE*|fzlnYTb~@K@s=+g-&Ed@ zn)L$HhS5RA9|=k~o$4R~#BG!? z=RPTJtM>^!+xxN3{d8(*4mULKa&(;tZjk8wNyj$u)Mb0PGh@Af+PquV>uM)7{Uk4w zrn_AWN^0#zdPNoL=6ei*1KU3yERp?o4H4UAMB0(T!H`Fr@$*fG8u6!i7bTXI)!{)L!8eUZi+_g`?$X+tS!noHVU;l& zu1A}V)hZ_^RCzFVazEh;*#Qc&wxZo6vr1~0$+pbCvan!x^Y5s2WI&%l4CVfKKT+~2 zJmw(>dX8UnUbLB>Y99fTR(lI_X#Ct#y*tnn>tShh4=(@pxc(zF&rBUMxclP8nRSvY zXpW1pc~sK!8*v8rI^nRR1Hpue<*d_2a;1X@&lxI(foO9M8oPN5kKup<{`Rm@b4Eef zrgU05CCX2jKB4!Us-JaM$%7mRa_GOuvEDbHB(e;xuU#arYIncx&vJ(E?P zldAwny8yk*NFvQy^+xA1O4(KVrq-`Yp!@oe$gfVM9wWC`+%e!2PGzJO$Hj4v-Siu^ z0X(;@rW-Epi|2OXB07z$d=*n1?OFnO9Mcv~RjV&2ld}%mDAOhGPbc}2+cuWFL3}Qk zS-PLrwpj|tj*;E7He{SRzIPmsO*UotWkD>c-_Ugoa_)E$fDs&e!avvr4 z8ZNTqv#}CAa0gp@McqUOA?f*rgL&Op3DemJmy>z3$WgS~$o0T1;J_8r$#rqw_8b$r zLd~+N$a_`IYlhtGM}eC*rOs;;@7)9E+W>PESe&^x&IlTo`aviR8a8Cj?noN05~y=oS48rl_a;cNUe_w2wDJPY@u9*Atx#DPclxNsPMGR6< z^5u&{$TD&80=W`(@)Pv)MZMX05F6g&*GnrnZg8H8U&l^=x)h1f@I_j#U;_o3RlS$yq9lEk)@%@s>G0C za1}X%OUqrBU^SC3q<)JmmjA*}7P2&(ZzPvMRHkM(2eh(Skoi78Yc9(bE*qXaQ&u{= zhb$T0K}L30w(l-oYA)RnS3%7z&6Jz`9Zg|Lyo@<@-Use{-%=d&mBOPIr3ZUHy4Lg; z4Y~HX1xl&%@H8b!T=HyMX>#1+;AYw3p|K=fStvBwC&P+)so972ir$)$k)inwT5-aL zr8K75(we2}l4UIzsZH<_EoQNwam((gT{_J~!b;+rrORIo7qrc*)Y3$btQ7Fxm2V7{ z6>_DA&5Arhrl4`BW{;>YNaru5R%AC<{OprYTdqKyuUv0cH!-Z_46Qs$4LH#ftl}>H zhLQ6^G_^WT#7?A&E~V@yv`V?SDn_&9;YZceF{s+3U+oG$7dEXLx4HUprMe2T(!D>! zXEsGytOnhz2J6HF=b;+!p@iV1L97l4JW!y=CLhhS#2ko9XzpJ2=Sk3+gilh z2u<7gL)wOEwMMmL##iGe5!%zp+tH-j$6ebqQ`(WaY8hPG*54*?@I-IXcFe!+@HFiJ z3+WKmjB|lw({{)^(PoM-OVlk>t$gT!&F{pd>Lis;bieDoc9WapN=i8{crCtd~ z+CS2L?)9@ryLsG8<5yCoc(dMJ<$_epGxp$0Fma`&S9)+x`*c%!w(s)K?VHucq?2&X zw90!Q%*qIPix5NO>xT!FuX4kwvk^;^kxOe366!?g+T8J^D#`k6?nQZNB~9l#o|^kz zPY2G~s!v^VJ;qf1c%{0eijm;^lTRYkjh`(t z%XEf=%j?Z_#`HwhoLq}Nc}s?W42GhIbK_s;-?*{-pcx z#eDz-Cr9{JzQCn%hYdH48 zG~+!)MCR1ovdFQTc1x8D{!fzkrTz?urOV72Ez9`t^^D>yxZVb3#a>ZwPwv zXUDF}XV7`dd7oM(BF4q(=M?GZZ{r6KM&?@0MwKh($=cg{T<7~ox;T{U7arz&@fZBK z7YzAglVqZF$6L(KVl3#RKJm3$>&DogMcP-iI?~5D^OdPu0uesiQ@6rfx5gK#!WfC% zzrx2LVoADxDKEKETXs2tz9unZIi+GbO?Nr;jKTD2=?C_5cJX2j-b(J-@+ZUENRJin zH!JBHE6uAb=eH}GJlLz9Jga+>jdD+mA9U-=Ggesx%8O~bV|fyzTzaXsS5=Ic$ne%y zq!aoo)ID~(uG(bQA)7GyG*!@kYhhuZwXo9Q^eiO2lrZ<40oy15-mob0Z@Q zV`EEWlTRilRwky_rlvNgW;SMKwr1u4cIE&U0QMFDmd{vPI#_;k_yl0(U}f!SZS81d z<78{=WM}7WZ|`F5=|X?9rw{U>7!rj$AFA?{@EXb^Nm9D%);`m!t?AS za-Ab{JfgCEqcekIG9qHrV&hWc;#1=j(f|??(-V_2l9Dr1QnFG~vjIUoKw5e(Kze#! zdPZJGMt(+Ser9GtW>#TVHb7B!PEk&7ac*8oUS4T_erbL|Sz%#$QBg&4No7fCRasee zSvla0t|;=smI@+{H1x6tj-qlc|^P9 z8LEEWi{-E1ZY>$JjX)CUeN-|uy%{D1#}kSV3wYsU$k7XVkC@0iWAD*~KOV(I_r$vS z>ZTd^QV1oH6BJP8-1H{cA!(M z)Q?KFOl>StuK0wi6%ocpw;orQX|Uz6xQVXcTKj{(xtpl@;`tUmKQ3!hH&xxc%hqEP6v6AaZ+LMLFKgSmc@S3__Lf8$R zkE7{WSwJ+wJ&NfVR8kY&d#9~Mw+>NvHePJ$Kj}L~%K2-;K~k)3-GtWL8lX3S;guDC z8?Fp_&`p`e_eztf;A7~AGA0RfQeQUY*FrMN$&fi~LRp&!YD8RQ%FzPh)>DYOg~44g z%;R9_aHwwe5=k8Kbki>|+$Eh=1i@b4L4w0P7n@h$<6siNpZ}3l7VyaeCRtouz#&US zNJv6VO!oRU1t}>N85s=)V3Ve#r=nt{re>z0VWFX6rKM$~rF}z32f+Rd4tn~(;H0PL zWMKG57#X>K!^FhJ%*@To%FFhKkDXnBgHw=;TbPGOl$TG8k6)Z$Kte!JQcy@r2tZg` zSVUSxVC^Wu&L0eUOtwl#4@> zt3#%nL!P@siHAdlr(=zmW3#tIhqptIk3*l2L%**>pRZ$&pHqjQOS8XQb$~}jpjSzd zPeHI>c1S>KSa5uJSY%{mR8&-SbaYHiOl)jyTwGjye0)MeLSkZKQWBt>PEJWlNlgW$ z(`o7H=@}UrnVEomIx9OnJ16H)Wn_N-zwV$0^wUjEP0!xxXX$i%`yT??9zZ({nD%;m z`}_I^e$|-a!NHNiq0u3L;jv+Wk#T^L(TUM#CP&96$NpeyYkTlp;Voe+$^= z|AVRSFZJ{jUBQi^uL}O`;#>e)MZqm4MqeFU6>k)cB8-4(FMCX8Z*?2k6tjfBKT)^M= zs8}ron)DY0$oAjNQph9rcUqs%*Zsr_z!NCJL1q%@g2k9j~@}JIGR5Vgl^k>g3;E?@0{_@yA>+8`msW7mp zF|cSbuxX#cp?if({|b)*6OR!SAAkvqfEkOB6_b$d6(KtY5hpq^4;nEa>T4ks5|NiA zVlPR>Uy@3`B$IkcCJj8d9+_MQnL-wsLJpZi9+^@BnNsl?DkWqpWh819Bx+S88g(RE z4J0~kLfHd;yMAcBUKqVDSltddozDn5%}Ck}Xqu(i>Ny0e$s{V#l*%ErO1?}= z?r)TwxRq=K70twzjpS56sB7qGXzFTdzthpxfA129>iYEbtc;ATjLhuJtemW@ zob2q}oSeMe+`PQJ|5F(OH1@T%fW97(*Z-~`e`J%k_OAAh?vBph&aS?$F2LFeFwp&* zp242q^bYj`^Z^X_^*__!Khi(&%)sC%08nQJhsJ)@8NhW9i0-EW#XV3r=H?a`mwp$G zwRJ#dzp=Hq_Z(i%0l6h$v;Y1B&|==*-8}#`0`SQGH)i^O;-viNU;a;J1mkdWqbCR` zBL+*>x&2{i1ZsUlHKam8KiOUJO;HUbKYGbULqQeD|(DKGYqQb~7Qg*bl zVzudSa?0<=>!VFo8=c-S5C~eKD}?@g8@_I~@a`1RiqkyJ4bEU>-{ z%&p?Ong@@v>XLG>K)&}~DRbJlv$EK{EDREM`pVNiTA*0|o)P`9bCF)yS6k%aVs`Zv z1ci=g)fTTc7K+njQ=FyqonWXBsJ!Qeo}{&PgpWRP3>NA;8LTWyfzwz1aI?-F$Rrqj zh3csIhg;!q+Ep(5U&`I~L!vULE4)C#+ez@^3p zz@x^&qrt_e!6Tr-C#1nAX25^VL_o?)NcM(^f}NO><24lrDK!Tlq$RcQ;0_6$i|~+#>20S`*BTrh)mkaO`B;=>sZaodCl|1Ez%V&<1{QI-hB!(vhuOC zas&8e?Pm3itF?{WZ)|McZS4R&?Cd@49XuTzJsq9AoL#(KTzx#f0{#3#gMuQ%!egVN z6M=RrF)=+kB{MBOCo?NAC$}&!zqp{Vw6LhGsHnWCxS|-Kr1H5vDyafGq>|F=-;|bC zmzDw4{N{Jt^xQdB)B-%WO_g=eol|vvbxnOuZ9{EcV_kjIujZ+xxw*BqwXLnKqobp< z8xRQh_5tQ=;9~%=Uk?KQ>pun9qoZSEV?Y}{ag_I^QVA>>`a+^} zCo*`MLFqX5n}F)!C&8-}`=jxE=?vjFt@bqYIcl#6$4gZy7t2^WeLXa%hQo2>Q)Az7 zifB~fd_b383Yzp!Xoexb{Z0vP^`+h833G1LDG-e+7z#acY9tPgOfZ=Y6R-KQ-G;*( ztVUE*e-kV7D5Y-tZo}9oTQ-UPwE~RUq~=98$XHIF*QeWvay_B1kIda?Pfiw%PLB+! z8tiK3Ec?N*``M3#i}|)>ljTBwW|3 z=+Ub%90^!FaD#Qe4+^>9Z=4-+g~^6gND(O{d21u}uMJ{FUrI?{5~=@vsv!Uq0Y3!e z0V`6G(U%id`>6QAPTSnkz|q0P)7&ar&!I@pxk)>y(eW~?r6u&ZUDudiols$+Deb!xwU>ZEDzyk+^KW8=rr;q}D% z&Ft6fT=@hV*Kj+%+=N6)%Dud&GyyJ z@zo7rYQFyEcm_1j|3CjXKLJ2u{m*`M{~!F;_3hn1a05uVZ|{D+0-yW=VDJ8~c(}d) zgXd|@!ymJj`^U$J$EV)_spy$sZ-K7^!1%2{mEkW$GAY+&y8oR&2}eW#y>ZG-RSZ| zAd>q-x&3aU?d}~SBrLbJoGzCS1avLdZBkC_&Gt`R(9k#v==0s?-cBr5XLG-|5B!f> zY!om4pjLWB|7<)`i0%g)sc#!PPXVIBx=;O-Lf}MWhTsq|mtx?4p5!KauEphp8lDeV zO@i;Pw~C0oP{9rfz0ur*M?!2`ib!Z)ol0OqUF47u``hwi=jhTYaB4&$xd!C~o<#)1 zLvfzJBGJW7)6JA7nJzO{6?ITFc-PM6~X`Ud47E`n*rl6TY@HyO%4{`H` z`dxxh)lK`M{H8S3K)EUJ;l8klS240nfgFIG>H^ZDGj*5zl-G?Dw*g$C+=HAnn zd_=%tz}a`&Em_tmNE`{EO)p90KJb|QP+H^#(SBrHiP%_*1;r?hlbdV%#^toE7W(#m z`A63i-P)-4$0v0vBc@sD^p&`09>LHp#e$^UY3k)3N?@k3W0B71!Br5N#6p~{Y1B^k zZ>)_RNq+>GCjW>vqsche8Z`@Pr@$$zTloT2Sx4h~yXTLTig4;Fcud9QLq_`c!uh9u z(z*k#ymU@Q?n>Bk-QhOkW-MjX;m3-LH(-KbF!9=gP$;gncF|HrRN$f1gAS*kc?RbN zmyKbNXd|EY@1q81ZC5wl?_15kdCK10PlDo@Sv0A@uys(nbT|k#>>=UvQp;lcSSp*L zD2`9oVEH)oTl!6Y=3Th4o>6VCUP;r^bvchXpT&J2=cYRNIW=OI2!5I;2SzpYhA8s; zl}_uI*b$TWk<-3w`4%e&a75mMesS#AE;yoDJ}*)z1uidtvN5iFU6>ThoHW6l z^3Yj=5d;IDiORygO8=1Ud6$TeA_#^oS_OJ4OPJ(EA|I0%0D+N3fNBTZaDFg)!=8?T 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‎fMfe@^q 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®<#Qt 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%0A>Kujf@hmOi5Nc$6b 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%0A@Cft#Bj)fn;k@TAp|18cdS;tmFx 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
    @V^TKWYN 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�N833b|p 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 &`*dTubSCe&z~mXZT{tu4L7q&gh~HM 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

    fCK1ONCV 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

    W;A2!YckKR<4;MJaLAcBx#1ZPjR_ zX{>}8^@~?5o>v^4;f=aGIaq(~Lf5SD>b<5P#}*HUH6EO^@%Y;}+_fpEDSKW+#$(f` zKh9O1l+lv>X+;M1(DasSk%sr*dOvBfI3Bm3t5?8N`rZ~S(aE?ab!|tV4RJ^w%}_d4 zrghg#-Bf&qpnW?e;@JFFnFeh;(_>sqbg!+}=+D#k>-)EA{RyVW=A$|as#L@Ip#7pk zWBG-sU1`K%shOg?Q$?q2<&Evq+V`ZciZ;{ZbBwf4L3PeWldS63cUC3Wp0^5zPS4%n zI^by1h!OSp-F7*L^uaOR{n;YAU8~GVDbN;tY6=cQHUjU-(3NIZml>CI8NHGu*)lg? z4*e0Uo!y2gDxQ|nuyFP4?{1FgwuJQ5)hx1rOB9G};v8A!j)ON)nRIB50w0;;2)s6P za3LZ9YKa5ET>xNhb|x`_@wWow217GicVmgZKQrk)Fw1120SD?XhY9b~VInIL2Rr|a zjKKi~imBhj-iCPCPM4hut1x@5$e&?hSk7q`yN2KXehnd&W)Uy^U~72-aIVA7x^h+ID~WP`3;S$7Y}W257`FhT~2QCQG5T}v?ykn ztCgP(SoMcv)S=Q)XTPP+%TJ7kqPZTqbBazGiwBojQ& z{UU>_p);-IZ)Pwx&6g-jK&G|R(t?XwiUq%LGvHpNWzkB^(;I{fRi zC#l&c2OQH_-k_|GU|FfU){eVw!s3K`(u)OOMk_UAfanKg3pym$^vOC(0A+%X=f1x& z0TGtCuSg)v;z;|6od+a3@2K8UG!c$8J>sW<;GKPV0F>=XLIFUQM5rSXXrl5Tk;pMx z{yP`|>8$^QL}pEyW`)vk)FU+gi8uO@e;=yAI#vEW1laIaEJFuAKC89=w^?G-1 z)SvX2?X)+?L`6;Ia&SG(5EBS+mto zwZbRTE~&!^>yJqc&8uB^(kJeu6~i+gXcVBZg#>J)5G7^a*y?Jj4H*0g0zRSs^6@n} z`?D{r^fTN2U$T0|AHqK|#`4m9;V?#2?KP5O@H%7UVS5^6oKcoF6m{Gf-r-Z9UFB;c z+iux0UX~4jA`8&jz2C0{_%A9? z$skvtlL+BzeDSM)bNn@T9)go0%~6Lq(kBkwLBfLu1h6C-S5SlOt*;0CHgRa%xdHov z6iZ^tk~WZAfrv5!?%GE=aYY;Dw0JsL7S z&5{ypePg^l!FL&Qh6wT5xuvQ(Bq2dUOjIJ)1@`{}_~rnJAfSxpaNM}iGQcpbA1IFM zj?NYqJuq+49ZBd)Uw8HEDMrGK*9(NIn*i?{j!IKY0cm5ktyZQ z$ZIt|3;(Qq?6V-Jik)~k9(-p!$DmHuXda;b@-*XA?fp+c{RL zAFd&ijgRVV9F8NDmKH=!yVS_Y;YkbmFEx1!TKI`tF>TpS$a;FnfjG3$k$V?&Ia@%e zZq(9tFwS-;%vQ9DXX+q*$~7lYmiOf%NOh!l++X-YVqtXHy^}F-jlKd-C7CN(;+y^O z->~_YZOY$;>DhYLzb`;<>65rd>`orO=HAv{-Ia#^kxl~-oVGR9HSB~q5;aw5e-dcy zXu3cUNkskBas}!LE&;ME4Pb<~^BzxjP`pzIdJ zvY3ozU$fu8VM&@hr!<^Pp}CBsrN;8Bb^s^PWORt5t-hXaMhfc}CqfuU#BaqIXyukltYasD25~8ng0_|Qxm&WPf-omJ;Zg2wdUKq zJs8~r&qSP*JmI*BVF1yMgC>fe_hA=`wL6>&K^C3oeqnTHWWW&nZ)l zl)v*!XXbzwRDk=AW}=VWpeAPEx%w9p4lCOIhvGDdWxo!F2!I3h4wXSq>tTt=6N+T> z`24+A0DQvUSld2VYe1vTpZmQX#D`y0W8PCz(3YxZ)6keack^P~o24#u3_$H`9KX;g zvH9HKImiO|xCh#IqV_Dltg;J(LEdHSG_$Kd&o$!!Op?Hwzt3BJMqcHRIy?>eqQ|92!e>hU$A5+vzGEo;zuY*;*#HlMF zwYdx_I9X*@=(qV}CaqBS8}I#1&d1u6e{$B|6OutRSn&EsZ`_N$9f{li)H>~H@6e2Y zt09*4Gb!X_DSyc}{iXNdcUTv?(xQYf6#UJs{fv7_n&mZo2){NP?5Y#eDf!I%VV7i@ zNW^k75*GSu*1yX~@&>exDalt*maMN>P`5sOzUN)Wam;MC>`*o)?Oj5S?MJ7a+I-yy z6)Te69XCF#pj76nV$DUc3y<(mMDlM|H}cuWy^6Q-vt!=88=In=RN{HiZQ!+d?nfiTRX|1DzTnEh+awQ1|0e4&xY}2N)IRCk_wH7;1j~h)fXZsdz zKsc-QW}QhwTWj(ftj~hDH=HMc0xer?eTh8i0pnJ3 zq9a;jhCGKFht8Itit+z~Jn$4dc!oMRoVY|u`V7XaLoARn7M3X{Bb2!{%zO@*nD$`d z?qdV>b;Rn2;~wiyAwj~jJ#S0b&-+@AJclP0r^kq~|8tE%$Fz)sv$?Ew!{fzG>#uVDleuYVeNtPHFY4aKxVnp42ORqJ z$DUhIA8cCtoFrjgv<|3U+!10_yW!aUs3d{2>9aMQ^|h0PRbK~r8olh^RtUd;Sm=PrjeVClA)VoyH)=qoDG$KDxg8}CS=@c@2T?mC0I}uB14jh#zsOatC}6V1%zojQ{FIdH!H#@KAv!>iw#7a~`swDf zxLohI>iydxzAyGV!F|+hYSM*7Y@7WCP%Z;Sna}WjZ-w5FgumFDP6~+;qa&X!b}3ww z-t%NUd3Ls3N|^w`uTzYQQ{A?}X4n&`Eucy#+2jG#!|_g%Ek%p?K>p*TtJX>x>Sl|Ij-}bk+2Yd!oq7T=c*iJIR6TW8bnv-VlBepZ`EXT?VkEWadX~Du5bi?1* z{a1hfyTt1%>)V>)Uw0L!A8`FS+}ZhwC~rvN52cEAsw zuUhL9cTa{(gm=XTJC}0QK0^UcxuUR+4EZL5Ul0MTKMt5D6n23O1%}XilSRX*zPBXZ z)z=E7uQ5p+Vo`KCj$`Z=rrXCl*L^u;JJmsO83qze(BGXfBp{LIb@CDz3WJmgBD9v``oWF)hi=; zFbU7NM|I)>Rs0+cF(XOC$2%8)joA-N`i*P7iDK<2>n*_-?DYDQ&Nj4s#D+Gs9$~rp z+L(v3jra-c%c?Fr1&>t?mapv~G@BO5S&g6WgmQFk%xiEw+nm(kklY^PZtjkqh(7WB z&+`Sf7n^5pY*WbtU#LW}I ze9@nYG*PbtVCb{>trc!dLEsekiJskj?yiH@e-^#b-Mu@#(QV~J5_6|@hK&8NJ#AvX zDcieK1WQ$&dJv}>%>;_}*EZ9B!4qrSJ-vDPg!+`HuBBuiukQF!%ki`CBl=%)+pV*9 z)cW`~s_Gq#flwhGi-3Ne<!`-*q@982c#bKiifnP$3LUgY}iYLl;DCm%U zIOcvEMh}eYeE^ONUU&i4W)i^%3RV(Iy<9~&s8tw9h=(gRHs14|T_nB2yB}|Ph2`Ri7&#+Gfx!i{cgHP`TL(=}rz;Ro}NMbh36a~7U zf~y?c>~+41G85Kb!=8B$;Le1nsCsU5c`{9q4=3@WofAqIs?oe5)I!OiRBlf+SiI|N zVKZ4r#a}$jZQQ-6^*O(4Xt+Q$wRG5!^{6pAWmWpk!5P&b3^n(M`4@LGZ@*c=wdqXc z7lm)Vf3t*YPFQQbZTzi7g=3$liF{ljldMb9ut(}W%ev*IMC zL0oBSj^XE6UThXXTN+J2(b+4W&iQ%*`ZgWkR^eM&YIZ`YBXc&V;)n@VW0kArb#Tj^ z`ZL^w`or@Ax+YCoP_Xq=no7q;M5i#y+$LI!S{<8WZuUP!>pn5)x)#ChF@hibi?5yJ zx9|(GzAGv{C8lwmy-@_2q2panO*8qfJe%_RdE-oml_8nS4u|Km1(4dwj)~)s5!Yl0 zfN$+zu8xY|-0!Nr@YPiTIAcJJcWeP+w9&fXPbqUL5sv*)A>QCf2#ZKreX(xz1&}TG z+V40H2$v)R#UB8OYB->fSUG1dGaHL47Plp=2;@hZ3A#9fIOB>Mk*@iCvHyt3q;+Xt z7JNC~!V)iBLbFj+>Eo)OfLngDJY$rZ;+BOg<-s)Tb$BTwuRrjT+t@NzUO^VMwpC-~ z--7)>CN9quDS78qIIfG|71rrcI1L?x1~!<8_CNTDh_i+4Hv%Ps=@!Z^7KqjfHaEZr ziqg(}uEFRPhB;+L^;C$E4*XDW=svvCJG5w8zhuZ|Zygm`vXE$jVw0@S;1DCJ?l)q^gdE8?11O! zf#yIbeN8nsvEOq7gjf=?x43Ix>N{VP?1vkU+7Bo*oB)YVIF69E!wRz`S+`B!LYvX2 z-un!h1!{zc5YmdzfyEJzqE7VS2NT3 zDvW<}JBz=@K6SLXTwn*J9o=AK?ca+~5zGuX-O{=@m!e?!_FVIg!YlPYyj$`>F9nrl zcyJ-$7jDjVRQKoUJ@JcPy|;VT2q&)2;2;%6EcmQZtyjF_`^9DRcR_QG} zfU_tUPO}4 zwA>zCS_kjF@Rz?^J!=1YUU8Q`Y}{7u zcgd(<_=+VZty(crE9m`1Tcq~w@aBK>3(vspSq@X@q~!1 zc&)@C0;xY_ecW|LL|m>oFR!QUl}k;&k*D&hwirI6$F=uaxhhDLANMA-bo%j4LqTK~ znCA#JfMX9w+O2X-V4Ik@-q_wL}WM-@_ zm(4!w-2Do)-hjR1w^_rY~Sqn*Pjl`QH4DJjN?f`bZVDP5_Fy z*dmz>0yD#>>NWQ@ez9|<#o0K1ZTvAdC`{clBypkh^0}ua3!^3@u01dz6`+9S1>S9o zmopB7ssPWY!}~%EW82&V*WpOlGmuS(Js}+AU}lf%1B4G>x{KoyGvOENWv>fP7hIm9 zXS8ANz%s%+6)iry9AJfmsWAK$9l${VVi14@Eg*7f17q{bMg#C$ww7F$#GT#r%c+*X zv~jO=h_7?tx8O*)4kOZP1!M8yO(PYW&ESFq6R_{tFiwmlC`=nD8e`f29XaFlA08-A=ct|Gw5xMgT0ZESlyj#cl z;UrkuZC~MvpBHT6OMoC6RR3`6SM(F_nPs3=BE@_$E zl7KUF%ee}D+|J*4|0@xQ3NH~!2oNohstmB3#SXYGUGzRQE#9jto5;AhDY%+9rkZWz zwG`mBf>)g;H%_m4vg9&0oVF92CJKT_F-=q#`;2L>vL$o=G89d^nBHNI zZdt#9e}6sapuq#mNo-2c&A{M${l)}S-R=Ts{XkV0Oqg}5^qf%B+8uO*Id|Ql>j2%< z#ojGwkTdW#Y69gshx#%N_151==-lD5$Jd z*q)3jm~ndu2xo)Ju2aPeo*(;DT{I?}Hu?jb3VxrnT%blmGdon5QGC9{ztTRx|y4{p)-&oc2}H{w>Xve7qG zVx74OHn3Hyj__~r^0$b1X-os_$AL9Jkz~S12$ykVnjqd~>Osxd2NfZ8`{tomA2-6b!aU?%mXUN-&W9_xRIH9@@tKnT%*j824A36gvqDl z7jKu7&c~G4t6lU?aXTuh8nCs6|N8i(DD-Jx#%Sk-i`omPO(p}B`ZFF1bxVOc?#(?D zsN?VsB(NxV=li@@NTI)0?QsGv*J|{xeKshj7_?COy1<45{iix9r_{|1Rm|9;;?NM- zu)F^hgEFjU-r0ti9h4unhJL1|MplIJB&FibUPpqy*va8#gf2aLyBWQdDssRA#72BX z;63K9JAJTIC~^_zH#9*}P55IQhKyMu#`a6k_GMduM{iHfUO z#&uSyS0OqUuoxHg2CzG`?^zZ_ctC0uhCjU2W8e^-T@J(mqRrC%4JvkUxFjLsB_~m^ zV+A0X2(4lW2*EQVK=R$^1;v0|u8q9Gq8SC2S^FHXufvnC1*H=$3!+3}o&Z<^9F{=5 zH30vp3QS-_05L%6zsm*sqqlZx@p8fWtVLKEQoum*UC9N%I9t6n+0o*!9w$qOh~q_R zPH{uW?}$~$-F$eP?02$B7U=@4-Za4m;Y&firE1od01IT8i4?07|Ke{Y3O8;k0;!KH zW~@(kr_9M%)dWO%f3chV!mjF}F0EVHv>Ddvo3Yo3jX_vd_1N^rT`6o~e@%YA)&K}~ zXo`rDF3lNl~G;pt6a2l6jA0y0(quz zC1X*}TqKUGnTSPpG5pN0|G?f+q2B9szMFM?fh4En=LWY1OCg~xD>%V)f=y8WOiO#I;N{4^1^OrfkNc$^cn)1f;s< zr;MS9UER(!`lNU8s1@6QdIlb5Q3f<2!}6MjhyAP}YZbI;t7&JwCL>zvV&JH^=Gn3^ zds3?VAtgK_hWkb+oKc#=cR^rihE2XQ{!yzfqIw!@LX zB;0^O(wrrq%uTsjzNGnP>Gb)iT^N{_n;w`R9)|6eW53ss zs#Y@HSKk=$t9vn-0DrSr7Iisd!`dplfq3$Vk90mDzszzIZ-sdOBkGm!kC3~!LO$$e z01DHlO1E&^vsOcv3ng=2VkK5PCx3oeV;$Fj{$aPyN!As#-?zH|Ja|`itacsvXTS0V zj4Tr+x0AY72hZ1NBpqw%m0N_0^FNT~>E9}K?~r&{$GQ5|R^l9gWG|m}=z7&x-ha4e84tdH1pS#k!d4i8 zisCz@Ks?liKj8HL8{}DJ5avd8rN&P8d^^VRy9cHhE&98sVjJ^CL-I|u$_P2OpBy9o zL}EwdOTg#An9m`tH8zd%m*THnVv%buogN6#&6ivVmV5$|uhp}UYIT<+D;&9B9cD)C z3%Su+(IcB4aFFFJ^A&j?`FqfL=$B8>L*Guop2^USR8bg_!38jp4HAB$F>)f^LftsL z%LB{`dou{YOs8;$U=u0IpZo5ie)fh%qCv53g+R8vYVItpV%t z-}%V42e%i>#(QMe&L2-_RIG>K)?-FETmhV0LEHld7P6z8N!o}EwJgci{I3y-m%Rk8 zkM89V^`butz0=iw&0ukxJzV^t38w+zJgOh^h&vsC`}9@rZ`#$Jd4?fl-uE|<$!hb!=>_1X#sS=5@ zebjCm6{6KbmUcBkP7`%4;%g(VYh!4Jc}}J3N{_@e)Z8%+mNC`wDDInb6Oz3nC&~hQ<)@sJ+cpVwf{=<(@j^J zCYlE?11VJc`Ghy=weW5gVrBpCVTO-wDV+$Qm#%)`eG4;#@ww+!K+P4YDH) zOiC-lX@8QV-rZH+xRTLS5Vjjt@+V`H^&@@coMWgqk;a=5dfw$g_@*UlHnks0n=H$6)r8%ER<; z&Nkx}oGD`2Kz4nCX<&|o!4>36*WQ?LtKZl?qt&5T9)p=5)Z-rqZQmb^ls@_oEb8p9 ze@osp^Q^&5IfEFC&rpHEtw75py^TSYGxm#50*{bB;?90U*%D_$dH7ok6wjg@Zu8}x zEUa@zJ3I=U8H~@X)#^cPZzQt3h9e}O zs-`u0_V|hkx>^#xysZ>H`YF=nuD%Tr6{7x&`1gDJ%R7fjTDj>FkjIgc4B=@pKH@`f z(ZK&~UiP-ZGSvINVA(WE^Nx{;W~r!k$9BXe>N50|HyjAz4_hFB`ScF}DWb*<0)^u= zn=PuO@)lFzXtwdFz&S}0T7b?_i5an@**cHfsz372x0Ea=XDSq7L8)9Q0?r18#1Tkv z8CM)hLlDaVKnEnxzJiRY&;h#3E+`OmPLc=!fkt8hx*xiC?7)ZGOdA#_TrLg>=FumR z)I?pG0Pwgi;FT_~CH<`?KMe(fKxX1ds^V;WAzTTE=ET-eF#?vTXbhK}i#m{e;u5bg z2rd!CV9P8~bRbNEh@)^Meb>3*V}2ROj`&Y7-d{RO#8J4@W&mKGwChYDkJ|b^C`{+z z%lm*5)pwq;!|nU-Vei78WvPA$RPK%X8MO48FZ8eVT=l8 zABYj=!eA1HHeaz%e&6!ZnK6V!!)AQxuMXxtkwCpAzp*H8ReG-X-;MS;a@M!IZ*Fj# zPZZ}qKY%=SygG_ms5n$@nv=nd3I!+A{%m!EbN}$SQZU|IZ!R8r#ys-7nfwWru3v@b z#TfMOAxEIv2Wg4CY;)(44;3!O-a(ulSy1sR6ijE`1euRaz2}GK@d73pbA$L!7xYO? z{D8^;OgIP$$Aqmm%`pdzY^f;VvE?8^!(VxdH-xI3;G>pi&XfFFUz(Mlj8Dxyp3(Q^vN!B2 zL?MCrj1b_^d}W=9C%EHrYZeHV32x2@cDr1F7c)Gs=nfv zbEwF{tATJ&7Lvjrhtfk#_Zdr#rH;rWgWcEhpr=s`2;PB2)*n&ri_J<-`I$H~b#JGM z2HpG5j~~MivC5{<9%ksuKW_=Q3=3WG-p%EUr9F_CbYxreewN=h2TKV}URYCm7hMjV zfc-mbvcXcIOn)G77-7p19Y3Ylxor{l#rWA2IIKe>kJ|+KDAR7!SERFu!?J}eS8OY2 zwCjVsT5!=xY*g^xoSDK&QkAWWRYnmVf~jds!88D%A`>_;a!n3`<+3Hz;a2Xg`$zX}^~38)q=i%E zi!C$7g<~)}rB+T|iyztw6qX1uRlGCBPiH7_d33(2Vc#FK8!)1KwfYBvfW@Ce;;)w;|2H90$USe&f-yhd)>5DwK6pCmCiwp4j`}JAs7+F1ifwDLeRlINhSXci+72_4F(XR zcEf|p#O17FxUAR_d}t=sWbfkUY;qKpB9U?|KfUiHZ)eotz}qF9aYK*%n^ZM2*8mFa z6lD_mlODgV*2nWF+Z5Z@%X_|Gy!ROja%`Oe_uOjPy&3x!69nW`BLKPE=6J|P&M;fZrZ1d01kadsTjMjtQP}5iRX8A1aYyLnNB{x^7CaKiGUG}JcE6q zl7nd<{@erme!b^+Lu?gHWHS4NZ$wuk9cxbIZD-x7xKXWAt!OV-S8zXHvydOaK7^y# zKwh$}zBp7K1%wL1&t|pnJ^-&<+l<99fX_b?|D?aXAkl(*;yVh|WPq@Mmr3TY#s)nA zjvnJG7bI!7r?bcB!p7&bK_7Wu&!)cKGzWh-H+QK5hpa;V>4cM21egWkq#4W|p6H44 zLw=tScm?Kb^zf&7t1!0D6aC?rIPU=juqJ}q=qW4-8IyvTeT_IEAOcN9EbTlIIGlyp&$gNwsR2DxC-XdueBwAMQ9uAD0?N0fTfbP?E8_Np>Qb* zlHMrnTaG|3OHcD|cd2eyS$&voT$pTJaExx|8U7i@)VM}B6$5jmU4lzcA`b%Z!+j7I z;UfA@%VJH-#7E12p^BT1@?i?Atf2@Pq6Z7pBQEnzGI&_M+zAZs^FO4|H}Q-S)~cfA z+qRFjN6$_ZBuArVzp(_I(G;I=alL#=+lKg7(GC60P$Qqs!1 zTg0wZN_}0H{=6)m0Fu8$5_k0xW}!v=m!8l^;i^+wse!>)UC#6UN>JB@W%V!1zEccanEf5Ub zNCrq{&*dkhg1~tbwIBHNEFXPTLhi2s!$t`A`#IJAli69uz|i8Sepu|m{DXs=xdTk> zfs8pYqS=YS^N$H}{uE@tIN}hl6pK^GY;fo0Ee0n=|rw7Mn(YxZqNL$`$Rt8 z+y#>{#>WJl-eCc{d0wW-fge;~&?bU3Q7-e+rcg3yBlaAe^o)gJPz zyb|Eyf&V6)*(VsEl@GAAlMgUK{KX+*B(BD-H%Kl_J%7a*le^lYW2lAa3U8=-hzu4q zNB+(`JTx~UAMTtP?vjsj57Og_*+E+Ut z`m*ObR0ANk$^7=*iI+)mT4wSt5%@My&Km^h4Wc}D&v^sqyg}!~G8L&C`HmHVwmhipRm5=vuTRLi4`gPM+GEl5ZO)SMn3{MuLmZOjV z61_$ejVb5D3d?U2lm99>C!*0gXo<95(Wu@hEpNGJ*x^IQ?B*FW^HuiplrL`NGp&%j zO%k>Y7m*s4kJePxZbbUZn8Z8(P@yWUs$o-|@o*AOc>t?n#w|h-Hm)*d>AqGBuPlYD z`sxxr)Cj(PVeTtg@HwfrZTg*0QE~h7Mv7fFz7|nT=uT zKrWp2=+6u~Dzp@(ri{W!1lU+Bz9igoK@oJ(gnfL#%Y1XsBy(3*d12HoPwG?;edofG z2&ho&7c27i&^Yj=%7p^CS2a7JywQ_uDyLQh%yChWvX}(RmN%_d&}TcVNL8cvXDvDd z%DP9lT?zh0eZE?=${Htx0+)sC(EhD!BkoOz1ObsECfFaJ<2AwN5-_oFzX^$^cmm)dEIPs0>5Ouv zDGM)BEtDFg(9Bkgc+k`S&Ip-HF=>Qli%(EVHQ``7c1hd9u0P2^*1^rx!H2GW5^Vi% zx@rAgDjQ5{$dqn^iY1sh8|=B27beG-i(V@i{q;q<=A3+-Puia|qVeVFu|Vqe;$)dU z>YsGCZz$3DUQxanv7~mbj~Ev4gRtFFJU<+|nE-PWcUtN;=DZ0_g^lkV_1O#rg7 zowu33$I${`vfmWDu6dvv)!eQCjaNFwTJ68p{6l4wivbK7t0%SBfk{wt$!-#@ z#K+6WIWI@ud+o9}ce$}8IbW4(yx#QVpwNUmiC2S|-s4a-@PO@x#ecqCY7a3dnE8q} z>c7|oS<++k2$S~+MLn8=h9sf*#M`6J{zg8#kWK&hlU67oS3U}AivxKEv)Si1Y!&3! zFB>6776X3AfHmH~`RO3O=*|-Zhwf_*OiQg3QyRiV&4-?LG?5^=Fs-Z(C&OwRW*Nwt zJ@67ai*TGY2He*H^!Mo?U|ugVz(olMz7yu&Y6kGLD%8Yz5+A?3mJNCay}h-6VgCd` z)&0U@Q6*3hR6aFsc_-?l`DOxM)zRMOX1 z{EBge2N}kF%Q|EGTPVMa^WS6RoG4YL)fC4O!pQ+HH&yNgW*xhLBfHY5%e~|I$%dR| zak|g3p`=Qs3fTd##kY4%r`-9RR!oU9r+x->SpkPh^H|umJKLwx(>qQFq zXdbMz?ODGWsgJ%evb>`W*|mjfn!>EXNS}L&5~pBGppTn&@$sdlzSmQ)`~{`xd>iR> zO}!yDCYFdiTp` z9+BZa;K{FK9Srl-^P^)qkAI+dT)%FbO4OhwSRN8p?1wfWqtZv>!LmflvP47q)={~{ zoRR!^yClR7(_JophtwMo^H5y;*^u27Usa!~B#y9>uUFR3VRFmZ(A4tFB?5o9zXnU| zAPxq1n6OWo=-x2LMD9PUb|Xka%>w<4n1?bx0xD=hT(931N$N;Gm1t-+>%8Y08S2Ey zBgQ&K{2bK@a}*%_#-!SP^0yC+SHFH90KvV?aR78*I|S1Ql)_FtdrAYbr}V|Yn&$uf z;{Oyk=V2I5s6kyD23+2*H=hZ2K|uj4DH(7W4+__US_Z*|G;vW3B9QG26;m0(0sDX) zdX#06KLpd@l9w)n9x2&(+KLPSF4F9~ILLThz}INzawi4`Yo}w`I)I5pl)|4B8%f ziCZLLevydYpdeJw3-PzIt)LcpD#2WGA+rxhA1Qeh=_v0=oxTA_x32h&WQ#`xjCYd2 zXL4tZTK$(F-Ql>6YmeyZx~%Zz$A|c+51_zJ@wV`Rr{Z5et3Lg;cGUgfKiKiIxQ&Sy zR*3ZDW%|*pLwT~l5r;3fKfNq9&OP1pcQ1Ip(j@kCwz!&CSHgzx5YHXW!+-yhb1iQC z`Sb3c#8KPt+~m)|VhhRW&UBcl`(fo^iibbR9H}*e0`tVAZuN1q?Vc;#>Sbc!;DEwv z_pp|2raQvm6G}G3kkES`8CpFH7P$}>J?LKlhv?@~`<_KKO~>Zf`15F%t)Y32MvxV^ zq@JVtlicrITFG#z`z%W{f%$+vV=hksIgZRO_p)uIXKl-(EzXXYzO@wOy&Gd;nytPL z1qabz@cgw=?}KPPIPrXU(x#lm#jcIdx93u<$YlLO&{J3y4=Q@L@@>@Or`c51SjP5{ zMCAC}(!?P#Eh&v79xbWH$k{}!dNB)>ecvPhj9p8DwHB|=l&vi9qiL5|Ufp-Dey?<9 zY&~8z&w6J`8SNCDeXYMj+;Px*rP4oUTwz0vR14aw~bY-rpmC7ZNy0{h0?>r$8SmqF>FJp?c4wRc3lJ@{3=lZ zJU@!v&cyF|oCM+$!r_2igvNFdrx*?Z1eY1%-iL4_aSI?gF@7 z7sRho1q4f;W84B`%&UN#s^!zpQ}O}yUSqw%f?iwg<9iGTp1J9>zeb051#A^w3EhJA z=?UL*ne}U#Ip-E|@!SR0Mbc#%o*HzCL`c|E;UaiUwC#oSQrEg&_8h~vh|pAi(wson zar~%Xo>e^8OE}W2Oamf|Gp*r`rkY;LmqvY2u=@_=gG0Bt$a#D?h9Vz^| z)B)*^TI!iNM@d#WjN`u+o?uG{q3UATK@^Gmi26nxQvmH~gQ|HcIUNv~Rd2J?%vRxq>ehP%s$?4n0?tG&0}Ci>*HgNv^#ROr)RKM z=GM-UNmJ^+e_rC=#@ucL2~|ama(ksHiLM6sA7f6`Z3FyLTK=BVd9_nx3p90NM4i=W zxttSqzS4CnsYs`ezcI?U-)%YUi;fKEo87Q_N#>uT&XM*^*Ad_QkLR+b54Y0edz8I* z@AwJKibjLag-(3l`RSs`aXL&Z!GwdIOV2J3 zu09@hf6pQx$W2r67h!VSou#D|8u$tnZ$T7;&Oa_)3%hh0>^`BHmto`LD?r0Z2sk?e zYkgRDacoro6hsur8FUU9Snq!R$(SBkh#r3B>@nbHi&;RoYD7?F>3i1xljoy4k-_yI z?|0*M7;KIy6o;FwE22Ge+XEzO5zmO>NP=wLr`VLtfAY5hrwRaAX*vRuM3VB`%r4)! zO#P;b<9{V#4i+a`f-OM&?@Ip1(Rqd?`Tl*FrQkqt<3`1q12>uj+*{luGu)cyO3lm) zaBti*bCx#D)Xd5Xx0aeKQ?tU6+We_$Ss6b3pX1;KyaEnx?)$#J*XMJdLpRKSON6IC zl8OzCyk0oo5hQC=A^uV6O)lrJTb)MV6X=)DaM;1{+F)^rH0+#> zgSSERv%LH2>X(#kiIVlg+>xZdpC`GI#=G6SJKcYF{L(-z+1RzRR8v-&A&wyI(D`$k=DoG^LlNp$a3s4Be(iD3@9pWyD%u zNLlv|Gq>)h7tt_kg|!D>(%RRf^R;$}(2&c>Ss4xUceJnsw@o$Kehr}#=|F7>7*7Z}woKXR=Z z=oqWXC9!=7HKtBoYQpY&!nQ$`lqhVq{q%T8P|W$@z@tqsBwd+p$OHii|(&am((>a`Z<~TRK9s_ zFvN<$8z;)jn>O;B=&&hMZF3ru8(S^kL0od>v#qS9!-=mWO%)?(Y+2tTNKGd^?)B(A z=VhG+uwT5{=y9D>@Tj;aCwy0Z$X!dd5kU|1r+#?!cK$PqPO-z2%+wx@I6L0A8WZ0# z3n2WY9xhUP=3?0D+BFp^F3O zPOe;Y@m-g!0RLfb`oVk|g8!-o?9eSy)_T9Gyjcve>c^-Hp{9K$c@QurjKI5-`TpNQ zG~(O2|BN+$_<2W;AP?9;SFHF`JDPXjS%2o05|qxuZ>&<=Tc5J?-(ldo8{knf9p`A$ z{`@iu>676+8Ct#)pN_!3Ylou`4Ob+PZ?Q|V&jwA5QJH#SAK)vVyQl$lhQ^%+xPC^e z*o(rz?>Ddh+9U}6dVshp1)4EAs&az(H`&NHDITScdffDbY>#E~j5sgCPF z)e+M$<{Y2Owzln7zuhoir8!=u)RO!0zT!3ZwCCpb#e0Y=d*QS_`SMOKyH>8_4z5-Y zXd;b^PBR;s4Lb%X%pz3`g5c}U@a+zsZyh|JcOs7Kc|LVGexBxu7|ZyAzbtF%w1(ti zOv7i&x$ksv&i6oT`bCyT)OH#r-`!CE@RNAFr=CTIt%tx;=~*AyVX(oh&zsn9A*cV7 z7rhlKuNFhSIs!~+i2m<9PzvoTe?BJ99Rj=nM3DI8$RJCa>&Y^XX0#SUPfOQ~(*VrL z?-^^7>Q;{BC}P0GDj>>4kR&vY4e06LrRTe@IwDL}ziwhpq4gaWno?kB{m8qvE z?3thgpq2o_U+Y{n1Dtc!WrTUe1;@)g#7VC5X1D{DNDyn9zI8naqyhBN(aCZ3h7l7u zczL}Zvc6fNC?3c|@C|uN9HE4eAV+8a=?eO*K7)~|; z+Yb4ot&C(v{4S1O7*F^8-X*yHz!!@L>yXcQb_QX|;3`P;hjq}^9-uf9wc{$#cTnWC z3dGPsC&(A^9VTgXziZI4E*4Uf4o#!O@My>(o6s$2@W#3jJdI620G339J|J;*kjwAY zmp|+vaNb})VadL+b&2;U_p1*0fR+Enann(#*?C0?EpxLu1<4=G7|Hz|#6GDqI!jW1 zg!E~PJp#+V`jCAqG;k%fO8dr@vPYNtej)T&!U!8l-JkX!f3cq>vU9{$5Y&2bLycuj9UhI9@=1hO{g9@p@X(?D(t?7Kt?_C5>82QJ2ix9Hv z>5kfU>1!J^nbEP z+Ep*uJrz(mth(MEmv*H%?O}HsP+9pHo$i*U>Y=FgjYIXnG}XC1#2HjZL{i3QtYl~} z&wu0wk(`FeLj-EML4*^rQ(yUMCi2w?_l2xX*YV6n4;XVvSDhctY1Pubs_gr)GHvYVx>g|pKR#I6G*M*{{_ z0Oinyc1X5jGDj0y3-8YPuvg2_)vb!;X6DKvqQJ&N^*IT;m$1d1FU!j;xv6&(AX5QI zc%b{b(j)%7Zp5(16@E|sOkMK|j}MjS^K%N+LUD?tg#}vPyfGk$H>`JI(FH()Xkasc z{!tPVy2=Z6PM`%QFvIv*us?1zko3^U=MR4;IMF!PFf=q#n11q=YpLw`$pNsR@XqZR zg<_!{fTVx1Bc)iW>cZtrLA5F)#gBq!B<5WWfJ|Ga<=NG!DBfsoEJz#>MtCIL2L__O z`6<#Zo5Q7_KbF?M@!Pp2Qq=&xLi!)=Q>UZsX!PoOiD+|!sCNg9N-Ph_E)NUmdRX6a zP6Vnd#O{w}pV}ZHbxjj4vS82^$&V^t>Pbj&U7isR*r2Qaz*lC4cPivot~(_ipP16cKbvOIKVUw+7b7GJefVIIv<5ya?GvAh~%ca^h| z+^19(uN4<*_tZP56cm*@R7e1h!s+wI(ym~%@W zFzzx*L;@_E)U;a+`*`wMnj)5k``%b+O5JH9yl_UyT1p3lslqa~7#Z0C4N0tU0#NWG zu*LN;s%RSKMsAUtHn=_4q6B6uis1<2bh{CA!$2$6C@|JKy}y!?TZm+1^@0=uz)_s% zdZnJ;O+EiX9VVs%mtz1(|Dl`8sTXq)mzJ!X_2T@6bv+*~k07=JFy14{%=7G^2WP#z zo9lpw4p6=xl)Ve7ISWxh-nt;?ksgec@kR1Udox>+6>Pls3=-uU_|X19I{G#d0Y9}7N_7T@xhAw0zDNHwtqXx>scJmg8s9zyvi!mOryCp7R{Rp2$7DygBaHxH+_ z-&8Gc1)7vUr2Y-QGG8qb_gtg?%;|8uTxvimb0{w7HAY6oj} z^dgpfzwE1g8|qzM>ec^Of6M~9R6eS`t?h zXoW3nMO61!kT}ZG90iR2LU#}s+7pxFssGF4sun<*8^?(S^3wHmj}T!A`5G#^;bwz6 zzB+}=x*rsA1}o=_mfZ;{9>M&2y%M+Bh&cDMAsicyme=yc|lk#MupqV(_wxF!Xb zw&PUSm#H?DdD;35K8Bi_Z%K-Mu1NyVwa9uRUv^r(>`8gq?aTEb1{zmi?YG1BjC@ghZHg1d8eRP`frGV4*N;;F8E;^c4gOK!e42VTO4{^jlt=B)TDtm*ea{90ri?cPdeqEw@ zA~WE1MdL#BArDJy7yw_`B{u}apKho%?(QJ=cD{u=biMuleVwB2Lw&~7R6iy@%bsrH zea(WvXWuRw6O$35-Zd7L1qlx(tvd^6;LwcwbNRd-`GFa)Vb0k1$6xB?1M@?-rk8m}x|F>bBZq<-GtuvfcsI{@0FVLB-N(X(Je zrTL@Si2u~l1j!f?V7w`RYBOZ2^LIVs%@N{v$K=Y6=8fuf&obCoMIYWj=^ljAssJs9k%U z`FR#@k@;KD{Q~mL3(zhJ^1ay zx3`(Q8^ftkr>MRQd;1G_#a`Vz^OW*&<_Yx8j{i{hJ5kNjG2)PUHWS`xs94l!UM8rz zJS$>{m?p&J_4o%`<_Syt^`d}{gAu%U2q_GD|6nGfOCzacI)JT6DxcGP)~&XkSf?-JTiVuq~4(sL z0Rn|bve5lZ5SU1!vhgF)08XVe)>V<+x*hbneUq7mb9J&!I%5!RPLjR`l+jGw0IX=g#(6*y~eaa>auPt93p&{CDCLtP5m=m z0+Flfr~vXRn}|INY${46vxc)qPX$tqN8{08E;OTa0-E@j3=s-Lo5IjnIvR5e8=C*91;eKn2(5b!G73G(H z&}C`bQ`-UZ>d&w!ZdTm6DPR99{i7f$j}lUIu6uVgAWv)IuqK+mS{WE)Ds(X@W`H+S zywuh4k$Cx5RiuR)Ozx|g4g$O9smllH-hoC3^J24(C!vy$tpmePex^O)F<2oD!uxiw zfSW1YGOcp-(*_fbx;r6(%{j0at&LZ)oS*6k9U2vPQx_I5*jvD(g(c6b?m%ow>|8z~{>zGbkq>UiN9m~%HTlV5BF zBCpmU`4hKlv|Gf=1W${DQFtqCH7$c(Ko%mWND$K`awo8i>+Z13KN>R`0opRWW!vWB zYp-$+`OU=+wxkGmob&9p2f?Zghz*U3(WGQU_&!0v12f12R~aw{01RL@^MPDgOYZ5L zd2VMN%bsCwYOIFag~ZXtKF-VI{)OA0+bgL~5tPUO<|Lj^!Arh&8#5~pce*@_zovX( zZM_%mT6+r)H9)dj(IglFMQ!s*Q1+a><*q>k9+9wM(JZv*gI&Nqz_1lZ5ZNJZMWD0v zfr>Qtwzmf!BF))fX%=&v^F64dH-KIp_o&`ohUkfGr@>6ea{ZF)<#l$0xvs2I{3x(A z^fZ~Ba*;!v;S!Xhrrx@?xVnw;P(}WrM*w zvaOS!DP1YsU#0$xWxncpXw)b@$#cFwY(_EJImhE$qk9)~*=LrlqZUKBjH?7L1^`}o zl*O_CXC}rIw=>&QB@K7ZeY>(d9)$a9pU!!*>v(5sw_zlI;8g||GxRdWJ%tRl* zJ+1vU`AvIm3u@rdLOpSpNVmnP|E%OX{eG2aXm40;?r*Ly=ZV6rE3#6Q78TNy(aNi7 zPF??j%zv{}o=rurYJHWRY_ng#B|PSIRcZ&&9+j%JG9*M+B>{P_m~((7u>$2;L;P7} zDpzHj1EO@QP+5cUt+G+z*-cvm(&WG%Hg$($)*4!h22P0zy_&{3XVP2xF<0JZ&T zazs0*I;>wceCK135*N%JtyPunZ7(`hud`;YdX_Xa{VX<2$zW@=3x6&o| zrX60?J8_*_@8=l`$C@il#KAA2G?^I7`$-F3b-#u}NB6xuul)RRU6HF55A`9Y%4jzn zFG9_(dU&tP!Fk{{+m`DcXY&SD;n6W%3!ObWDhxdP6XUl_wm%}~#u)54036Xr%DCFP z3WwL6Qi~%%aSVaz7yyTey(Zsv(044zd(dJ5>qhZ{w5`kka^o^BdYPRDkFGpx&mZt-+gb6-Fdv0aA6odVv;4DcXc21y0k3Q zh^Zl*)VD76Jt^ztAD6}FicV^8=0=Ac)SkRF9zkyAP8^Q?^0tUv<@B!q{b$*e#j-2D z@&nu-iL1Zu@e`p}+zH_DVG`5ii2k@?eTIDE+fx{TXD<>R1v#`vVO)GrX?*+nF~!SKDW9<%Q&o){`dV?t|f#< z2DkwQ@zQ+d+ax*3-LzISm<7Uve!)R|RZ9(>AJM#1YS`R@)u_))N-nt9$}_57)aDQ$ z8FX);^p7S`b2!;&PI5KgD&SM>sL`g;v04JqJ^=uc%_00#yb~Ud z`ovfFvE>`hYbNLPz0J`e_=JU8@B+R9kG`sARTd)L8*+sa{Tjwlft)}MATBe^p0=_) zzmP`1t4x*9paLeF3Ihn7r${K$CGgs*&Xt}Km8W9T1Cz9)7#SCfwex^gwq4qnHAP~P zP=~kOj_0c?fI5|rL{jLPDU~X9{dFZ~+y*qoJOj}df6<$$uA6d4{m|8zDhgM_s1;5 zEceU~+i5Stx1!wKCa$4n+}3U{E#l7e!^|%Pb35(4(wBKJ7R@K|QtkbqnkV}A^z%Jp zyLeC(A!ZnmkE9y7Dz&sH6(mv*I&1KQp*jX`*SyF>vZ25_Ax>yEP8YTILG-=929C6L zTb47bS5087UE6>x;L@P}_#|5N9={SzlCSZF7IOb{;Xl4UnLn2Fbp)pfK%^MSZ6lMg zq5f}-CLltFVJWg%RGW@8dm>`P8YGCLa3po8t;t~Yixj+@G;>Cj?jux{n$(J92^XL0 znQHKB0@z!PmR|w{Zg*OCTI*@>!)S79t4&xDwcIp*t2HA@R(<-3!0HKMACWR?^suLM zelRD{M@gx~AYV8spXboXkE4_jOa23Ux_F5U?~wZ*2>kO&ADNY}exl4pT5vxipSfC+ z|Jy%*F^dJA1&y+r^#I7dJ9!nk`YJF`XXJIsq&D#m4ptX2-5acvCF<*SGcYM5G>aZp ziMM%zzcfJS=7z>F=nj7sD4L4Y$0D^F1Q)x0yA-a*`>Actq6Qs`E#CN+nO4zeogPK- zBt)U;Gf+Z6tW#Q!bV=&LBZ~A_P|zveMrTvMMYhot)7o1-O3edl*4%BN>_V9G%0^a` zj9%2s0cuCBURKYEAPz0_L7n(Py|h6bMxsk5=bB@Sca+L(6F}T(?e(yUS;TP-4KY|CNJ}1EA-MBYhnB9MKP1xzE349=8;2R zo&_WC1i<1|{Pmw4y7QHSDB^Rs$o%?2xP4J60&T$OMUf*5t!Nhdq!|c=p?P1D`K*B$ zY`Y`C^4sF$%@^pvBZK{{G^HdeuO^U_OzZmFenfhLxkZZ*XcRO>3O3vm*O!UyQ9rL_mO0vXd5qh5P$)k5K+W;_Z?+!= zX;!HH?G);;^(xD6vA@Ba1QZ2OL;WEnwuNcYY0v@1*<9?wMWq@v9c-_yF=nb$nR+rz zJFwO6Ni#mWwL5S1rQKjR-CX3VOr}#wRaINnRb1;OYtOGUB3H(odXdm*61x}fQR7AX zrf?noEU^AqtLN=%T&0d!8q6mbtaAvRoS(Qe4tfQtx$iCLA>z>eJM%$yn`|?TT=Zh2Z?JoGZx?aN}djdG5^cHxY&^W{&Vbg_oasFN z`vvV{h;4%rd+T>69z#@vsj@_%K)o{u?n$`YJt?1il54c9X!5($yvHP7@m=*JPW71i zCx7{P6$GsnPWvV1-8;5rbx2)KI3R)rq%n)5SA3-(rJhvx;Zx4G)G{sB8bB!W3h8|; zR%`1l@SW%C81r7{_okAe=3vT=|p zlW(HTAt|mo6o<{!H&S2GL8d+Zg*YSnMB84%j8Tw*ZB#pZg}ji4E7|l>Fmn zuGiH_2^Y98WKkSs?4)}AJGntpigv&BYv>LygM$~$xxgBVsSj&Yd*kts^XR$#FZ1tf z7qsD{Cg~-=7s@yYRZ5C+4sYTwXI*AMd;cHm7{kc$z&;X+pUBOzA5VGE@~QP@>4U3) zMZK)lpi`--I$*7BdkJmd-x@0=pq@-chn`Q=pqkW>xkbN4uhA6n(;1~y*izBm$#YzK zWk$C~s+32h#aA|Z@(%}CzhPkYt(3vw(HQ@nX`%3oZeLRxxTdHCpq&Q#M7^bA&6U$&v8^)END^ZWcWNn;<37nT#=*;K<;8?zxg+7+4Ma;V+5oxQ4X*hm z1_l_-?EBUnBpnfkWxgtN{PoUx8&RyUgRPdEv!LT$pu=flsY+m?U&~vDpe|tyyUW;& zAzPhx|L$707(+pX0eGgR7Gr?I9xqsr^BNWIA}uAFWu5)w(<&{qL@|HxTCge}VqYm! zP#u1m&8o{Ia?+zHd+q7hh07rO6Y@}}+$!p`q1yPCxhZM9$guD<4~L0T*vo zt$U}6w)U@UPOlqHfh4yBUxR|&fIY0U#;lN6uDj`(opg&t-LR(hn>iaxzOW(-Sn474 zwkzqZbezu(0ul@D$Jy(ouJ;(KwopVYxUZ;z;$WoM2IbUxjDF9H@CE@9$Yw0f z31G>kyuF7HOY_@4AcOwL;AD*M4A11ezbE%e1|(t#-c^@~mF6J4FID?gWa)=;0UX7t zKMxF-`6qL1%i_QTIB-A=jmqjtTE$SmI8gYp6i(VKIpv8$we+{Ue0C;y`;xLeV?AC?N68r#Pn)X-J9Kb=Ea_aLl4|iXlBmja; zL|FT``PvR$_7^c0R;ZcZtRZzs*Kqi?&B$+{jlYFhRAR9K9uf3MK>?A&^uqfJZzV;p zy!ckmK}aUOO&1oe_J8|9J}?v?XwaIJ<5NA>r`XzNf4i+e)9XP&=Xz#k)Mw+=yW>C+ zz-uJ&u6M()WG4@qlq`e8OoK7DGm1ldH^BQ>YH8=9m(%|1A2-ddeDGJTmf7?;t1niZ z5|;Y1Uz}>KOTG-7h|m3Lz5_YKmE864a*bJ2mQSQLw2mJ9@H59J?#)&*CnZ-no$6G1_jqtkkQ|Z-M_zUUgF>ZeFO# zlX@LPnFmpvq=k`TEiA97&8m$jwSit_%+5Cj<#V^t+?6)ytNM6&l5g1Ye=lgEDK^4k__U%PUgCMZ$GR8X}Kk=)- zZ*dG2Pf(VPp#{?(c0o^_Z-dpA4)*%%R>883i#K# zpLz=)0@kOloviJR-M9iq__eR}t^L@RKE9d4)c>(LRVG_#d~YQ>_I+L8tM-3V5(dYc z6W%<`zt3^81IMXNl2bK$pZi~he=wdB)eN3s=$+5VEtk|Zb$d;KfuA7<18I}kCAj_$ zJyqmv%+_hOz9Y~CE81C{E3x-<#`1Dt7H=aekml8xtx%3yOPsja%Pw}VZ?{lPYhZQ6 z=MRsm#B$>p6n_0rS|@qtrfZ-;_8Ygu*p{vDX+b*$51U5 zn`u-_xk@~Rohjl*B5?TKWEIbCSavObCp=-8AEGm3&S!yco(94VcZlW!?(w`3LGn}T zT1o{g;w!36oHbW+jw-gpKHWHU(EHRqN5b2D6o6YwCzQHaUo#%$*0Hv5_Z_I~p7kW@ zqszglN{dwF*GyOzh6Q&xx$Z}KkWg%EN6o9x6uz~1murnFCRdc>q)`o!5K?D zHI!8mk*!H*QS4}Q&vL}QPnK`bJ?@thi(Wqz5|0^#dx{&?s0mBN&HYOiUq56&_R!A! zK0z@0PH_UQqpNrBC{X`%W~3|-GyZODasStbE8k1tk|StmfjzK$lD(Xss7fCAg?viJXDi~l2T4l z&V1?eOu^M4B?+HD(270*dG*s~_R$8e!f%G;KhncRXDa9FvM4uoEK{9^0j28kMO;Fg zAZNCfrbbB}3pw`}E-B&|>6-O6N7XIbbC+0ZY15f{E~+OfHvie87?D&2?&dtsE$X2gYQ7;VtPkSD3cQNqF$(^nb-3 zPdC%EFFBa<(P-57c|zE0g!3Wrh($VH{(fHn*8o(BH^6*EJF+;^pP{lekSN3i0DJEQ zU~;_&P>EgDW&G7(FYkT<5=c;7n!^TX%306@I46@wGT`@~mnTu%FRMld{1D|FXnBQE z^yjdju3?qGL4qpnF!1JI7LW4%$f{s(~%R3$lV1h7Y{#tX+UpQFG2{sGzTmF5I zG=Wk)Sf)YTNiI%*qCZH!X3)mb*ru|6h>>b}kpd%OAJ56jL9kx$xWfS8H{KRrs4NM{ zVq3#!g~(S0$U$h=P~cRyL*Yl20D~u0!vP_i=b!P{>A(G9C?T}P>E*`8 z)j|f1`EF)Q*aP``mpNQWX|gcPoE4VJsRq}Rx1Z;T&mDNjutfGHlycsRGROG9vx`g{ zZm=m0aQ+z~_vZ9uDziwXj- zidaBgEGAsIQ$24NFu%_QuB=jaOk-qi=(h(zq-qyY1$*4BNqa2iGQHLgd8cKLeH>S)aiKM&jw zR8X8Tmk%%*X9TrpM7(M8*=fyu@TKS#c+2b*_nK9zF9XQKltm*Y+3(3U+ar=_>8s_^ zJc;;r3Ewd8trszyk%!3aG1BQTnx8FRUK~t(N0t?t%=&UOq2SUo9-}+vLCMRJX{m>N zQ1(;G^>rC7dt6$O&)H#FK$947<*E zav$xMdQC8$JF+KBLPVOpa8UNG!UuN(CBJwB7xFZXJkQtdad*F5W7P@G>>43Bg;G4+|79C$R3H`Edl+Z8u-aGM`sH-n?`LPq-6X3bq6a;s#(D^H!GyY`&E= zHsGy-$Y$o|?Qq9$Dw(VSn?(v2C#(imFwc*A%T}M(ey!MUN7DXi-Xe_WK4XeKiGF$- zz=bJ%;Q!Xmy38$TmTX?=+Q9?w#Xt2=_c&)^VNU<5f^YvO;E_a!b>QLV7F>&qzP$i< zSccz9j#IaV_)iFv1={qlEjTSQ-2Zzuh(Qiw(0^{@Q{$aT1mDoKe$*YHIC15Kibr6u zc*F+#XhQ9%v(UdZ-jL5dis~k09)LvmN?t*|k z6L@I9A50+Yv*HR>;N46t;Uc;`PUVe;I2gM^mB7$IuQOlg_w$`TouK+6H^w^YAMFC= zS{}(NzDlCVT=)_;=Onn-W}9Ieq$MJjc6#W|d*i?>}9uu60iI8m#EG zG4@EU<*pQ!K#A>+VeIe!R>|@GWIT?@A@IfFg)DAfW@31wy`~NC!cRGz(Ima zrvXGiveHr-8;#D1>F39p?hM;03>6pqPwWzwWL2aTl0Z)Yu1|XH%Fk8FVO%j@X9SQA zoRxS8n%wlSY9Q_U3!2o?)2gYV>jwD~Bi(b?dC5mg_*0g7daEBMN~`J{O6tp&UQF26 zTG0J%;UG8HehdJlf}gjh`x}d}ju&^w0gTsm0en5{Xqwgr@}Yn?FD~=pfP(h@?+htC z1_QEq(+FMat+wj-=za%j2%A?Ld%Pg`Gqmjy*rqh?TM&Rpx#hM_KPL^avAv9B&JH2# z_`3AP95AcU`ch9%)oCrPiSt$y7O0h7|> zJMqMM{KaSKVgvqrjA1`+_A?M)e~9mSfP~bt`@xR?0cT83uz z`>#@2Y=;pCnaH7$$jDYXKN(z5WAM4P*sn#$^JYfm0R21}uxenu4pF(i9CE)ka=cu0 zXia4{0I^8_at;86y>c@e$^B;*tZgl%Xf-SCp|&xgj!M-KZ{qH+V!&-uJB@BgArLT!U4t*I-MO_kHa zR}#i`R%8#>37;edURj4hX<#KY3?)rNMo-{BtFC~WpG|Z9qhiem&0T_Yylh_F zPd({Xhhv|$G8k1(+mPG=N@-h39}P-Z2EC92vGh>4vfj%++OYcxxYM`T&O`VjH_Wwd z;lAHm0Oa}}?*5%E*uikhz7@{d>(FILEgF6)?*+mbQg|!rAf!DIMlzF=a@RbgK$3gX z@zP@;cVC2G(_%o@x}QHUm<=;o+25kAk#`7RuKqU|U^`)G1Rsy#pA55q)jaVsk9&f9 zV(zf&j3s|{mHjbXR9k;y&?^sQPG>be`n5xVN2$PgW*))aXU_o`VLLGP{Uv16fe$uh zoDJuUhnuaI5Srn@<-*z(fG&MCl5Vz>g$w z`r@N}%suS8u^-B{p(AoeQ1e%+2OUreBPs z7p}_RyCydM$E`4j{)RK1tM#?~lAcZku};M%?=ym9hGco4ZXH~3?OEzB2$RS$otT|f!YZp zTKLno$l*qj9Vi0QuL{a4yn^J$Wx$yXJYt{zQ#(tz5?H)UnEN+?#VKhj2ZC&rw0**P zf<2Q&W)d&9>4pTdXqAU+*75(q{LX>NZAykmsToC0HFdJ*y_{>NDunzo@0Zjorr(%s zR2U|XUuSwU+`+XQANwj9ar$* z5*!`2(;QH%EY5TnkNH{Cfi7R_2^%{(Je9u{z=i?zW5^Id4R6Yqy6a8i8FAWu)r%Qv~as;_aK?VC!YrJ2ws}AqHujE&Wo7eIBnNJ^= z4Dy5w8FL4Eu(o9KJmRvd*v^c z(z~Yep;JD0^Wm==9Zb6wJOSVCu4;nB$^GI02s)M%+21BeSfBW2V?Jboqkk~ON4*zi z3>7RSli94bIP&mZ;@_Q+EHnF*`@zDqY%&El?EYJ|yJ~jjXSHfmn${^c*!Eo^$G1X$ z!>4;4ck}RHdE0y78U8yBYogX?mdF&D*7fgadC<{3DzJ~HtulP$LuHnjTkKoyueNNv zGe3`P7psMwW6FdG@)^~)#>WSwKUJHk8r5@|K2czm+s@hw>i)iFCCsq;Z&^ldpe}j| zk025D_>>UXXpz?bq1}PsDA@z$rg$A$7GxzmC}&X>s;g^-%)STNvR-P4=4%OW?D)^} zh0@W;y)w7)lM|n=y^p?pj^pyTwu&8f-ENL1B3MTp+bDEaj2BGcd;vC*ygLaHBVFlkWpsgVvI?&DX_L&3u z!lZ_ojy`4*{~PqlCdDcw4Ja6XKwbY*UUlE=)_u-OGNgC6ug+UsDlcyO{@lLzFwEXa zB4i?dViX3z^-lnf%$`4%JKy_3T&$DCv-t}?5AxC@*pTPF;Cn_CHU9w(=YhwzKmnD0 zUeqCPD}#d;r}T!$8jYXFIr2T3H2cJVH`{@Ol*EQB;O!w9+#K2f+>M(rm{uFA{O8b# ziG91{U|J9Hp66ji4nLN#>m<;%o)595kl8kH9A4*7|EqC2s-b@qf_#X2W~T4wjsTO#Gi~jN;(2W>k(B8EzR6JP=ic<|PRi(XAuz zzALu3^@z|pAD^)D?q164Ptf)7_rOKKsh~zLvs&xaxFP4xdo$*igV>`~tU$BGM_x!E zR9pL#pP5f%}n>bkEYj#aE@zHUrb>po7{(%6!L}|C4IAUVo%fC+o6GIe= zZ>o%gclvFKmg19k_)1`YhE}XEvHcy`|Qr`cro*YOWakLuS?$)N&J;Jy!~NZKxO!u z;jpe~P4L`s(%ki`55H71eb_^W4$o-2m;B9s2tosfJ@73FK785~0c((aAXUzdiouye zd`Mx`e}q!G$Yw~lu8({Dcu4b2l0l8RoyrfV*mIqGNrhc_?rw_*{-H3h&@0`+5e( zMcmXZ>~LrQB5gz!J*Y*$XzmohJ>nD%BU=8LF^bo|epTE0IW%dk-1fXRs|px#Z6T?p zyHPz^q@E@cPY@0xG=q=aVrEbK{@GQ&=lCREq?sa`^yfDqO@ngp>mudHrh}Mn&{$sV zAu_-`8-ASs`vPyEWxkmH*?2H?$|OC4HMX0x#8h34SndNZ@ot(V{l}b1xzKgm@!Ik7 zxrs+n?_MQ*&bl#I$)jWt`R10+-4NAH%ZT}A_q(F1%O-bbZaAEfSMgdr^ECdWX3ZBV z30;YXf0ur8`>gZ~ru>_=g`aw#yQe#{E+QC1>Q9BfS{}Sv$docoq{U(;hQqxRxbg+e)T#qkLI%D;^RZ2v=0>r*f+( z0$&;KcGo}R)@C72p`xg63hp=_Ph>|ac2mGSVR$f<2VYNQf3M<3v=SR5yTViw$W&_y zGUGawM{eE?F6pK7nj72803rP3SVYmt{Qh)ad<=zMI5D?@T{P;F|3joXc{M%AKa0XH zWQpWq$*hM>F_z@hJNln0^RwWXr39IVQ0J7YgYnAIfzA>>MD?@u%M{1&A9i!H}ma0^5w3~r6C$fNV(aFko?@wREMO42qk6E*gcq30JdEu2Zc#GquV^f3nV zH0LV^3;f9XBdD;vi)_NFNS7?>Y;KdIhWc~t!RYC=1+S#5`^u_=U+;LOxYs1bG@mx91klJBAjck7njoLW8cFZe^Zwp86?%b8gV1F; z{Kw4EIY?j-L{_%F6`*=+HCKqyD59@uBYjJ{$k3cD?xARH+9*>L6JNw9tpRj!AwwKv z(&TXge4YbN5LFEhYt**geE0z0b|plxmJSiHo~ypa*2+x;IMm*vqfgM4%m!|`md6Ry zqUlQR(ze7!nbJm4fFe|d(>doXpFFQlt0f3aY|0U6SlhT64n&rra6BAdteNA!GV`PSoA!C#Dvkt3oaQ>k+Mr`M#!}Bb20$bh@mdK7HM}&t z@;BP$Pt55rlazA(4sr<3#`MADN9-hkmRElqxg%Be>$Q_HDAZVeMz;G`!7*&ch=IIj zQQ1V`3I4Db12dWxPxC@Jji;+Jw20K% zH99R@@S9AcD~oeL%xJ&tz~zHFwM0KaoW2D=bF7J4Nt#Z7{7yCc(w7z&ZT~-SirZ%iX3z{)@44(A?{*JKQ*r->o$u{zA<9LlT&uyfQ!Enek{{ zv`?I&jc5mL83b^fp-3Df>M@?>zK3NO#C9cis8)F$0_#cRgO(1J4~47x)-O7h-3bb% zpuT9_&{(axV>zN{{G(`7Dra#(sGZD~->0rSNEMF$3>8N*)UWzZ7 zlu(CESHgDiRWJ&!8UQ(+(iHHJ&&B+;)BQ^3ptL&VD(uI4_P_lt6CfWEa|6JJQf?*a ze&tPXPa5965@w0eEXpZ>jj-Wb0qoXvkPMkDCwd=X#VI48r%e`J8_Us$xTN>M8qkC> zz`g?@+q4_N-&)T>>or$2m*ylU-OP_p84$lDmac&FFR*0NWvlmD#4I~TVX}RR>`g&i zSQtZps%Rd_y>mlYY>#+tXag*o&eAF<6VJJn$_O8G2Zx^}=`OG+R^w}eSXBlPpr%h8G28@UL~e3Tf3A?-20(ktZX@dRed$Fl zQ6ugu7kTac_6GBD-xBVnj00vizj_*QEI1}UcJ=2d*&37)g6b2$3Zd3->K=(`lB*-@ z!7QZkXaljjtf}!2-P>Hwc4qSIkQ|z0dt%m%J~_Bx^7({sO}IxubM_nE3waudw{qSaN^82uF4$Hy%V;sfw8D;^ zTP+0eO}q$G{-c~HKvY4!Bs-WO?+M>(ALSkvb49r0A*!7mRhxEzAd|ussN&?g^MrRz z12}aSH&4@*D)6wCmRrqW8j1Oho_4%nE;{P!@Q1&`;c!c-Z~i2)&Yjo{VKUL^Rl>yE zvGY+Z4PEFJ7*@cS(*0jg(nl)q9v7q?U)7(g=5gS8;gs>X@G*PDM%Y{#M2YgO(f-bQ z!iiMkR1cs(lV1-J67g6bK<&VH&b&$UH+vJTs}J^yPm#4WMSOaBFbnW3|Dxiji?8z| z)rE!7&Qb4~QN~B3&2P>-!7^>Fcf z%WH2OS6;W^-aOZh@0nCEjKi6J&{zP<%{F0KWb7>F-5f!)s`+LI75BP8_OM3!b^!_@ zs2#6?BIin{FDol1>yo16c=#+XQC92o|77i!!-^tL72jjtXMob2XT>6i-|JmBP~26v zoI=>E^pHAw&T>p*R!s&yL)Wi>3)DWOe+GMtNft(fO*w(QWqZjNU(Z$_cM>#1P@oyT zSC%}$x;)@Igyf;^h)qc6WpPDBP{%-zO9|dFIc2=JI0?vInc^*=A8cF?6XGc4ZQQTI<~ghGxT`vn zTXx{vJKY=3a6IsF%23Js95@L?WPa6&lyD?`GbVVbV#M_f#kKMTF7beRcsc%jh9_S2 zn`(fbrnmJPK%V9Oi@qN=t#&|YjEe=MSpoYk75gz0rrF+(&~X%VEWpn%;43hG+Y8HI zrp5mQ1Px?f4hmw09;86S@WErA4c+>%yjbK~>!EEONv{du^p|}@em@-!@?t)e6$>G^ z)+GxZ-1e#NNHxpEW&S!g3okw-e$+I<0S2En5omiVg1f|Vn^XT3$!1-850L(Pq_K|* zvidad3Ihi_gFTADR*a?}1n#rXgy!_akft$+$(E`g?sCxZtFWDffEA@}x5W5P*o9B9 z+W^7%EpR&SyeSiW2yIGZTseI$`8hzOh}>S_PHvfe^}tK04x!9G`u_3EW$Ufi<^n3N zG~p;+0X~(t3HE}~QE!J6uwOsDoy5KKi__#1I7w|c+ak52;?jkZwF{P&eqEE!0IKXU zRQ{}suCCY?6HsTD^{x@Ro=2WS7>GHDbX$K=`U+AQXNW75ie7Uuz)guA=vYHWBJ2fC zIw%rW27r%Hv!1J&wrP*8@gLHY`+~Y8smYobvo;t^{*up@Xp9vtQM&fC#90HB?-0*S zB>{i`?S{x;2V3SMH-+9lH8|*8tfOV~E&l$^;`<_w&0sz&~&>EH)Hd3M> zQ3BxT0c)YTK7miu*#SbTe41XVNkoJbSc~KRBS!tejdJJg-=t{^K%Rvm2Y4yV=PTW( z0Sqd+;EP-v^)#GNRP&Wu`zbV9ro0KPDT6k2Lz8=<+{mghN>$ZbHNN!UN_N#;tHszC z;zr8M>@A?D+N3ipC@r=s$E2>?t?CfIK9dnr6nkj+hTh24*{IcXFowP; zd-D<(jhi<-9IZ7AvilT+mH-PDEMSeAkRKF(NGg3L@G>_g+_$FFhu4^6M#4dtE1aL> z?Gwf+s;&vbhRTVD872{1CeiarpLNb}Y?~f0CvlvU&rT+1N=VbS^yvQlwFnDQ7om!8 z&z4x1@mm&+Mwqrq)hMsiI2WrFn$_KTtZQ!C?9h!rx+nCSWsxqN4K17bc@DP;B~*Cp z{c+plz!gJ~-sgGIzho3;NSfA!Y6G5nfWWU2@RCnY3GE31SYk9w@gs3X$@-m?1~HYl*7s+lqqyDWwSZr?EZ9x4W(aq^~s` zF&_SIZ^UX5yM2jOLS#Wen`~780~H9W_(GI#Emi&Q%@dmuSs>iAvXWx~ipoa!)K~kB=P#?#@viawLD30<#s>XX{gYH#m248+{Jkmx04o|bwf)6C zJpl~^K;jGcANb8o`;-~8!139^T}8oRtpA!TtJbgl?YUq!=oV_vtvSSS(mR^fFx(pf zYdgBQsek!sk?%+e3xF3L&QYNSCRsNV2RJ6jXOr-xXp0}v7?SC}8sEy8 z;QIO%4n<^}BiSh|OYA%R@TgEgdh8=sY=rQO$Da;MtXNzrxa=%(dSynTPfN=>*NSS2 z3(z=SX4-KmPUPYbo8RA*c5tG{n?!yPRSaRWZ<}@d2zJya>C7fn3nuAlWYSa>{`|0z z*h1R#^!M^<2Ay$hs>WjPO~p2XB)ZV>WELZiqjicu_(AqTt}W8snnXTU3BdzW#9lb+ zqQin>iHgulJr<>m>2yZWN-x;izg@yq8kJIkw4}4a7gIuH}Vp zVr+PiFA&B_KM&az|FVtRc7C6><9vDt9n(_Yk|8rqNMHjHtQw9W%Q}Vy+qM^&es~D- znIjUqDfU1B4N^?vZv%*{Q^m2%A796-)U(`Xf=5*Wz6&qddsy(IDhP!FqVQmKNgpCJ z$b%Gc@2mggq_BPIfWMF+cM7zC3C|-z!#EUu$|EThh4jUz%Cn+o`zPZLD!B7VRiB^PA^cxk zxzIiR%&OQ!Y|V%tYG%K?^(UT@Q0t_c5a2~~AvQ@^9VWdL7;QRMa2v(6_BwQTfH~Z> zzzQk)UK#N%QfW5h=K#m7s(~?DMFH>Mud37*E&vY`RD*>M?S^OOF9j ze-4+Gca|xE%8gEySATnbkapzKe2ViOOQ^$p$Ir^%GL_NxSlhUFkG0fiJDcCmY8`yU z5tV5h+@|;Hk<)M4yo~l9mx9&rEVm;;=i*qShRZu z^N#&70U-##h$Qr}Dp;OJJfZ-l&|ob9=%A(7S#iIp-ckW9bn690=+~$oI)Lb>7JA`^ ziDYT_X((@m$)LaiccEMCfHD1`<|6+m(;a+kN+mwGvWfza3H}M8+`KqymI1As6A#Xu z%_4;4o-=_(q5jlmQMoGh54?+h0I(Jz6O-;CVec+vrEHp-y{e7pKuysQJ^RcD2O2&? zBjS@`>|Q|D?9oF9_~44+AFlR&%#UixXw3NAnE9&TSDmR`9y>NP@J;g0m!XK-GlXNG ziftYoIk=)M64kncf)xrU_8eaP^=t7SQ*Zz;h^so7K5KUGOhXcY@2zt(I|u%ZReJo) zv2L&X{JBtFi*l2fVn<9bhuN#T+N&}dgM45`$Iov7BMJk=8)F4nd9kXsYg%oQBVzj< zV>Ida64gKl1L#V^?VHkPfvYSY4ohA|#FzbOOQ|eZ=<;v2nf9o{>X{I0mA*J5V~5)$ zkVIGLnD44m%xA7ilKgCkVIAdi=4o90;j5K(u@D`Sq-gMdvl<0eeW~)b4UN5@iA6`@g9_RHjE8rl+iF4C6aRVgl0+1a@+BSVs%{`UTu@vJ8@7=#G(r$nG)?(gOlJRM@y%c#lHFnb6aCHE)3M-Lkp4kI< z)%BFjUpcw`YgpD3We~H#uFd)=*%|%hQp&4OH~agKv9f-`L^=j`SI0|p<$_BZzI}Ml zb!+s#KluI3&z-gT{wIS!zi=Lo{O03(T>ouj=5mDHp+VV~^Q-&X-$XOzO=7=(FdJzt zl-3U?^HN2^IOPRQp2eer>a!`KqSi4OjOd{n-yB);B6UjlmOQxFfW?W*J(a}0L4e65 z)|ihK==5!@6h_fCR*U9)pN`ojSDaKlb+lz5r})?aA^OA)^7rG?&#JX8CqMV_DyBqd z{x!RgLPuIpJSVEm*v%7-W{l@t=hbRP5k{W4g-^{>dd4e1o*2z0sa!E;?~4l=JSAq$ zT-w@Kd6M>HT!L3w?YZr3`ENVl+w$C@&5g?p?kXRKj^t&5xiMOAFwc$@j9Hy_i{mZFy`5Mgk zf|J{>C=-yLdz4T?G?6v!rb5Ac zdn@5?Ly$GZVSa1_k;aeIfed*^7eDK9h$BoPz)$Lgflg`_l;L1qB-K$&`^HY6z41&% zKa_lcf_#TnSO5t+(IF6oJ@P4t-zd_!lrN&G;-{zH%@%@#UF(~9AJP5eR3cr&dxWRU z;tcyebcSUK7HPs9go?ha>F^uQ7zc&Mtvz6ImyX`g2EvD(*X(!LThC}aXNz&5$g}qk zB?b;WK%WaLb#ktatqQ;O@^EA2`u(5ntz%({kwsl&N$IawCJn~k^U~voP`-ov6VD>f zo39jI{%8K=KZ)>`#i!mauwNI<7`ZkZzg^@XgL5q^9$6k|Jjq@`H~x9=V)=KIcHWq` zCs4o=TB8|RNt7)3E{BfnM&xXCHoZEFbxhB2xZvH=EK$1bWPVl*s8p$9hMTok4|4!! z?_pIGHWl3c#4}phY_0GK*^=%toZ#qy`iFJCG)s7-H8G<2k9C>Lgg+BGfllyQ#JgNhcXhQ7+EU74RkS-hU38)) zl(rZKUh^B(3jU1`TD%R6v7emtaMYpX-Jr4 zR~~ewk6!iolPX_VgClpP8zj~69w^6{TERb}Ng@gGTZ7% z(mBv)JuuV6apF-%%Gl4}&YA7KjS0nBcK@x3S_1C5*mRa(_>4?-UAG{eo>gH(A20Wl z2QJ9A8Sx2CI@_kqUPzrGlcg zI3WOB82(jUy3kiBa=%$RN;O($C`%}M+|7GwJg2$I%81G#pgLHr=LO5?};;?g!YE7_dy-hiFU#>bgP1I)Lh57Z5oYkoRXvgC&yQX;k3LXUxWkQMAGG zT2Fh1PE5O8oN)9Dqcx#<{EUoYi7Mtmg?V6zL({93vnJJbKNhYyx|$JhTLz5xQbLq| z0p5ji`lDy=UgAIMG2P`iM)>d=(mCC(wE0c7yR7NPO_2;1Udnd@1r0Z|lUwe;j z>4hN|^SW@Iz?e;^Q%b>#ofUk$^DhO0ZZ4kmvnbyG$b9Kn}HG!jYTT4K#n*vnxdn+1AGLs zggiZZI!i)gO~Pd@IopVw6+*r|0z{L8&WVA8s!*LqC>4sdGBY4K)+!nUoQ^V;MdxS( zrR4z950pVlvngIrK}4|-c9i7h=4M&nLJSv_ZvZygK^ez^jm(NvD`ZSNk53L1 zy(eI5~ZJsI|nmFD>&jrc*HVSggvr0DORvxPrlmc#fDy)e4~#fV_S z=M>BrDq*ilY3939q8Zwpghu1SkADE2aIt?aiTU>wdYB1&=p=WGBJ>2HJb>r>ke%_- zDI-q0d!8=UB#IK@*9EFoR0{&_;lhjSJ6V?HI4C2qd^T+xi-U#R&RsOqsy0r}civWQPp7LRw!hX*`r^-Wi z#V;WFOBgtJ5(FO)DuWqRNddq+hJaBcrE;3i0Bvr!Zo$_8>u+#7pJr&D0NoL7T{jdX z8%dHu$!(CO3JBY+K`0s|&N9;R&$4zfbiNI6+em!yLv+~rBI?;O5#NH=MZkS^BjV-5 z!SNEL4Jo%kh{p~jU=|)oguZ?b8nXv&9L`M-5jTD2k15UFyVK~{2`an|N{8o7fF!gf z&)b>=?7eV*LbI?a_0PmYxLp3!-RGckIOyzh;j#S8>&K-dp#@DSQ?#sUcZJ_rDsUGj zp;#?_;JLJFa4Jv5)IXd$H*b9}URxhfWMl@ZH9lV1BvY9ZYTjRDb>(>VT9M_>aULV_ z=>Ud5H0<@PuBKtL0UfHizI#HzQDG;e?Xa0r z{0AklCfb^DL8Ka;8W-+PkhuhueFH^9`47pKF~)X8Sv#U4ni*mf%93uSIc6}_W~;fq zql2nL5?Q%44*#E;f=k8Kf26XvVrrT%PeH19#NaZ+Dl9J4$sx*#Kj`$KsM8BOBnf9B zspo=shOvNnKS0_Y1P9~!auHQ!z#a`MOEC?0)JmK&8aN8mSNN8 z8t!)U1z%O9BgnQic|(_lW*@ZM&Fz)t<&mIN&d#FGVaXF-O)tJ_3A<#097oP;@Ar`ZaS6#ZccAa8Ji1;hy*H9knT@G>vhuS zC4(rf*7~e`_!V-;2sthg?0~y_`)>&2M3JdR%V1*5rOK9Q7R-T9NFk{B4WM;LLOx(H z&8OOKf3^4>yyPe1=Ch#;cmN(6Q!)fi{~oC@a-wa%lmKWZkhQKf(skq?0Z-NyS-4>S zMPeLWDyWw225BnTC}pIkLVj4Ov>R-$P-UK~XcS}n=b2&uQ`#% z!7}I=R;);5kwWU1JbmMjTEwv0Q=)oYYGm(H`CX>^KO$e!&V2_>g}!QM)*sOY+zmFv z={Z@rIO;6TUg6{Hjj5lf{!4VNin@77GsaPhy;KK}-PD4V*<8SY4auqJpK3FEG7CMb zw+nJVyX(lFv=EI13KIc*J0>E}01@R9{JlOJ%q-x>ZGRjTB*$Nh@2uGo{nM?VG1j^7 zC(yjp`G@9NM#d4D5G@3~A}FCZKe18W@XnEXl|+L=YX$UWZ_NV+w~lxNTaV}^_JAv; zOtc%ENe%3mx9#d7nna*lh%t--@XVNO-%iw9P4s@1cx#{N4NqFE)F!5RJ8MRcE7lT4SMT@#nJ_K-&6~x-TsITzuo$Gfe<6B$=(xR9pXXE6Y`{jI2 z-y5&LcWo{0@2Z{6_F1c_t8-l{v6c#NUftgVD$cYLR<1)+me^|^d2&0V-YGW!h%KP2 zCuN2O=YNT!zly5Nso2`X3S>)v*SG7>wg2aFU#(mDA6cb@yTtlL#A@9stJfIXBo4hG zA873#GM-S2_Ov z#)U^O;;!jljLv6Q60BqF_cbfS*>6rmJ*6NV0U4nes788h^PcuFsQ>rXz_a5iX22Yp zo2kfmeeG_o6!QTo_QD!|+y6-Lf&493K=;CkGR}9i*f}AO@c$l5n z#z6PW^sL_(UBFX*Q$2CJ7kvxg6=How)l8=I}&XPLXO#Yc~FdN*$b~~#1WFopFH8{ZYnG^yIyi$|WFl8pS0~|LzmKEO=ZfZ^W z9(?UOnKf)#_+8WobNsgU@p9_AA=#$3hqsM;I+G`hmxjdv?&&zDjRAxQyW~dC6u*Rv zoG+Q1+)WGqV^?}^<3jU%_>O3j^c4e^+x&OYbW8iK>sPj4L1RhyfgMqPe&P3O%4D1a zR{2?A_?Fa5<&ZZIEmkrTJC*IqR1RI^s=7pBKd?^u(y&|jO38z+q2lV1spU5{)aQum zDgQa$Lky??Cz^5d#q-EB6dQ%1S-}T@vqqi@V`d6x1JpLi!c_K1o``0-FuOWDrq+;s z=m`9k8N8Ct?gVvsO`|=;K(18(d*a6)h?^Z)UjDt=@#RN>c}f5|TS97$_@ECUg9du5 z0F!b+yTuTZMu^C}c$b}91v31VB>o!QYbNP+hK|7RVf{}u{z?QtJ`sW?0>$tKjm}vT zm)|UfD`>60>Ay;=aelj^kkI?G?*5&(f9XDfSs+W-cS^26@lHT@5#X?xpVJe%-$!F3 zhga`v>1_=b`_CmH3=4~09bc}A*jOK@X#9y34gvn)ukjRgx+ZX?&%Zs*-I21Qne1;& zgxK+p$0VML1H7*FBrV3BdYfHs6nSanEMm-X^^_ZE{Tj0RVc=wRe*5*pJJ?vn zxu7`3B4dDzxqyrzPJ3~6UFKrw<)+Xpk+LpzEuBwc$C((to)11tAJo0%u?ix~xQ%S? zrzIa1i<=vBRB&Qw_&&B;P1l%kkG>uFT}@oJ&kc(wMN8bz{+s@^SvR=z7o0XSCiK84<{o_4 z$1zVt&O8lZFzh5Kt>WCn9HKc1PUE1_{J}=sF7m)@B0mb5eNS zUa+hqhX7_d-0Xdh;%RAlUGq){A)7=dl}`pR1LUc@DMm;WR|Bx1G0qn(=#GwrD(7LN zvX#jTU3v1FDg=W^w3ZuCsXT=8gxnF*S7-E%&J{j%9tmTF5RZ<0q)J85CdKgP;&9Du zY;(4#E6%T5U$>J(6T~fYfI`-t6o~NwG!SeY>{+e|zh_H3XK;ZzC{nu# zOg4d2If2S7rrwH(J(KK0&8f5iEJpI-d0_{kIABZo1tVI*xn zON`dL+!%j+1B1z$ijNktBAoWk5kOFG9=Zn-hng&1M z!F&kN#?G=NJljb$xH6LiK@!br`B=qLdYPy3%b0S{OZp-ep6V&80_sIL4CvE<+!HG< zOAdW@w{QLeB?m-ozrXkHw*K8eZ*w2oe!KJN7Jz5nd}I6%)$@)5Mup4(|L|Sf{&md$ z-Bm_**nAThjpkj_(K(746xaYiSeC`QVmqo#49A1@lz{@>6?E0tST_wFkl@!e3sFcu zM48egsB>IJ)-WHY4zU+GzS!!5ff=d?hzlP?gSfJJR|SF)_La5>Ei02!h|~_gK@o7x zlEvqycD(ScK85(GO`RmBEKzQu+r*DZ--qYP3&y~Ud?+wrTSnOXtTi!7LX zY>U*{e4;amplAhfmd&GpNks%^%y-cz`7EAPrczk?g!TboV#vMW7-3bjtXRUwP;;hK zPbx__DbnKr2{9te>Fi3e0U!O~E`9f{q)6D@R8@aNu=!5!t%u*|svYD)hD53u=y5#c z-y+M-H5Mhdp%rSzsOdS)0%z4OE6J|bJSq}1wA$H@i$*w12Ca8jjd~t7zW!4GE-3f> z_KldB5U|`5^KmZfrt&rm?z|&amdMRx%f4XMns_(&R*{a%^1WHI%Umw3-%w5B|Luli zak33xFH8M1Kd`jW)_2r33rjxE?|!Yc5A8?*DR!kSYdFpYs>nPlB0 zw}S^!F4cp-0IB9Xf@&#FcSO=Ch0aS2F@H8tk9g7>pX{wQ^W#@xH5+Ny4TKY>{?XE1E7j?qOKKdY@x9*hb<-O=?T=#Dca?%6z?i|9Hp3Fm@#yVn-&fb$?M1o` z7{lc)T9PhL;|oeIbw}58dZ>cTO%EjwKsq z4QDA%bD-^CDTYeqanu3{E+!E>s@IltK51J@5&Tly`A3dvjZUmq^vkK~Q{HJyLgR?# z(CX!h9J9`E+-axVu2$Tk(h^Eb<4Nv^+4iPhdQS^#jyZOzl~9!ZBT9M@F6Wcxo15p| z(;TcW?b2^>Bp&a8I%YNL(E=<^s_4OJjLKPdP>WlRtW9q@3Lc26+vKQhsG3qo@a~QT+h0Rm@DVU zs+maS}fY^>>SgCbJ@%vBaFk-~*{eY)pxs!E{~5z7KOQdNPSdD%BGLXj_K zQmce)LvJFh4G+8$7(p!paxL;cqB>d(%ob0qo}^|~^)Bq{o_M*M(y;X7p=!<0AOouW z3Jv3X{1pM#A~_vQmjR+)(+y3vT|W>uV0%)v#bb(Qu-J zY*KqpB1g{uuI)oNliOH&kOSR;!QvMx9Ld-0ujviDE~qnJh^VI?`fEb^YcpHgbW26T z8p9jRbuzRsKHn;WeHDUJ0J+yq|AjFAkq}^40C@3Irjh8hJw?Eks)Fd(r~&miLrxs% z-%0_gwwJ&+Og{}=$o#AQ1>Sgmu>=UX4iIC&2TC+w+wZmyf6dS_+%EZxXdG7_0M>}2 z_pZ|qm_M|n%#jD68H|5aw7@mOkuzwaO~#X*3S_C6Y>KiKLDFxRV#Mt#mx{k*6H1+! z6_ZS$Dl}o%1P1Fa7bznou)l#AoW$s&r0VZnZ+e-(d^v_w+3YVa1xHG?Q@6T1a7PAx z?g>3jDRM{BBoViSz5;H3D-uRYir~v{NvL$GSc&1TMS?FHy0`qKH5nR~nfRKLTCD7E zsJs-A1WqyCYRjEGiyjJEqhyi89;!c2TMM57in&?~+*V~Df7CQ5sUfJxYu4S}xq5e2 zaQY)KVHxKC`G7jL(|TbEDwL&SP_*BC z`WyGfi*&8syGf@EWoGZmF;NLyG(%$6`vbP|@ee+HYuf)akvs@!mvn~bX zT}sO;@H8r?UNObz`Tu=T@{6yCn43m+zyCY;TVWkOEC2kWY;{O}6*Hf{k^Sox^!MM~ zKmK_x4mtFVhdnQ0HISVCuF016oYK>9qK76V0AE}){_qsOJCD_elNUJK%XiQu)C1Cq zqmMQfYIhbY!nh6+gj+NR z?bjE+xB`@|X{xAx%?!FLgYH%Xam%A`MS@g$P3#bWfuoWxWcV}S#*z#r8%2S)1CTh{ zD{~&Ljq(u%`20)6_{Q*(=Q@}nDf4^RxiX^j(;|O=D-~v;5xYWs z#EY=G>1zdjdu*oJZ}XkW(zgZd>AdF4E4oVg?0+p7z_YTZk*2VQvZym<&RDubJY8wr zVzdUMYT$ImN?B6|q|9_tJK|(yFRt0uF~+e_3l*OX5qc}GGD9YK{1&oFt>KTQ9;S~y z!65k(Z~auhJ4Wf$Z>rEfjpieX&5k?2v@h3K8gpBwIHgsxqMb*H&Zc#etiL8M-IXUQ zDl>XI1&_Os@<)|O^nn_|L6*zeZI>g2uEz(u$bPNGn>9uDw1jACpXdZb zG{r|2GP8i-&0rZz20ocqGyna{{X&}QEud+V=`IwyYaZRD284|Vy)gzps59GDZTuME z_1XFY9MG>y2C0u!8?IyxV%Uup^sd=e7CEwxjeLGIzs4L5NiD#v6|WbX*T?wwo*ielJyMvXEU82e%R-Z^X4pL_0LZ5#Ky+ zJ_P=5p-qA~cY^dVmK>U18<>V`)<%z4$|VR zXvEWa!+rTkAQDRzB3=}|gZNoNd6Fc2Bdqx2qG-;j=&mQ6ivy~Vy|4_B%T9FBiQ#JD zP$+lj8XNRD)9QWZ>hMx2K-m$b(H^470BI0G8MjLeTD(6AHGakn{G5HfnKts}>WR8d&U>0kJP3E_1qGls}BZR8L@CzxtAiA=Lz;IJ78QEWrX=yU{QCxLt>D9 z>6@cMf~9i}%ekFq0DN`HVb(LIBPK^r;7wDl;KAUeSEP<69LQGc<~8dCUFpErGm({D z4t?seMFikw$0MCn!WSRmyJwu1p4*Fg?N+=}rs3WO>$x?FF)Y>oD>ErT9yVTZk}cBw zkMCrU-`M3~-QE`0rEvR2u+2`m?Ld6d4=^5eh=77PEz)$*n@%U*`madQ(D%tQ(%g9S zaoO7jCy&^Co-qhlUTN~pG9RBe58+)f0w`T=bR1ZZL!%SlskWA*`H>U#)VSJr^6e3c zVQTxa529zg-4)y`fE>Vjz(pMr)0za)3*^(2X})Ju5T9GXBz?VIP$`h#z9*N+fYsLI z_q9yMyXHlrb0QJ2de_J<-#mTtvZ<3?vAaifXJiBd*0Q0I#97WI^YuN>8LYTn4?_iO z2df`G{7^@Jh+n=JqP_(u8+z_J~ywMFOjcZC`XDLkdrD>2A)Tt#-gmnU}-% zI_UTIP-3Vgk)~PW#XGo_oa^WL4ZgLTO$dk@I?i}KFk4rAvgBKQLmBYzr4mp7o%FgQ z{x3qHTUV%E3pv4jnJVFb$}v2k3?zKe4W#_BxkF>uVo+hW%W0JPY6S}%Y@BLpyG}b_ za{x2Wi^V&%5Q5IXsF)q%rvh=Iho!RZ;+GDT0dI=Q;G>?oh{&?M zz3DCre_4q2cXJ6pvR2*|jn4^2$awtsjtb|Hx3+Hmm|%(}ny$*|d&r!kk*b?-mknPj zj<35uTVE;8fACWvLS*>})4ijX|G|%s$U}Kj++69g_TlF+_v6od_n`76lfGxRb4a=4 z>+q!Tt5qv$=O?C)+q^jUFR0TMGHW`0U_@{%$&-BssN6bs3+>AjkCYSoat#U>nH2|`| zwg#E*j_>yvpu02Z8${Zhmf=4^BSTw?I}`7<@7-8FJXlLJ5UQgfYSe~c0?TY&*DnqE5X3^wr`FD8YyT>NAUED5;4Uw zV_g>EgrU5`F0aYF`|HCll?5E=EL0}FN5JrHUssxM77I<BX)z&Qi(oG-ov&3q0Qjx>+SBQQ2H_Buc%68x*Ds+dN%hDRsLnU7 z9Y9Q-o;DU@JNqq^#0|-V;{ScKyTXOqz#$ChJzD6VGaqJSZy`<)rB4`Q!M2j9CflqxAA#?TRKNKpNRzhYuwCcd-<=2K8K$f+4t?LM>56`I zXt~>nPo%*tX`CTr{Z;*O z!@s57*LS=x4xZSYZTfp98QYimykj%z&yz5dg!yJMi6Bo|lf;E~@xNxCi?Ng~NUTU_&kvRj?8o8K`_ zZ$=AF3^U}q3$)L9XiEa5gH_Q({LVNGRJEM6GJ$Q(DEl9^6U2{nZP<~gES|g4n89`W z`Pxn~{ijmC8kbMuq^4m%pir0hTn6$Pb9i1Ja{zDpzL7zN@d95wSjd<;#3#id0n42G zSoVUd44l27F~UGi%sxbuU11N{1y|#Rc3Ug)2QBE8T?SFvHTGvi!NLZeJ9UzAslOc> z@^Go@2=1xmBhbd6fD{dtzz_^XSTAjs3O8T{L1W_Qc-2uoAu)}*kRZ*&m4zbiU}2M( zS`A@z5-}Pq%wA-Hh#OEZ{Fia1cbF?F7R0dT<~(+HsO5*q@H02^wq**z?Id zZVkhUrklVXlr`}hL)7yl04B8&VlU`u-2F)B+H~^-#t!*(m`^c@Mdh1F<_z&E^h^@? z&_!ra_xA#PQvFXUxhz3F7BQ(_pG_P=B&9ALY1-!!;Yzp14X1RvZisv6h)>ZB#=2+A za?zJA4mn95%IF#qiD(ysp(1bW!W8c8fr<>qtdAR+y-r)5F|+y08~YOkZH&1*M6*JP zx!XUZk|-~$i{-Muj|@&M#8&L?xdzooj@cb9x{)nv&PEU0-Hh59;(H)_uujHya3jk#%n>!=z>$5G8RKW3PM*QT5Q*m@Q8n4mwL9dGA3vBp6Zk@({l52w z*XIF=8p})3S>6$?R$qEz83x0PF_q!zGI4d7X86z7=hJyq*+tS)g8n_04*VZt*FhzF zwyIve{=NFYmJahvx*~=^2om56g5&V~B@xh<)r3fKwd203#S2s*vLSA+ z_Ft%MB$EkIVX|zYuAGyI_KP5OJWwDHAfQbK?ctZjDtTx>TS9>!V@2ZPI7CEy8LrWS z2MMqq!-rN`8#Rb*F(N((Mz)6{DUO)ECjeVIK)QDv7<4bHI0>Q}uomxPo5}*8jxkj+ zxC%Xda86)yKvgqb)9Yx9fAtN=HtS+H54)O6k~FfKNv|fXASys|5P8LMaYDe(b-ro< zd1cd0)JuoM0_Ox~*~pX;OKIf|>i$Z_d2e9g%=^*e^;5lxy)U`~y?$L0{0sDBi`mjldy)_lC z(@EhoXU-&bc+@X>*26PH^&@ttsxV}NQaIT*XO!Y^f=AUOp5wx8JSA?MLr1-z8iXbl zMRzf>74hpbdSr~ChC}FNkW`LACl!5X2Rf7Vqo^SzE6(n3?Nh@#-#Li6nCRPaVtnc(T{$EF5?7b8{z zti+^Zh=$ig@%T?Rn(0r@|0s_JtT`ybc58LxseIP>c>9O5hB*Qv`|!LKmLBzoW}H{jKe zZ?8R%Vm`Y`R&d6heJXgUf-aK&-y`Wca_)fvkc{aWkAv&HO4?Rz+EzxcwSClu^DLe* z>X+MxYt-Vj8zZUvT#Hzl-3uFmIYxN{H)3s8Yn^Zk#?3PC;(R3@$DH6O8IzPSKUldh zy8C(ud~fb&tkz>07q^K3Z*lVwD)ekFMP;9H@{`BwmBVp(4I)iNy)@r@=c0sp(dV=# z4t?wE6Ue^JK15s;zf<-Tf_0^eF=mI|$x+A_lD_6)*AZXi@vn^oY?TT!h~FGh;H%?9 zk>g&VasT^i`RMz*bbm+E|8aCK-c0|GAK(3EY;&EtZSMExE-9P)y^(TFlS_zlr&4Wm zoBK5h)m%deQPSlzbC1eBNi~wxr;DUgAI)#y-#PEUVCQ}II`8M}`FuRqCFy%b$h9~U z=iDp*SVxP^;+{%hP_)Ow;S&kdoU5q~zOgL>N3HA2M4L5C8hi>3fA(YAxolf*#n1sR zD?E&=MOyW7r}DoM5wYq7oz_E??U^ma6x`tNcpQ84h&Z-2QCpe`!FWoPLd zWY{XieP7Dqbu2VUHN(X6;GkU^+1_U8?|@gU)bJCjkxLM{bl@nmHt?!^z;=Nn;+b*Y zQ~FT#{-`=jZrVP^ppb57u~Gf3O*t5PG-Q8fNC?J}c=QBW!7?r=JkLI20)S4Re?-hh zpev>1S!$oCC!3Ev9}{2}B6z1prKMEst<^&$(bsVF%gI=um;FcCy$Y|?Xs>XH*M~uA zsUXc1(3@zGx(`p)mZxIQleA^>wSeN?tGEaCIQ$5*o{Q^N6;}mHbWf@VSZnwMuJv3# z7r%_O2wZ?Ta(7i1KzME<9+2!h<>@Kh?uNqB8+-yO zuyr=HLiT1zZYgmL?pkq^`xsS^BZNNQ@3ci%r`EB{rh{B-@-)R+`ReLF>IJBYfS znvDrpQVHnCK&aJt5UJ^UUMy0hZCKSwR)xd^WpZmK8P@>JywAuGgtOF+Q;1hT_LEA* zLMTvBzJ|d9`|3r_+Q#7&B-@NtR76b6bl zC7u1E#fNbUM2-IK6hW9KU3IAool$KUdGIMWrjiqrc}O3MF6(sKr-v5V_P9Yr<1}4$ ziy09f)|%C<^D7ck?i0H7ASHmcvhJEx2vvr)EG9pT?{XF5wQp<(-+B#d9kIXcwRt^E`yt*%u1p|kD(oE!o4I`Ik1~ZKLgX0k@W@qcX6F<<&2cIRb z@3nung?$yKs_-kM@E7-O*i@a;6RwE7b3HLvM6&-sDV7pIb)k8F$EQWj5oGPzFKN!Y zVZn0>qlwRE#XG8go_Y;n*dmUqV%4~irK>7lJk6-r4lJI-Yg)RO5Jp=Rb>^d_Bk+aj zYn(iy!=5QQsGS!6dJU;|J1%hTeBk;Ou1BVWUEByVrQVWo#{_TZx!dPcEs79#$*ea4y@qsT)g)em#y{ zbSX<9+(__?H61VA3hSc~MchT2!vU}WMoB9-vyGu}DJ%lSOllOtwC6rbFO5;mxy2VL zGj=Vl(TMHKu1a`}i*puX18|nW!KG5|YMx|ErQ#>5Qh1QExq>tzyzNz`Oe(d#({)@k zTyB!8Fp<|`7yi^$GlNi-5x73N12$h+R#+h^Zhn{%(5iZ_HoX$6pgZ)5?)jv0>MPf| z+(s{zl026eZ}noiFSc?Rbp+c<;jw za=@DEl2v-JMd}{U?UjmhcXG=d`9Q|Mw4qPoONJKt{;Pu)+$GbvV9W3bbNz!BTaX_I zo(20BT3Bat!j6PJA;<`GD?a7Km1iO`VQ|W0-J=|=Wj~3=i|j0?eu1gcc+tJmr(K0) zJ&%l zy)4ymZRZ2lI0w)<%~6f*5G~!4w+(d!S}E3Q>554&o?l3JlvJZILjS$frHb07y(9rr zvY)5?cn+F<4Wczb9^CZqJL%)ecwWvZosGTS@%d36poAmK?sxv@&&jfYD{@nz6{^#Z zHyeJs`C=bP@Ol@nnqM7cUB|c@ZXX3&8uHZk@TNkCr653+{~;lGm|4okif&TnLg^4A zIFKrZ2%A`RqPst{T5{t3oIW`lx&;J^@^cNEb7oqKWtl#$+>&e24CrPK`G@mu$V62P z;nwOma)S%1omkPvZ%Jt@DZ`0KG8K9-&vM34dEBT!M8)wd^-zhw zvMxOPWlaJD&Ux{N28_&LX{NK(2c?^=L23`?)kCzU5F_HoR7p#aYWK)FOY02!^Lr)m zgrbqebi{>FxT7G!5s1AkewYHG@-!=0TI(C{9{ya*W@&D1h^48Dh4SLxC7nvPme^fA zXdin!&RQz>HDrPeEK5pxpoj|BxqIX}a>?Ok>^De}45~E{YRdq&ev|vB)2Bd9q3%lQ z*0iCK2)xg}G2Ey8!E{x?3vSNz32pJ6o-V1uIJ)VY@^ebA@20YDV^AOyIKFUU4~vMf z{)uKu|NM0ovPRsPQ3~kCR$$Nxtt7Wi(ub+bOE`}F)j~b>>*3c66g+tyj7U_Mh6zYz zeG`#zf_@8k>Lbd`f9xKIuX+|>TuQtp{}`(8TEotHw3OTTV7!IG*ZO^>!fio#{?0W% zxhY8(2wz3DS-KRU9~BUNGl6@$WajEUK&iY&KbFd45RAU3Tw;iKlr8=-whn&&UN@bm zD51HJvGzdHI(lm4ER1b0>aVlF4jEvFGqe6m z_R3u1Zg^$8=eznmA-K9JfmWRMDXV7McIif&_jA!ZHZS>v^a|(AU9V!1LTiNXyYh;t<-yd{V z>EcCZyzdATG;w9z} zE8-`+P{lI8uP?8+1l%sJcPZxh;&!;C-h(80UfGD}WZ3VQ+C~<8z7u*hbb%B5a`C!} zXKtgDNISt*Cy!SD^9e)S{3!~$_Wlf^nkgigUN2m>?gSDZPY?(eb{{6v&kk6nwAb$G ztGj#AA>+P#>#6K8x9oYVPfsuQeETzARyNmh>Ujl_67!)JI&PJwfWEK|upUIn>_+m7 z*l&mP9^sVIiZ*lyDg697ZA8DcCb zush?x2C*Bi7|!)OnLI@Ck9PXb2aDAH;J(B-`T*J5Vp{1%)=hv~gs^grNI&!LYVzw$ z`&*fyk=jj3>?O zMD`?Y^Hxj$0odf3EOF`zj;7*-hUB3u(Sg*;}_glZnMLI+X z*{3I}|Ka>}$v2+nXMenTOW)>Xn`q8kQIk{3!J@Z3{y6;G`eZvxM8A8K*F_M-N*jwY z5)m&*>b2|2d-@he3;v;sXRDUZ`qbr{VUs^xzk<(dZZzN(_-&49m_63KKRKq}(pbq` zt88wmUE)O4E`Pi^y(^oEKc0zOles;~B2-?@T+Y$B%P-NkZ3Zv3Ag>62QHmG+t1(dCvVzbOvm#-NVcUGQiV~gvW z$3po{F{{P@6&oGj`@S-Gqi^?C=~W0CU;(BvAi_u8oXJF|TT9ks`?VZ}NJs`xLN~l$ zGm4BVsDP*&E+hX;zE}DyJ9J??nCFqQB3@Dil7J&^~N+a9ToEY7k*uberP zGf_S*NFUjgZ-7gu3`*%H8SD{o;Uzi2^>_oe0~81|XzJ&*0@5jovH3NGl493)tbk}n zfc{6XR`VEwKMv@vh|Pzy)O7q%H=!!wh?^i22w_z7nDRP+94b{Q8pZH?9!I%zXHM@7 zQ^!x(O#2^PlVn+g`>1WJ!sr0`YS)8B#;)z74zvb;ae)wHsG}tD9m;3-9z@fQrYa3BK1P3%i&euEFG& zqw9a`T;^W7o;dJUHn!N_PFJ}<;-G)6xKs_&E9IF=ZAXfHbf~j8(#eq}-r;}rn*s^x9%HMj%|v2PS*QP` zE}wC#vGz<=mMBdTGBP;n|C_QKRs&ewbqs_n2lTgr0>RQ50^;!|0Bmv+LjCEuQzbZzU>o$r-@(BCLY(uxkyz8m7CUb+8gYF8fP|%MC zGwW%1aBB%BlE{)l2YHoGOJaxF2pv`A_5D1YXw2a@(G-r|P@Z&KhbAN-L!^TISPF5x zdk+*IFxe6UFv-KFgGCbXvkm}Q#m~lhoewEp*+(SN zGA7jyDa%uZi|7-(c&%s7k)CC>Wes}gD?{weX0Ei58olm+n2-A-yaEam4LUS%aJMFt zBTx~wF&P-`rWB3$GQhP8qKASRx=qfuEqH@8!dg*GK===QyC!l0{Ty~}KE2hr6Y0*q z8r#(NLo@okPC%!4-{mAV!eGp+J)6#X$BP-sgwgDKYH6iMLcEy3yD-ClQWzKFt#I1z|rGazE!$0R6@)-O$XS$Q$( z1J8d!^1Z3z9CEdj*NXU+_4G4SRXjocWeGqfQOHF;_9t_lf+9ZzNp}Dss{Fg)r2T1# z7yzP(gUI-Yj8ji##w?;7?CAZ7wRfP4XCCKT0}Q0E8aHU%-)J##RzG+X&u=WP0_ zlyV*0YLzK0XY>)rCE1i6+}XkibTwZUQGl$)^h|<@PbD4qe$>KU>a@a~Yni@Yday$1A<)`f%y+U&@xpM+%K3ZfR!P`9)6 zmM#Wdy+!n3U(L6@ zI-7%im;ZWVO^Mwi(nWl;6ov?-7+?FKlzfLa^Nv^;i?X1 zYSVlmxVy>SPEhmFnEy#00J_r-DdMCl2B9heNKY=v5Fj*=0OiF&?u`58bY#5_G7kXN z22j`AzX&ue5bG*`Z}f`uw_Lw|O&YzYZu_k=_fhI7MV-HoXv^f#QN*B4?Pn4PmLZTk*9Tx@jYK8lMT}L_^W*_BL z9v*t7B2sTa6AgU#cmXm7=%-ccx~0&qe#O^50EBWmFNV1>e6E=E7kpS_Sc~2X$Cmo- zje0>)gx_QNnlSA06YNdc$)|1TdA<@h>jvT5jn(MJZox46%M%gZDR?FThFt-pl>-UN z{78VLjxt=pgm-a71XM9eeO0e-%V!_Q;EPwnrdr7b*2P{Li$V;3LFY7ZeY*esolZhi znRAvwWDAo}(21|_$2TmyJX9v6FDs?i$ebl5v3XLB-(&^Lb)K;rU9obz%Lm5zRXWCxICAwV?0Z5lD@6RE_i>x(Men z9QI*!i;s~Xzyhe45R&u}s`MWKE)&pgsRFT|8E*`65q}KL;tThTzjF%dee3Y-$Oi|k9xfUD#W-zGhirdBdWT0G^TKihHzs|cPi^UbYk8GT4V97c-uiT<-R17E zLAXBz@&FpZ35QuAn|Q zL)1cl@ZDPK4}y>)+Y%}zpRL|t#hx=nUvpJJSz(`O^=F4q$Zt^#)iroE2kX?uR_RG^ zDUB^c@tF3F*Nj(qkEqvAu;UEdtdC0eFuR#mBP;Suc*NlYQ!wpX`?*GR*e@}ffz;9d zn_T-S>txa6v8~!q9iwqc#j^?(-AS18`;llBY zjc*T&5dTr;fXc=Z;us!{`$(B_v39`w(!Yo@y86kIP3=O7j%^=mkZ;wTM2ERVZI31? zh^48yrQZdTw(Q{8e@94qw+)6(7Cl@0ILy;??AF1uc2yi8K1j~=zq78#73zw2!d;bZTq*W{H#lWbCK1LA zp2Ljwp2lg$;{Xz4@<$G$YcSz8QWaF$BP2{K-H6ZzrZXn^2!j=NDT7n$igp$hq#yc} ziFm-iT+s|q%Y?ag#Q{sw zr@pJ5YdK}-N6aS#9c`gJ<2arA(0DyRBM=Wd#ybO@ldx|oI$1Y)vNY6QExBeTWFZ6{ zZKyx;%OcbCvp1jP+kf5cy?pKkoi7F9GYnQ2f9>h8c5%saX=ZA7cVD>v?ZWrVn)OqJ;HMWak}kgW5@_&2oqXmV zf)4ZMpFITgJ%w3aUrIdnZx*-HSkHut?!gHOg>+DizEU+ap)N? z3=mNW)EopzGlVBx)`9d#)grcraam`!zxYV8(-u^+QdD=pu*IJtO4!+Nx2;nS|MO@d zwVH@(q?@mR^Njj^ddxLTfA|jg?B}|p_##M32uO0Z$7s+W_>3YJ;z^=_3^_Kb!BFnL zY`(*Sg_gaz~mLA7vqZeauft2gVR zrOXq3HBvC3L~Ie*HoitXd%<;Q$j9|}LAw5LQRqqTKqREfmjQ9q%lXrMBoO*EHA8s1 zA`(_BLP8JoexC}6xx#)oOK_{4z5<^4ZSnGjG3{iad+w=QMiy>q(CT|QLQP)6AZhi8 z@y=dYRS^j$x()b<`VFOjL~=svn{(prPt3wX+jp%^B|D=<`OtVqy!eA#`EgF^4ri~VrCP+pa( z(K=u(d(%sHWJTs$Y+Ls6uqD4W!7ruoQ-l^E{_0<)j~4oTp!0e6TZQ}eb{20TRSY2e z4Ak!!&#bZx@UZF+NZI?HhwGKvvPruhAYCG~gpZW(|DTo%y9qIs0|iz9&;Xz+1LVqq z5N^GdZGB`I$AHjR9WM0j7gzo2u6k~6OP#Ty9$_!EMMU>gn%SpclAUR6lm5O7NTC^r$W*e^VR;9V+!fFW`S-G(!){VFvUte@=p2OwQtBUeq5-QmN- zIN+uTur(1TjlyjfF6cLpW&Y-PX6q-WgK5}Y2LG7l(6OcOmi`P#_&XlJzQUd{mJKz( z^u}%nc^&2Eur~bU0}L^9@6SlJ@ydz7-s~#!=c>`AbJL?HXU3jd*Ilncp8TxVG-q@y zeDGGx-`Lc$A=?H^MU^qs>a`cxl zl)WH|kvHD{$^Z+nXkjd>CEsZ)Nwe%s$7z<)fKXXmhVVa4xq}PBMQQd!Br0P5zZXaa zLD7sA0H}4e765#JPyre?ngf93*P6jXJ3KcSh^wYU#GO@R(@~lgSRfE7T_i zA{87S&ea^3p;GMXIi^+-QsSg>Ex}q{qneS8RKMiNYg8?CT!u(E6JyhH+SNoV$n?M! zN|vyQ0L+#{X#%0roFW#=4&P~w(rRUZkeaQuLGgpeo{pq^uWw-NVXWsUDUa-FCsh|X z(k#_dSvg9~+X38cX$kCiRA#K!j3}kWm?4PgA)qKCiy?zh9EOQsOYz}Pf(6okn#zuy zJZ;0*$~kE9PelI^14qR(TLzz$+rC%`7wUjbI4P6Jb3D^c;5q2Fb%R`#Zq09i;Bx7J z0j!Vi%1I>#Sy`uzjkgxYv;e9N+Mt&#EW>sP!xRdZ3KqrB2phL5vq+5&1cmd>!t0Z-;q=Sij#|M6YA=+sl}9XqHG&E-hYEpG~y( zHbw(e_zZY#;IdEeNpsBpEiM>yG^Rg8_Gn7bqzs+0Ebz8T+5k-$L=9!)%m|4yq7DlA z=@w2iZBNfS8&4&ay6?SIGwez!CP~WMeJC5Ya?lzollI=Gr6H7Pp>tNg_qdj5g9*UA zsM+cUMka#OP;MLj5IRDOdlD)md*;WSH=WT_aN&=xLHUB!=i6WLAs;7>`JOI68kQgS zAvw%1d_K=qbVf3mF^C=BBbdrPDrv9WF=cNT5{w25RLo}h`KK4ns#gXF6vn6{R+V*P zBTc8%)ArgrwWsm^O}Vj;@0*@78n39dO+zR&3*kYep*xCzD2AQxnjjnqB@}}si|h#5u>g0LI552XhM{(ZgF1=< zV$?%4&|?TKt^^Rpt=r%5%`Ul`i!2|7Xu2-r{6Cgl<_%`MbF|zdT5MI7N!gC(%BWaR zmfHI=7D|^=h^PQS1(GE3&~PAjlmd0;3@Q_WNIRMrE{Crv-AY8drC2v#qi1Lv^RPmA zTC?*I``^6BD667N&DcBb9-?~?2h8P_5mQ-~fZBa&?6q4AHAMV!zw{D%P~_HRnjD`C zej&kDby@(B47oRljv!2I^`joPh-b)eQXCi;V13vU)GV!m`jl4Y< z4OqfxZa_(RpWri>BfuhW3+s&P1~Ln-!O{cxrInv|lo3p*HW-LJGI?8S@JXoBcl^Ww zY17IV=o2Pzexu7KPtx~C4&7UaHePj8dWUak9gp|#~ z`}|XyF&j>9XD>@W6xfLHRIZJi)1_Kth;{lg3utHMhw>ZTQ4;r91;N0t6;I*85)y+$(#4j`NPdTyH~ zlXZn{>e?k*3Q@riY`ifMe3L81j+oCr`~p9(V*UP_t&-}5d!nG2C)r-KPd|ZlX2QsZ zutJ9!#2gg+H(p5*mS|=BiCj~Z;%wUhfGTTyrK=|}KtA7c49^yqNpILG7Ed{W@O6X} zwkjImJe(|@0kUtx)6E|vkqNDg0_8~(QkOAwcFO}w!-nX(_2Y_)AeY6Ejjr?+@yku= zmoLS$tW>O4?%I_vR%Si!rgnis$>wf>}-phfgQq1o>kEz zu(lTGX7)BW&i>A_PC5Ri?Qdz91-CiW6}qU z&2ToFLG}1~OQn-riLS&l!<|~|8(vqY%x_Bmh){WH@G#9$P z_vMjA)6)g_KHoGM$Ga#EKai>Jf&J%xlOK+d4wH7zj1QH^JZ3#NuC5`%I|bGFc|Ocy zwN~b;5iCgbT=BHp&xAaw?BIS}L5%GY`L|6Ld)xK-^KH!AjxWZXEccaO2Q(zvXytgM zdc%y?=R|IvcmC#e#N_wRz^U-7z%4=X%U9(jv50SNKUw>hR?hC}e*02{j{Ya$;ZDQ7 zQ^Y(u!ZGpDx_Z%@x%$D4FAGLp)eRR$?M)6G6aD+{;KkMfm}$$mD>Jsi58f+eMhcs= zKc&}%*9nODu5XxUlEII7n#0DrrTGb}ndXC+;g(ClD~}mu?vr;T2b@bV;>rXZ%)J98 zv%~vFsc8G?i$U8mY|vjfA|UUdVZmG1NrZ3gPbV(^50(Lu)TFtnGX%huXZ6N^G=UcL zo%yGA?f;so?Ee|85d(3ih1rBb9H&X@l;ctzDtbRvp&S5?qUOjKmc^hRWM~o1@#viU3*L#MibKzZBkw=-D9wODh&?Sqx3+PQSZc@F%1 z5n@2a#)qTrvjH|tj}p5bb=g#(K}Yz|z)5BhE4n~?t{*|cf<32z#(a~GO)y^_ZJI}E?i8^lPM{us8DNIP=it%m{FKrzNd#^J(m>IJA-3@jPw3C=(G z9k>w&6wNX@Sm))g26-6ndF)bb#BI|*n?QeqIP8pRyipwPt(P$wq(C$@rJA5{S=$r8S}&;=!uy95p)=RGQNDqJHBJD@SNua`@AAryyoNF-Y2L5 z#dZPW9x`IPl*A)hiCM9jUkuFeBFrBS=06T*djs=>U!rY*(V&W`6~kB?n1}JDm2-*V zBm0hx?CUus8GaWQaw2J8v7`%iubUt?%9vrcDr2lnH(~^X4`nCM-1ZNn?RzmGJrX05 zM25#q?yrab&vi4Jk&2%jfzq85PQu*cWhu-ll#o3ZdqhY zTHf;wq3lz`C0+aB+b255Ui9HrD zMdvrpys|snn$}&A9XLc3<^Pavsv)#~!}N8C+~JCFU zho*amq3cIG$v5bIS)%x)w*QTx9^b%nTDtYpPjL5f9kB<9@sdIxvVK0|hRvTN_I>9K zz3U-hh)t1!nFL543w{MV{7F+ujtJ^lkA8_a(`B4i?8!|~F-Vv(T|?v@49z8O0DQjz zZGPm|e}DXuR$7CdY!H)qdE!{e4dR2fq@H?2`Yum_J4B8-AEw3g+R`_;-NV(@E5a)myuZ zzDKhpLnspaJNI?no29^z(5@(QqQ-&ILI;7V@D z!i#&-T{W-I*!iaS;YJ-KdsR}l`u3OGbyg|g*0DcVAAAC$(tZ*I)K+yW63(U72o;QS zTaDrS&WR75PhDlo;h~;1&LQcr1C9@EG9gZskb>jalMR(c=*lS}eJNf{Ns5~DBt$Np zQ$nfba376#pqo38g+-787RZ+7!bw3e*Wh`a(LevC7paF=#~s>Nx0o5D(_I9|HgHAP zBB$!%92hToJ>ps>{K0<2%dM>NBHdomYE+)yYu?gp!-#$YxadKY6;0<)$Us>t^kIqH zxNW`9qJn=+)TN||{T6O@nyu#tEcdR01eP;K_|oXaLk7}^AS&>ogL9{7OSw33+_23N z<6yI;q(Oc1?6HaFKg|3;Og^Fj=wR2GxD+D|IGuoAPPGSN1vwmPbnJ z^0UoPT`V)nQ`qvt_g&kr0=QY#?UA`>yXeVpDi!MtF}r`MOP3D(PCGlr2$b{+ys>;X z;y)2&LQh|eob;;{A$DPxj}3k~wJb&b2aA5>ASzui_I2Y9p={cwO6(bv`r-TZf1DDC z`pkz?g}G#L=6_WyodX~H74s&{HadZ#WMiA?Jg1pz$C+vY)fQYrx-xF(oCG+iH@DE- z=h3xb(KXk5Q)U0KN>uvSW9OBF-0p{WU;AqYKuwY~=nIEg&mZ30h~v#i>phP?ujWDg zCSmnWgUv$mAdYlZW&7jo;}66(uX{*p%5{-8WPnmwWlaXtlOlAhAk_${8(h@*B>D*# zRZB#=&=*`N(0supB8!G7=fZ0!2$8x)<}lo)m;B`w$b_@lW{vm*dEH(fkyC`Ig^9FL zL|A~=KAisd>FzTt)+}=5NDR82k(JBuKfhp2MC-XkW`3VWon>&zMK+{KGh=Slvsw0m z$@At#uQnjB-!U7ux7?eggS?9nkXCp)7hyvLDQtW;Uj;E$@{~`QIaD{j()4^~`bOOB z%}3MIMl(QnHB&{tmtXbiCC6EBn_-$zB5)w)cDSj^Thkcb5!t{NB|9x`6CwCkcq7{~ zb}uM0U?kP`rtyGHgppL@cd{~|Q1npYlxX2pTin3MnLrZwN^r*=gVG8`u9m{+%ecjl$jmA+1Y?`TfSb zlKMN|!^;C%k*e;lhZ<`J&1L6`nBl`Jm-TnM=}2oE+bmJMZjn&tWQQi$ge-2K0K*Fu zQBRt(jYgj8(1$ss`-u)utIT4G9!1m@P2deYWO8H&u#sM^Ub3g&E%y9db zbKmxl-`7{T6SXki_TMjkVaWe?`}4~iUymL5wEcy8@xtgn<&EVb6=$ylQ?+*fcLKiQ zR1sow|6gdW9XKy0M~K?sFQ>uLir5XHsA3Ek2r^-CfiOG+z=Dd==}f3HXM=%IWpe>w z?Rqj3gur$JuuiTVTM$T?MM2#mQ$rwEU4vl|tRohHvwJW#ED7HD=0Hr7Cjz0WO^pHpg_XCxCJ^`|?02?fy^Vt{Np|PzI{Nb{Zn-Mc-sgx>HC{ zAu_W+-FT*&n=YBRQHs=3%nlKEA&&_?O|BlHSHcKK*Cbsk1sk{78mOB3B5U0#)iU25 z`f8R^Dqx>0AKRvT^p|Otuo^ zZene5c9Z_LSpT9kYqBAM$dF>)?gi^&W`%++HU>OqgYl<3tqHrH%nYm&J%tVy`;taQ z=->^~aX(+jpUyoEl?^G@X(?hF$_L zQ7CBr7y=kmUA`D0ib_ALcEz8#3=$XQ_%PEi#hqeiNac{vWmG2$_^$+uFf6#YM!-nN zId)V*u$!=c7UqxGA28N}&riD?fCs7*0#dRw@nW}_nSbaN!Wb>A!V)GM!T@GYoSR(9 zGoB?kp^doPE4d2pyP!Psa7|V8p-XfT!3g;ZjZQ29?$ZJglgK2IeL~)!%SP3 zm4bVnkG7XcHB)}^cj%=^HD0!5VB{NK4KBoB$lvBCbP8if?9Dl zx4eV`cuiOcn&!=-p2jT#2M1kmSp4C-AL#zR-`sF;-y7M&l!;PySo3?7$bF~O)k8}Cui$EO z{DT)fL3b}oj#9)J3>I;c0n_6Gaee@(DuslxW7|k+nhCX|Tp$9^0x0ves?dA;oO!Dp0x&G2gTB zT{kAe3-^XFcz1)IJJ)MXT$r9G_PuKhhFS;QA%6Jss0O{CTzg!e?F|nkLzHGkjsb&P zls!>oT?BgnT13QIkoryP4hfS^x5Y+ofGXsRf{lqfzS#f>?Jonduk;0CXH7}Gn+{bf zZJG!uGLgogVkFPK2=t|Nm*Q#=7ApO4kTIw6D97qhurACvrsqEM3FwwKZ(iU>Ug)iK zI}xJt3F@hAa(75HQl>)g71w_8QNtap^D}aGQZn=SZdI5GTMvy-F3G2gwSMmpSWDFb zKdYgN#bK|?_QmumsQyZ5m9vrK)3O4#tXY#CSHHxK+<$#uC52}cm|S|pGBz(*emDFY z;)cWif8T-6Esi_-ipc7V#Ib+$+&b^S7qP?tRhT`wM@7_wZL|3t3OPv< zQT?stEMNjNGz%E8)-kKFm^Aq<2K*>F&3YBk&xv}Am-GUA@-{KO;n+;Y9r}^Y^-JQz z6+iCr_d8F_vqVNOK}+_h&f@R;v`0L-otNw%NhJ$s)%9QDc~l{E zLZb9R{SO6p|7gIodvE?E#qVEW#pIX31V$;q#IeI)Wc)@lpU*L6{=wQCaHy|d`X(p8 zc-5Yqf8#+6TjjyqM8&O-v9Hg0VfMYKd%OR^ufN~NlNH|q&gy*8LHzuyLi%!(eer{t zRaO&~^ecYUipR%6Y&tIbkax4YFY*f?#WQDaR?8L@iu?x`ecSP;6C41@)KxYRUCcqB z6%zy1U{T!}MV)3bY4w^Scj+Q>Cq?eiNit_^EYH>@HId{%hRSkziWYf;y)c!VA}2YS zYATr*0oD-}nZi}b2(5Nv0sU+vLv;eW$4E4nn{knxInS?+u7EA#GwIk&a%JYp4Ut5$ z%3iqwrGc6~2`cXa)QknAkL$Pp6*MV+7wNWE={^9$e5V;xmFr=v6|xLwtxk(rN>y*M zs?*sb_;NRGrv6|&0J&X{>MS~xi-Y(uJmZRGB8suU)#%P@QaIePmkr16Hw4^Q^XqB| z0-Gu#0sj*OhucP#FpxBHv%Mfc-Mg`AB`5bahAj@A%r%vw{2xc>9@p~w|KXk6soK^$ zpVm6)Y@H9HZJpN9I_NCxs1hn6IkZ!?&Id^+SqG^klO%+7J}9CP!YYR%B=HHE-@d=U zx4-td@4esm>wZ43OVVKIPKvVLyKPJO%$Ae(!-x0T(EcB>m>zx6tHtK{wyXd~RxAS= zNYC|a>E-!Fxbn;-X#LNDuEPe z+t2b4gcilNONKvaW6az(7De_4-`;tjf@|ei)h19vKqZikjAq>lcYqQlU4 z=0qr4ch;jb0f^FPU435NGppIpubI=or(@|JY9MPz1$?^B8H z!}VU3A2jPp&y!ubnmLQQvmtdDgYK6Us0S&xF;nU*c^UPDAUlyj)Ki5;!eM(v z(1%M|Q>)M|n^I4G;ZOMe&qlLN%V6<5nBKOAcs@*hBAZm6V|y*fQl;o9z&uTG&BkxQ zs;S5>W8jPLfIYf+TUvz!8l+mn0$0mo;lKuB+Y?+biry;~FIfvEQY}qTpf26MWM?Br z3ZRDs>Opwez;I?Jxin~cPytv97gtJpUw~bMRz7OoKI_hQyGnul($Iy{-9+nPBGyZ% z6k`C0IepsBm!%id9L}}g5~&x_+pTRYC&y=^TbXin;Gx9kEf~42zsgKgAbSgJ+~H5+ zleQ!pL-z$h1H7~RZK1xi+yr!%AN@({3^%z8vIEm|I6$A32Te+XB?rSo;gD z)I$rM0#{7iWhN-pJRUg%hNQGYgIl2Go?|Y-2FVo8#rqXWyC9ZJisz#2;wWA0U*PRZ zt$`SOBPC@89t(tI=fAMcN|4RYZDS_5U+OpH`c|GvDhB7BscL5*@vbd1t8PS$$zp0{ zE0raSR4{g%s9!C*Hz_xV4L9j z-{E~Xc;bASjH^nu6xH83eXFQ@N>iE($i$Xx)tN%@XQZa(BW**NSwr@cNj9;JZnOwB z9-tek7ES{=q=Bp;+w2#QV9(NbL{Cvo@r_#tB_N#~#VlWCE19?SqHUf|R+8lEwZ<>? zQfe0lzHMlqjp_PXQe0Fo`v+62yI<9FUoWas$0Gv+MO;Vp`O!gEK6wBL1`DQ5LrA(9 zWn8GIZpgmU-MDE?>vY7TE@NOi#KVPfY(7AxEa294d{tZ02!Sypf`)O9&KmY3xe&yrSRsW2dQo`HpbIOJ8gz0%wr!FI`G zoKU6#_r$HU<)dd%5SlBe!m}cG;#MlOwH?fLe#+Tl3o#Pi>O=;I^!0@Ma*YU&x1-rD zx7k%_C3atn^Qhu~C5YMpL-uudDECFjA&irnS=K+Xyr>0ykjTQ>_I8D{by}49>YhW<&pWl=;|L`<_=hvl7elI{ zPaA6Lo?q%ushv%Hp_Cpvq{bSatDTyxoh${`k}AkO6UHKzz~t@&JXmZV&#~VqkjNgo z3U_>pdf9z4tOrWDHyQS6+2XmSqeI@6mulya!KDt6Vf$RbXJHQp$3@>}{=_O7Vy{<8i6Q9!7-%?=Vm?SkRglXqv)9}cE# zJ@-c64CXX3c+dXkVeu4&!QHRky;6(j%zAg4qs;|w4viu0`RKPNx}|UrKte#(-bZLWoGkTA-6?&; z%)0-L>d>RicqjXXNB<$aD5@eykj|yV;{SNXMJyI{xL7w<+|+-m8t7Z%!vVl-0f>YW zQJpg!d*JO3K+tikfNlPvhx|cIY&a1Y_6JM!1=yVN-+gF)+mL@)gGBeN8!&TySMLFZ zssOuh^Lp5Y1l0wjTx*(38L|X`Yzfd|un)b!=~u9i9JetVUWi-2;g-y$^l>dTpvV72 zSp`COSwVei<)pae!0TKZe8i>#YMUKx-w~Y@0}Cw4a^eHq)IiRBsGQWhg6R?VoPPYP z9&&t4Rp-st;}vH+p)BD|#mua;_raF&W2w7a3p^lMsppfAfM;gRf38dOm3r>BP@!XxR1;UDF0T`yoIRlUB;~r&wHFtB3n(-iEqvt?^ zJcu}P)vQX`R&_bDY2PX4UCZY%QZLBN0jIAzkr8sWi+RuR!xgOO*^_=fI&paT2Ppy5 z{wfnqOS@aJm;YI{9pV0rIH9y51va1PKL+gg`uKa+*rsCqAdf^{E$dE-o2@$k!K*5sWhQs4iCotBwfdp;Se1hKRQTb95p&|B<^WJSPL+*n#g{=B)pPMO{%j#KyH)>T0yDt`TGReE+ZL>3lS$2$9 zc&~>)3SRwOPc#n(tL)1YDDCTD?i4G;aBj@Ly7pzaBB${v|5{JcCt}SfH6r?U1iUw* zAknrnQy+aLc`B3iC|>wUD-=$cp&A`0W^F{+z`@gI;E}t~AYPJTQ{lKZjIo-vXEZ5+ z{>bL^8%bK?(XiwR|M{_urFCuNiJqc=(7x@dpP$L-l<7CQ9j{fS>C7Ez0wVrELNM@A zCYt1o=y~kbS@L!V)69ygM!B%3!CAf@8~MEy$S7TrVajz|t=3I{uA5hU;Q#HlZa8?8 zzG;<3U_wXrtgBo?3r>t(bkXL?A!%|u+W;x|51P#%bX%1?Pq>mN@m?+$`0vEda}cm% zmehYy{xoR7nPguAG=M4o8(;$UZb`PO%kt+z-~2oI=B!&FFKfFkgt)X#i^i4Pb5r-3 z8#%rr=aHModj%y$UYyCZy{%C7OsGS0f+ZYMnFKn+lK!wT-ZjFj;va281pb#za|$?1$CPvkoo@Rk+Y=8PC^PrH(Od(#WI}< zRwcruh+g^%aX25dRpmjYR`j(3)et-asRN%>*0A&W zB^0Bkrq~r9Q zvb0$r+P{EwZ({+K*7NtVr|AJMa9A;}Noz#?M(+aBe1wtLN)oQG<&%b-ExfIk+82Es z)@Dz)ZC?{E7M)#UoR+p;Zl5lyxZ!t0HvRX~>4%OBubgB%daT*Jt0NjUyv|(x{uN1b zlELY+qHl9TYo`hOxY$%7%U71EYTAP#OE9f6F?0%Ie?6TFRVGUqoNz)i0EC*@_Z0-g zU;vW&Fkg~LIk#aBngQQ@6q z2uHZkH_@+$H6WjdUUSpD;iO@w?Fe{u`zv{q4%HseCf?NOCY+~X%(zn&IB6CG zQ5e|cyInh1sdH4GGO-$rmds#nUHZd(IdB5Kr_%jXjd&32M!;W}II+5%6|fdh-uR*v zVR7>k(gzo&XF#PJi_W5yhQevNNSGQQ-|7G`F$(UNtDb_iCX%TVD@v`gRM9dUT*N9s5x~mXYVS#; zl0I~XfcLQQJvx*$nUKn_e#*dFcP4<g)%@}?p9sHJp`BJs>@K0_2pUp<}pQ_?aeCv52R=-RV(ozs$ zAgUPHNu0-d*LV^qlXf3w)5DT?EYB9(POzNy*swgYR*tP;N%~>Rz@b@@tMRlRWpabX#KpU zW=0@a*A(gFAg!&`PZ;JS54SKj7c_5fxHawwfY~J#`BjY1m@!NBcd|TG8be36`CKqO zCdgBd8h5pd+hr8ya?NdXO8(0FH??m|W(`ja6gtbU8@n*muwjCxICH1VN)GyA8oUy2 z9?>+#j?gU40NnDB;0`1MFvIw>2z?9?EGdwpMxWos39g*_>j}itnFB}#7KHF4=3lDP zF5nRX2rZF71y_N5tj){BC($veKZYYsiJh4kfJ6fV5FUwR%L$)AQqLLfK4^UfL}2VS zAcN$%3Qz<5XRQ0_K^=YH+yTFagq`^+Pt?BgE=5Mk?OqJk7j}2YHud8#=)1fyy^Y?* zBwkHzeEBL$E5CPnzrsb4lU5smo3jbPW^RBf$TF$WC148Vmy?F!S*ZyqQMP$t;De@LYNfj200qz@Z`fVWi{6fAS;^E_aFH-~)bs3V^zuf*cbOK-*A$Z|0f zCqq>OhRv;3`{Y}4pvJ+)*eUZjv3G+wgn}ErycRYl6u_2F21x8v{W}>%ay3%~TBzRh z_lujH0!Cb-gSJja@mBAht>r?E;NwzLY+kyO?VPj0F^(^IvfDf@k-`_MQf!0m3%`j_$Qb_+QnIz#c zmHLTjB@kGrUj#K5QjWMx&l#`sr7mi{lfNy5S}?U99@7A;-l{}Jh+J)=bsCi)@`e}n z-3sTYS}NAyZl6WbMTaM(1nq~;edmv;jnup0+>UZM*`)iV+0!m!>f#5+7n9wUwjc<#vM!r#)PxMZw-HXL7% zp^knpc^(@xik>CMiJ0q2eKkWND~w2mc-BQAPo%bg#X|o_HmlhU743F4KA!f03 z)M=Me`Y!zd=-naH7(_U{KJzaae*kXq10V}gGi-hAn8Y!I)TnK8RFvG+ZuxHg^*DdR z@0wjreizW(WUR~Nw5cDWJ63hXTh3~ioHc*Isvdl~X`rf7_3#|Rs=dZ~_kgwc$T>XN z38Y>+WCLNpyE zO?RC<=6OR@>l;t@ue}ehM1lEDQ@a2U)hH!Tl<*FHIMQw?o$T#H_BGq}#LnxUrB}cH znO-#dnLN6mS1@EO--n(6lz2TaL=W?KkG7u!&|+U!p@sUffK8{_T~7i7cZIqz5i7go zoLLNMg=wWaU4Cq6VE<$N2q|2}Vk~uwSA~9Z#jHfvQb~q_?} z``u3s^^6rV{}_D05JbRRLMh|fKYg0mc7XTB9%!|WUoha$EW~Z4#8V)Y`2~?CRc_8R z=*C&TIzt|6L*OTvPz+EW&z?I`Ct;#VlryL}7vSM3$fO7-x~L0j)^!HS2hdBlvr1yK zV3&oZ>gS+ZuaHTSa?ucUrW)e|i{&z1EnTH-i?f7)+=%Q$>|$x1EiztOzh5b~-&{)I zHYnW6A<)Lf>T^J~7K+{bDkT9+0=6_w%OfdqEcsf=wZ1ka1JyPx#YbxUM5NktEp^`+ z^;aB?FW3w&*g^=>;P+irCu&u{Xf?s5sqr!`8<<{YiUhc#7qQ*I`Fjl4tw4DrCMIrH znEjTlUle6H&(2L6uu$U&C<)HMF%$b-RUz=2y1Pj?uK|fDBuVHlxz{IY-I4BU-2;}b zU^5kMd!fh8%VUC^=-!yj2K{p_;ARsDfIB{jTQ{5K^2;09q*mqZ;OA?SKM6{3# zyeT0HuS1$I=!S#zn6Ia4)JdF1_yP8oH0j-6E*LiT$HhiGJn(u?8{2@%)|;3#=CLE! zfSPLWU$?LgjhjFIDv~FF2umzuy53vk2Px82d8Fdn+q(D|z4uc-`&oFJ?jGGpiaZje zk=Fbu!IBq;H~p&GlNzVv z)-TNb%lv)kd|Jm&_qT6KfUF~rxgIAxZ)c+rXi->K@gEWQSOnZ_R1^4!3pLL2yi*QK zYVqg|Mj7{M#Z&cpRQ)8j-ie6m6Cxa*ayy0`fYC?$^=WZ6mbO#z4lSG=`dkGH&Nd$3 z#L$l4%E4U;EZE`}{v5ML$63qf9$~@<0o*ilUZ^WNh9@l&VtVy|M@Fu!G8K~hlxtb3 z^VEL}{donfy0Kvu3C?%+qjHgNa{7QHM(T7&q{_|t`n!)BDKFGQ4ASW@AYZk%PYgH| za4fn&mD#N)EQwlm)kNur78jg$#{5YduJ%PpEgM%A8zpw#2)dq5Gzjq(wgUNg*=9)t zj`)vi?NL`LIal2UX*#NfgHfbM#3m0zxgf)Gx|w8snySCt7H?*C?4wz>dsTT@wQ*xw zIN0{WM@MIE3)g{dw+sio2OLERGjfgqhupftzT7`;e5SFcfu%OX-myE+Y+a57;A{oF z==bJW%|;KldbH`_uXWcrxeF-2#~s6sr1pO5wr{#&c;ET%oXY}Jy1*#1_TkO(hr4OP zJsWeh>KLyCvUeOAp~jLt*rZ;V>(D4(M9CsudwgdCL1)fQzVeek^C0aE5dR&7ClBb5 z150-S3fe>V7X<8TAA$i!qAW&=3Z6yEzc?l~9w7jrd?r>Oh_|IqcCXy;U(pp<=*58a z)D$LPw=dqj7XClrtF+WHWrYUZesWH~#wts1gEFnsoaka=WZbM7z{<%L zVo3dO1wl!;!3EnQ;)YNG!ZwkRT+)09xjyoV?^6)eLkSf5=F1A}!jlI5?`J^!VAN$x zpQA2%i=+L9qihWVl?s=zrVspR)m(TgtlI6Gf;{}gxI;cbb`jS1rp)L%q|bTP=j*7Z zbeGVllI{iQD;XO+f*gX?>IA55{W~k`)nkf+V`M)&yOV;w08qq+%ZllcFNx__5VKPB zHrN5kuA{!(Fn!GK*W1_v7e}>l696a%6zxjUcJ7B!L7Y(fx>#M_i(-0PM_$K!u1Qzd zIDS1UD^zO8M<@GNS^SMORIWn(can4LdQkPG5JN*P*dLJH5FScl5&4vqchg3I|I`P8 z%G}5#9z}I_SS!EuU{fUcO1({&)}a#vs(RlX=>oOGQafgRukdm-4y-zqIN2ms6Vt0J zeMS)fq=;)$rzUcEJ*&w7q!bA>U5Zpytf$loA$%^m?$p8|UX*eR1-RHREBn!Gb0DvM z!FY(K8gQdnN7bC7ibI{gM#*h}<#rkP7vRV8X}Mbu7&h*|N(wx!_zx_R?kJznts%Qv zQWqVY+*K}`iB%j&R8+w&qcxTjJ!(!;4$5olZ$37xq$t)>nr1+zn*&ChV3N)+N2R>W zrnUL^RMTb$9Bs`VPmMW}0;=NYhUXg{=sAweUxIZH-Tb*x7yS74oNkT#dPezLO4Kcm zD1Fa-_mr5J6Xb5Z%Na_^!|R`DWvH9Isj$H{?5L9NY|hd;NH(hOrJ*8(ERR=O+ zY%^*X_Z#E;C3Jk)AX6U0)(}!}eqeRRr)eaw%R4I?>vN3K9=t1w&uKr+Wp%gVN8LI>*n6!$TEmL4^ zk|jsuJ2XfvOcCy-M7uoDJ9jxhAA)+GcM5CpZAgRd%E zE@dK!`ub$RVHV`#_Dt8mD%Kx?;HYQaC;wP40ikjK{oJlBIP|3gK% zl>2-273b*$>d%9uCCIDX(0#i>K{|oK^}Mi@lk7Y&K`k~CB4dpKUcyM^NF15L&jhZbn2`?LtrXSL)~1rA6b08z3y;B2Kx0nDaQ``* zp`gv7cSW{OG1_PnTA@+eKJ}ZfQat}TEQ)tia;`dPe0QU!;2tn%vB2&N;%&*UTRCr! zuiDw2&s?oS=$%}<`JujCJeI4Gapd=KRVIYO`H(fUakGHW@!>34X6wBmG-O(5#Pjcy zz#m)c-#4I4I)w`^YH>^;M8+1=2X#$oVYMkI34uAP2S!;rrsMSJBDP^a8$Q=?%>Ev+gfRBnS+a8Z4CeGEu3=M za-zTt;9PBV>ID9;kxFqwo3FZhT1PAF=)R6$A5@Z#yg8?mP++E49p6;?UUTsx zWqXY21G1A$c+^+wzXnn7eHlsDxbad|5 z7L8-6sbiXb(W&n5kNHS_+~R9L=-3cb?nH<+@Cq4}Kf$-Uwo4cGVXdNPhX{CuK`>4? zM}Na3PeE94|TOiUec`$V~m1=O}8}`Xf=Og^Z+V>}< z3ZLFA+Tuz<{%FtHF8*?Cr{Pktrf+;dE{9K*ZzDs+0Eyy06lPI*wIDd49=sJUp`SM8 zIG`7h1xgSC=ur-3rG?r}&Ni=Op~D1lR2Uti!I(z|7r+h8OZucU_)wQ#KTLX$uL*1( ztU1b~<^@B}N@jCf^Pgn>R6x?)Rj@k_RK=x1v`%KV>64l$cTOi3cztzD61un#l}O?v5P49^(Hb@D%yrJX?E3`|!o1ZnxDssWU%eoA z(f;}tdTw4oGxd!A7aGFQxL$%Q?sE=e80qxn`sfSDi1c0De&czBKfLkk=rkgZ1-f7$ z-rZv0bpb(DTR?@Vu##p*5-~UOG z6F*AQ*oKg@k;G`v8R$s+$c0XcDP89H{aUiQPHHox;DH_9iN1~1Ptr@SEBEsEsVVi@ zjJV1x&l2DXVPP*{E8MuL9<6%x5bgC-Ku`DGpPE^!P76=T*=G7h2a7p#Z*_v~Y{L&o zCjkzq>V2@C%6%Zczr-Y|>gC-ZHxHyw1xI`)M-?V6eNnk1sYy#}dWn#SkwX^Zh_%W; zIE@%BEeWM4`h108cYN{jJAWcJhJ)^YzZ|XJ{b{#7ZB6#{#8=Iqmqx}dAD2D*`Bl>a z_*_$5uL)3?VuDWK{7X88FwkL`IkOKk@`Htp5&@Oe@?jHY54z%o9L)&{sxXMl&g;~d4UH2orhdyz)WawwJ#*(A6(k9)I7w$}?0V;0j@O^L@b0aZCXM z-lS)xKvDedcfPtIOif+!kWyuz%rGqLSCrBo=}*?;F5kn~LeZC1Kg|x0zdSti?z-Ci z>agSLYS8(=(E_gbi%9G~jFoZsRjN_w6mi@2w%GbkTLA^_yvmY}uYi6QC+vEl7utRE z^XJsYnpXu^U)Va!$ecLtq-yB~yK<>1xyhn-f$KSUcD4J)0aC|%s$0gCrcK(|^|F{gDK?=@q26t?En-dND%l$#)-)dYq`W?=4yXVF zN->z>G31Cii0qzagR~eok^wC(x4Hu$XZGz`cH%6YRTa;djBX&Vgx7fp&r<~N;g}a} zf;XG_VtjD6eMrLcye3{VSOr~qZOVtx0D-gu;3?lgYY$^n05p&ZC*(oix57gic<@dL zHN`iNUpLnT-61mSz^dL`80;qoS^@(2LYy;j$I3tml7*DH3@00ss`-eg*q}TnRL7L7 zm#1N8h^QtYyLom+LrBL1^-4&>qeZ+=q=HWerY*}Gq6;IF@f1GTmVA*xh-epjCM`=b zP1p=Fyn}&RCo7aEE>R!1R*vrppI2BR$P6-Qh_pb)wWa^8d{B+056UTA9~a zqip`J*IaYUME7sfhT|ST!~kewB;5Ww!~?@}{ZhQTUi`0j_#c3@)Bb|73gH}elyj;S zNT!cU&iDZ$jdLPI#T25PZ009|cnNoSGqY8qL2)#YJ_-V3N-`7V+X}Smb~e ze%-=88n%yQ4HCgpF!!m^QpZ+vGdLCj(ZE4tXaRFV07vGn=8O9g3)e5+ScSAm)bjznbG;m?FW7oMz`7n=!GOl;KB5A5C|xE8vSgc=F`ER1 zGA-p$EvF6uO2CZx0(JTgz^v%X#sl5f$QV2jlQ{2GN0EF1BOt?iv#>7iXKO*8HW1m9 zG1IpDS>~ml-OC>B0IXyr0taGGG2wBK2As*Sn+u=wyH1!Kkee7SCP5KDdfd^H~_*-#~l-%8N!c5Q4LZg3TjC$X_&F<{coZ1cW($ zttGT&e`OQ{5>bR#jfGb>okd4hcU%s)DZ~5(=s5Z5oEzU0p|w0CY^#)stb1^Yfs1PM zQSFe4evDM>@-dU2j$6-Dj@QCu;{=ti@lzdwf=>POxG$r^tLRH#TAEQ`I;4J@8fm0n zd~E`Y=kIQ!?(gqJp@}jghK!^UAF;*cE8~vFLz6e1-NR(`AoB-ugn;>;tdP?a?y3Fr z@r^GyQt7_bvQJwL4*tNOHaqO|ASyHjo_XLD>G=BR^ggoAr+^H1x_*d&i1wENVT zi8oHJlhbc_t95F@PHVzm5pJjkXIrmm{d>T6ZRe|z9VnyzSO!o_1W}am*7+bWkrQ?i zE(3S8lWL4Ic0RzCYrUIWgw0y3`x2?UcD z{h(k*P$(hrWrBhtv^J0L+f?c;O7V;N0TRms?5u(;TKr7_0Tq0t3J_N&udemrTpa*c zpd0%8C<5=($o>`rz5=lX(8-326$8QL?-t2L^{;ss?*0h-k5F6}{6glsku0>Pa}Wc$ zF1txSvnSC*oQ;6DF(N)aiO7-J^G{c1oPfS7LT#RremktI>8c8=V4!|}w9Jk!3fZDq z@gq_1%J96VN8z;I&#Q(q7eeof^E%g># z4sk0IRi_iTF5K$8e>>SN>6lyNzrLfL*88sICrt+S7LEMp^?i-!dS@X{=IK6Z5nm>Q zrDS}_`g^zaWX{277f*^Vs)Np|gH|-T{+d7gY_W=VPOd2j7c{D;Q;s({5^U_VqL@rj z+p$7%El7;|I0b8?!a6{(10TbB;{vx?rD-wS$Er=djsl5b;L}?WBR<3j(DCUI2v0bc z#e*dXtiu<_Vj6AM>;W(#x%4bVf;(9`1X5s(S3cv_DcZC>yL$144WY~W1m|dJ5Xg%$ z(E%SByn#3^gn#@Fp^JbcHzs}35Ia?`pCut5Bn5Y9qrL@YngMau1PMx#TuqU;o<|r9 zK~xvHh3Fkh>9QLl!u%E4<{9N)igJvkX~c5^QM{beF)`LT?;7JLtxUCJRpZ{r1sMMYlN)N8vD!cHZq4F9xF4x5~pfnMCc;TPz zlfiGnK_&oti3HgK2;woI!2sx7Zn1V>uzr2!BQuR?m!b3W3&v!yEfVpfG}PcZ&b+V8 ztSW4tcY1jV5C2TK$1QFH$ores_&k^8rptDR^APpep4R6`mTVlQf^$Ir!3lY8M{C6V z(igJygs3g;=5lvpWN+Tb>|>SE3X!EDRXN2~A6RN)pu#$#`ejjq@sXOW_n(z6b)$)& zn_w}guOv!1+rz)0&VXs<$Fk16 z&5b_WfAzX~Dly{^^(qf-8SSfb8g6}e181({4~2+}z+EYW zzq$SdMw;UfM=&0!&#}OWAW*zTQsXhr^^VK!)^)rbTH_dwc{uFo_T|&r0VU(@1y}de z0jD@0j%(aUN9BOXRu1d%GLda1U^xYi2oQ|~0H8jM?7T(sQq5FW+v|^4^zAAZapO*I z&lQV2(2z2QG5fsYnRV(Y|*9lmXFDF5Z z3kAwuWjrQ2%j&(wat5h)(@3o2$>A1pH=dVcVv_?*it0$h9c< z+Mc#3mt28)|En1DdS;$DGl;mP_vxu+=w#)5zsr8b ztr53k<{yD`(8^}v1pwdzc#ooG;*8+i!(p`YzXNYqLDn+0c0J7PH7jk4_XG#UcgEDx z3Y5&E%kpGAvM27DU3k`&@O`-O)1;Hl{km5#CRd*9n{7V@cI&QrlEnTL;h)j)wi1i7BS zyLHcwV*vmJSE2)eKnPh_5prbtT}5!ByQKQhRc<~T-(2;sGF;4w17V#@m>>y3 zRZ`z4uq0&ES_Ty==gcPqW7*3W0AWPr`v#EZ$EC&+`_QF^^6Hzhz@Q@`WQcri2^&k( zsUK|dSUI*AX|V1tWyXoO=^67^Kj zObtylc2jH@lFgHq_GUWpGK101BHL$t0<>>{mkFjjAMsZxXjG6zG3Q=CzZ+eByHB?*|UO@fhqhyk2wC^UHkT?btkq7vd#4g^2ZzU`on{&OHq)f^IGgEX}M@sbtCC8pv&Oa*TWy%D?dAKueX%{>a#{F_qXR04GH3!NNfZ)-x znp^9|L3(^Z^2eErRNYJ31g)SgElfU+JOOHQ_btyWH$U@yJTRiIJgVCN-0Iu=e#%nyDKvR;4;^z-F}yW$=9o& z8k5-y1^kXM6OeQ$ncdFjmnBN(ptC=%+sAgL9GwR24l036{547TB=;j_j@l4I_y_d$ zRp04_frhsirMS@gai(rinsXGcVro#2tR=qnNc|JOVO5>lLeJWgcKs4Mf&1B53-h}0 z%Z*0VkX~RAFB2zAyvTD&-PJeu)?lZ$AX!7>EmOgQJAZ3N6|5@a-w6T?92cQ9ZQ`gq z4lcb_{kP<9JE$eIKL72G8%xuI@yPFwFVrZ^%n5K~O}>*mWcgQ>0eqE8Hi-eEP+Up9 zT68uu<}iebonW9(SE@;V?{Ix5HF5p+7;?1vsBlP4gpqn4-z>el9jWZa>wh%^(>K{1 z*%#46Rakp(WAWBo`s8<2_3@G%1E2ACIhC8!34bJoz%yZgBL)O z64TDyJcJLEikFa6v>}H;INtPyN9(dAlkZPU$GfV9+*XhMiJ&*@7qIMK7Y_sqAw$Fd zcD{!}NVTvUy-Fd7NCE~2g`MT6_G=sZ=6c3NT_5$xGV}o6@-C;!!*&fB4Bjz0wRW1n z>Aa+|Ak)n6T2Wqd4;p_N=v@=`Dj4%3SpEPsX)A8GMgD&}mRiWFmvbM;l#Qo(M0`7H zPA&-rA!2;sj{JTtIHh-?{!7|0(Iq)6?8~dkg|&{qAx#Ev0Kj^j8yq^6#vkd^*lx0r6R^-I3^G; zEt%ymnW0y+&?fUSdc0U+iROS%GkIMTNQ_Hdm-#Z~`YAbXmf-ezRTdAz$EV4XAIbg| z$x=7soaSZ!uFAUEvdE8!WXfLlI=6rURhXuo&~w*Z-K!=%yl>IcHC3l(-EwDCpdV3p z7u&KJmQY1asK`#Z`BeW_vmTfa7{Gf*ml+)J^K5u(V7O}70)zSq5YeG-?{VILq|9*WskcPb^&k*9qG!jQ-NN+S|3m=@CnQ!h15#~) zx6&k6X?CZR5tj}dx0E28MWOXuG8PofCsq$k2++4_CKt9~C9}m-Oj;%xrThr_ot+ZQ z_m@DnkCEYQ9lw(zDJBWdW5B_UeiJw9-1>AB0OraN1m}Zi5nG*)QHQ7H<2+tOp zW%G%rZ%ARxP*RWJ>}!(E1)P5dvRqANYaJqEECMl>mI8}YKqVg+h+~kB?;${axw%Y< z4xSA3X$=Ex#!$e9aHDN?7$IpchfSDAY4p=42JEUaXD(geCPjgI1B^Tgkm!LR zlWEAUSVRLjqviCmnj71?dG-r>84u(OIvJ=x*1Ip|D8wf3e&Pf>Sb{5kgxgAyvt%8= zeO7KcYsU`Y36EK)fgAXD7@1f60*QfSeD9vUe1-kMlbi44#TOiZ`NP0*u-&T)=f)I5 zg<#uNS@f_~@Viu8#VLG~l7r|}qHuQ)6MY&etreG9S+9H$pyuqWf*W=Ddp3GwZxk5r z1W8x53v}w9hb4>dYlc>d;lU z_1o{?{r!3D^ZCBtuh;V_N3e}fj*2$xkb8-j^HGuO6L6yIwZcnWH^`~zN!Legd4+=1 z_hf>KCH}jBK(Xc0NLU(zJCTX-?$W*anVb9#Y_SKtl?=9M-%5~%5Jf;^HT}#*E9O@# z4@`O))-Z|YQEF*eQj#8P8@I2|>+mKo&e9NrCK0nd+x0zfsl?Q$g9Noc|5!qO0npZE zh!2`)!~kK)q`?4E&M?p9Co$j6sChV(e%IKqAhRMEXxk#qyaMZZfI9=kSihL5lOQQc z*dL87<|76Fx1emlSt@h*XTy=K!w;rW_nGuDk=&?IW<+#x1TA}kA_>d}Aa00c9tlym zg=W}?2rfl-6(rS9AXK0TIX&|ffF2L8hbuNeM?qM90^^QBFIqw5e+q2yEn5Y81Uh)< zdM+mbin1`f7mX}pp#wz_C%u9b%KW?PILc=*RSyyb1!VPa3f_dUg56t=uOp&_%a3<6Z(123J}w&QJaia*qo zU+97fLJ5FGA;|l*op+*Xs*^2A&?X%jA9LE4{WQ3|->xbZ*^-u=BZ3s9ojRCmt3cV~ zvJS}rl+@x&8tY812u=%Kwl}1f#QrcJkf=srIV(zH*_?sP$;n6MFSB zQZB>_^`k@QDMQPQ=L(L%TjB_^9Qkd@mlNtQJFHx$x^Z1>1#5+k9Sp2j7dI8_CbMep z+mu`90bU#4irroib_Yl+&}R;&F;;3Mc#o_g8lbBaKwKpjr`Lwm)~5ByPVN44`yTtkZU2Y9GlxE6y|Pfp_eEBsrl4K`KMkmbqOq z5ZfxF<7D}OXP$rxSmrT5pb=lnwMKsE48`9&ocO4>x&1I9lK!eM>;cL0fg9Z=u9-6{ zGr~vRUWY@EK~=B7l?k%vSP0n~{~xZhzc6W*O5_yu0PGYrXdMiQgf1~#6pX{I(R`{9 zjduyW_OoS?qkAoKFO6ZN+Z<@V3$lN0aCdYda0axqHo){PNH@ePz@O-GtZiJTZQ@LR ztV^5Q=i}aQTiuTWUm8npFTz1Fde;{3roD;P59eK*X=yVNpW02|>nY~{8BQw=IC$HMg!{vjpS?I-m{e10JE>0y7r?B=gSH5d)P&Q zeEONrydMgWF5U9Hsyv~3E8w2vvs-1=Gf=?@n3~vveWZHa`Zlnv+?D~3xeEPROzm1% zsQiUWlBGop)c>Np>^dtb&z($4)IlGlk=#gDg-Ezjd6Qi!WOs?|#wg)(&OuSGknY^G zRl82g6`ysP%hVo}2)h4ssb2ok9pa37KyiI4!Z*sM!fWvfMe@uN`49AiYwHT9q7<}i zcl{-ET`UCd$(ncfVvQ^h2{qhKoNP9oY*s1UYJukK34!*DeI);k&0+kyWIXwEtbQPx9L05dWaB*CWx+;Q0zr#~!W!i=P(>OAh50`V1P4UR33Ys=_WhauM zwORg>)^fz|Kfc;0pbug#6dqlr69ZX~5ZAVDby>SghqgKkjz-AxeZB@z!GRPy*i3ND zjNhX}-=o_NjC&Ro`ApyFsa4Xs{Ftxq2ud43{)BR7@LcE~Q&s@G)#k}BE7bns$Zh&W zN}(rtKK1jl%t7(DN5PkFCg%r2oHId3pXQYowGl@4IQxN&(4qBL?DEmbJhZLz`t`qL z{WIsb#XJSk=+KV%k~^b29(?T}Tzx!t%f5qgL!LOw#v?ZUDJR|G-Gb3GBbU0~j&7Tf zTfU|AzW=65f~KRaE_I9iI6(F^3l`RX?p5~q<3k7bSd0hmh55yuhjJ;D2`J2Ghq&V}4$TZrpFJue}I*Aj7zFxnRq*zSnU%VKWnC%;-IDiYx#iFUU>% ziy>}&P==Q0tEzw2*)xlZ}Mv7bs1jEI9@pPr5XDg;GPTkod)VkfFG_ zgnvsKZlW4TZ_R3-+kUeh?rVqc7?ZiTf4rVhtA>ErH-7cN}ebyUA~ z6tv)Nd+y4JTS$A|GsqryJFY)SO%y_8=XVSg(f?zfLh&Km9a-@iYo`ethP zn}-u`c<%BS?UrX>oO@OICpOnH!EF3Z{CHvw^u%FRUG%07xm{oMdUj_x>q0m;_sLIG zgjVkg2`f^%4|ADFq0OtAENcTS(np2tILWQki$i0M=|?no9&ZwbS6IhQclLIrkE(NJ zUg^j8yVkqqMB%EP%lzJW6uk=yy&2Rd9Yb1P|8sNb)4#VW>^uIn+IY{>YbR@!w>0?2 zJc=9=P+#88Y48nyARRv1jLlldzkfY)Q}uauPxAWfTXA>Hw6@2GJ-hYm#rP}7Homjw$CuzJ0h;M7&y@fzzcOUqLUV@Z{8J0|?*R;XUn}2un#lEC**1##>pRcY@1b zv0zLKsQsyM`7lQjp!aM13lw_Jl_8`6U-uw4<fsPe;3CxXUubTYIZk~uO) z+4$bW`e{fcw0i52{dg*O)veMt%POXz-i@zmR}O-@XUPoaZ+Uq6z&{t>{kgIOP8R-~ z>`&n!b%UkZJQdLx3#`Hi^ny?RIxJkg;3?=z0SW0KB|kb%W*6T5I>yTeNG$U6alhgc z80_m(91vw6t)k&HQO`HNVC!5M66AicEIK&fr6eROgjlju-!lsB_;WLrPu12NK)bfC zhx7353@(4?7ZErgAAy)pBeYVM@^)@+rsnHNScnDqmgV0Bo_abC#4|>pUh~+V0{G&M zhYxb~)X z2+ECAo9SvY5miXnmY1?wZECudGMyA%usn;MS0eKqa*0d9Sw+$d>C>-P{QD>Hh!!!ow6+?^N5;llJ0%FJl&&Ud0SFTRZf=8>Wbbrvby_bK}&j^S=BhT`Ovk*+12v0 z*p`Mf)r?1(HEmsQTiOH-KN_2AYQx=13wd@%xT2#!4%HNr|H8{6Z8BO8#bhIgj9WVw zvi1luDyJoUqV^&8i`%FDa#945T87~d-PdbXA8gje2E?WN;R;I!tz0ehGDo9~{TuO7 z>08Zkzy{|in%NfW+n3SVm#BScV@Tx#xZ9bnPEFk%x(~j zT#e{81|h^bKr~+f)fDa+v!Enn1+2FWChs33QAPn8Y9v)dy{q98Eu~n|R;Ft!PTE(g zx9}aMVb7ABl|lhrSEjB?E9H%GK(|K-isq9orGS0`TyYLyD`pBtl~=iqI>#kO3Z02W zIU1Tn2qTo7DTV5wfn34>emDD3Hk!*aprQK|czkS4r+X6x$VR+Xzf@O`fk}Z5kP&gl4h8L~h z5M7}j497WXtne=$K=tEx?09H*0cKUBoWpc;nw{)Z%q?>MAmN-+9(}f&Z{2oYPpuO@ zpf)gc`$m;_d+gbeS8K34UA+m1Pc8OEe0~mn@FNHAfYzK3vz?H$XMy#^C{%>VR?RK< zXbc^^Z1p|+=V>4L8h@vED8KE+o#Xm{@ot9??1EVg=O!!kR_)q(`K*IW(?a+h39rQ1fF@plKRmJb&ShY66Qv&~Tt8sdq_`6}yJ!}f z`4Bi!t8zTpT;p9bV}~xOw`Xn8JUQCkwkMxjf*%@`WX|9(qo`_Ou0t|qy@dxO1U?hl zLspIMW3i#1%b!`XfV*6u<)3P*c>UWVcc&%=u-wEV>1^b_7y6q?v&{KAlvo?(1`?XMj3>HGX z=s>V(C`bmN3%r1frk)`|-KDRq1~G3JpNT1e4EmdwxqBb|gtaJ_GI_*Ju501SO&2z5+qoaEuA6=;)F0aj; zn*)3oL#eU>*FL4}V=zFG-q+pkwne?VS}i|q0{2mxs-I?3x-Ut3?nBe~3x2{VL@$G{@R#wQ?T-}`S9(50X zMC7W_WhsrIHx0jYhZTbK4oMOIaLRhy+6#Q@5zB(_AusrVdWbg&2MD%uYcg7 znKCy7olZ#Qxb$ddhw3rPp!oK~(hip{j!79XUIV0#W6O($qD?@Ak;;^tTy9GV-A-BG z>BdXOjrElrR9Po5Id?;_)$--d6+pdCY%XM-PxRpExv`bdTkUG8&I>p6cp$gnn;v|K z8>4R%T_-~ayp!10{Tjacr>R$xsP`7fGR5Xm)jMC>0#v;KU>08{xK_uRjoqY@P^=WR zg2#Klf<>NJ1!wzG=k7Xve}wKt00E~EGIOQVZ{ME zrQR%7YHdDSqpJ75DMjk?7LYE+sv!At7jHK1qpBQ*^vdFQW!_W8FxVJ|T>cGJEQKRc zo>n>GUZm^Bxj?wYjt8Rf%4cSpUQCmo3t0-&0JSqLm03Vl-7dmPcX}qSzK<(-3At!= z7u&2~{~mXxBF%y9zEcc#1aPSg_j^v@?ssh#!KqgtL-%h&t2WA-rqi!$l(&1TUuoY0 zai-k=34ibtc94}uA!~H{YIL7ce^L=S>+X=ws4B&JOugh~b@4Xy?@zz1N?EVU z6FSUKI7~0zx7o~i3wj_fyqJ=laVoL8(-IB}co1(3zs-B_$ zH|sb{_C#=oqGzVi)&gRZ(XqFqn9Lnaq^QQB2HhY|r3KQRyN(d}a02BCl#^fUP(byn za0r+%x$`vYOOmaO5x`+q&NWr->l-g*2=lUEq@{r78)ha3ddBV(}tw#qL zGB^g0e08kY2sGWcwU&5y!jQ$$z2YRzwR#Tsdwy~Xl-Q=x(E|sW>HZ|HVKCcJ#PydZ zQ5orkkhGBXR2l`cm&T>7rxtOu%_dU$`A{1F_IOtzM%N~^FnhN%CWr|QSxGg6JO`zTTIJ!)B8OP|1uN+~oTY3*`Jj*TfH~}r3GPH$wFiY!Zxn9%5 zIcvjt1Eu^grGCS>-DhxxbKs&Su!MB_Zb`U?H7RvEUrP$k+cBMXgOhbDOQ!nx)jQkE zuOK{XoGogXt|lg?*B^g$rRZK$eZ{4BJMMd$-07{jI`F8?%RaIuP+=3GhyzX(^x&F$ zOvId9*{LaskH-r=?`E6k0KDcxtBQD9&)s)iCL(6~?l1WA`scK!ckl)@tKXbSor$~l zFkrMNEbC@6JiTeO+wH-zVXqUzv&Y=v(AbOXnCoTy%y*Bg<0x?Sd-Bvd^BU&fI*QE4 z)a!L!*TWfzd(pgE`^>qyvD?R?QJZk+FZjGY?B_k1m?qfTdzoYgVmW^NZ2lKMo@^~9br)tcg1LWxY56ZK-44sc4nzf^&$Ye z8@x^wo4Tctz7wpE2rq`75P>INZMlbbrU8j8&=-21{0Rl4M9vQu4f9n2d+YUkZ+XBB zO{VN?ygJ=V!))# z-OgZJ9Bf`3bJ=s^14MfK{76s-D`@gqX?EF*!p;{v0nx>Z(IRk_CC}Zay)0}!Eu59! z_7qn2`z|+d`m&cr#);`Ol+4ro>3UQ}1JYOPs#XpuVS^6tPF& zJKHEATQLzhsK!xa%uGH4YOjDwzrOrs2TO0Cng8LL%DYy`&;mK#s~&8d{9&3UDRL_F?wAi%$pi`uh>>QqtZ|Z}`(n zqQ6|6gxZjc? zhE^Qy;yS&)*S$6q03>~DXRe-5UY{QSe=oU6B}!JPW9p z3{>R-)q`&96=qTLMJ|o0U`!b*0^|CTm$0G|tK%XW%CjB1P4)|`2}=?BzZTA8nzvQk9zQNO zv(gwi!iouLI*_nv(?B{Cc8Qf~)W5My#f@XggXm0@x|SNsQdDv@mg3$#=lyP00o};7 zn(-uZ41-ILy0o}e>+hCIT)LyYmX~sB_MH8iR76#?M<2BVI9!l?cu_X6J?P|{%QGX@ zMbY&gZ%C@BGlGnymGmG4xBbT((^Kx@2P^JX>?m#aX!Tl-{s_wd;1M0uBwKoLQ9C?- z{GevL{gDT0-y=<5w0IT@OpV^oyf#J*hFyE8IV-L`#9Pr4cS|F^QfBJtX8z@SmZzPOMzJHn{Ui6m#kv^Coeu(*=IJI6dfsdA31TOcV2N&e(JmQhIOCEsbrzb2(Ao zXC4byD$F8Aq6~+MP(pCd})dv8w&V4gKA& zO~TLdnm7bop1IMiz3aM{o8G;~=We~fmI$x9F;iO04P8CAuM62VbeSgWY~USmlT_4n@`_i9Z;+IJpERPc+tBw z4RA31;DPiITL@lCLuLX)OUbfupsoHv0&=+ zgDG9Kp;O2<)x>RU-{ucn79F`4erM0N6P#G(zJQwTT$+IE*0i>H(&*>V5sVu!YY$sz zNgjf8GJKr#`#3-B$HIm8bq@_oE#ym3lyxXw5{MWkz?mwU7uTQY7}@W<6nTPG;h~xo zUZflhauabITk@UUKxgYnxxb31_9dxu9zMmFI3ISKpp=4hA(tzX@-EkF+==oR_4~8- z)NxABd`;51(;_`u_Fbu~=ASC|rP|Tog!9sghdy1-k3O;q-)M~9x$SSEAR`42KSD{j zpZ@gylt+_QYV$cM#opw?t%|Li_xiSce)Nc=4M1p^Qy8RVf>XMJzH1kA1thHvT1Zo# zey-k?JMM&O{W`VwT+1$Lsx`zZeY8Bqc+ZXacc0#PT3GHbX`NYKuBb?u?mWG?EGJ5# z{3|T`Jf#1mz$&dTW_7u_!iL;<_t=YTlBylW|FyJ4Jv{uXyM1*k%^~B%+O5vmw!pZ! zgoKx|&)aM64VBDV#Y{GM*QM{d{`$e`Iz=U$HS$7pxFjv}^AWupkxvYqQokvbTSWG9 z|6H_bo4pmk+R*29=h~}(j;&7LFFf>Q|AX_tB|knl-mrN$XMEo}a>#>tIfaYT+Rk0E z{j|ixSZs?TP3XGrIVUa+|KpM&r)8hKDg3kJ6iUO!o^;?fFcpllilRIL06AdT^3!bq zon8(Cqwh?j;MpK3gp6lH<=v!&PAvVvdAM!q6bSD;j=_2*>ycE-v-0SHQYKjhq1jw~p?b6G0__L`x8f&YaCq+zKu28uCs=2HL&V=_?2-L_^x(G^$2k#WW6b1|M)8<4_0 zXIdt)f_F5_SQ`9%WYkz+D^uGzs_Q|z3AyXFHUCDfyYd!$JV=%n;9E)Hkv|#frlB&1 z6|4tDXgR0fEFPghFdV9vzG>lUa&_PaoR9;cf{~i%q`G<+G=YnD#!mFXWp4dH+8Mmj zs_gh1z(qTW5=|BRK^>+YgEs(uH0f+p1lDf_IO`QPX=Li{Ht~hX>hl@E`=4qPugiFv z}s!jy}#!+quFPcJIVf!~}a*$6@vV69yMS6$YfM&W?ICIo-zhc=) zV!LIr1&qw~Ma9q;O>4_uGwF5eCx!ji?fKpb@y9e)Z*jLh-puj%O|D!Z?Xnw<89yd9 zew#pgVV0#px%NowkI9Fk?`q_o))up1WLAX7vEAoXTy-+M1t6uBTBI>r0MTawY>d$y z75xtDmJFays<59#w){yNrpb^ZGd@k%fcLpUeG<9)ex0={_nF?tWJxb%__(V!L5hZi zVD#Dg^@MphXSWDYAs%?8{q%y;-qnE@51wcG9*n@`(fg446oaib?(Xi%N|h06jdnB0 z-M#`&HEG_ELNHctCKZWjidl{hGJa#ht|q_NxhmvBL=ba;fSTa#`2z^5ju=pomh1Y?FPHc*T`B-Ut~7T zjQJ2XN1Fy$tX|kXIe!f$6)~uG3GZ5Eo83CJsTmj8xP4~q@X3yQ`#u&7Y0CtE*`u{0 z4Tb#z;cyD4ai>wAD-AINwdO=aH7QX?uP}wrzWZ;allv^XxZpSVoD(ohLoB*q&Um3e zy%+3TuKT)ajVt$;^6$YBKgSzOPcE#wW&D-gAY1}eLrnn#>H8LRRUtoJ%%%nD;J<_% zCZ!#h7J`HlzRQf*M`~!z4Z+!p*@}L8rZd4A2YW5cON8X~31W_XW`F+@w$awn3u1d^ zF;!PPAEt+Ua`Nuyst@{}_DdE|uve!6aJT#{`3OExMKq{XiO)8q^db!sce!YLlC2>tmW<;@!qd9jYXGAsL_Jzx8Cuz{3N0rirOPyGUrD%}WIFWbKCrr!$S4^2)k zXQK4M_v_pblK?K;3(9K~YdyJRg}L9jhLn*NlM@`JV<%}c_1OZTi2&qw6oe5Mfqb3R zpf|l}qhC%K8apo>VyRD=H%nTO01qnW$P-VY(egOdX z5N-8fF4XulXo^0GKsj4RmdaCr(1;Gg;=+-1UZ2uORJ60MxE}6=_Aun?RJl?d6P*!5 zF=sl8URgE%myf#;m4z;5XI%AtX)9aubIL&KCR7WihBgB-Y4lojoA$E7>OO?A2^;Zp ze09)A`?$cvo1ibMLWH*aL@S3QbAsO+JV-~HuywB;s@P(D<8YSWob~nnZ{NTCDa!cg zIY;Go`{0&IiA08swOtBY85jTX2`Ek@+V{i&0Q0K!q?O{S_+9WX zIIPQr1Wfk@GlfuQ5w!E+S>dh=0a7?@xlFyVNsTIFH3P{;mWPCJ^woyF6faR(!@dAC zs0ISb24;tFHsR`5$}(7PWmN!7>9D?YxIVg|iojNMRx36_5Rt5!zyj^0c^0BvpjfUJ z!b3K(Fccs@5LB0hskfUoJnn9gNV^iC3Xy1#)PHq_!6!m7XDyKmDLhcHd%k%Pzt{e(S`F!2` z&-qbxQ`T_I+#|BwBww(D@P9Pym^DqQRrvVNVs${RQ&P^j_tSd_Py3LHi4Y+?ObA<9 zboQyA0$iLla(3cf>}eG$O}?CFO}Gt@l~1 zdd|!#iBE`LZXI6;-@iN&v^MRdu;jB64LAbb_pIpkku=Jxq^(;0cfXcNUX*747BNSY zta{@9CBG@{Z&TWba@gV82FGg#d@3OtOWX*Lw1BQ&NA4cqCFf%a3MY-0HGYGCb>{id zRm1z71H%R#JNtLUr|t0iH@NlS*RKkHX9o8+LD_=_@fR*9Xw%un2lhjT4xda-#pT^z)zpx7q{ zD5z;%?wjc&ZH;h_c#4e|298U2l!F@lCNC9CAY@X@t?*28u>!hs+ignr)5$9R#oy z%h8EHr1zWE!SHlc?4^u(?zo@rM`SkAC;jO z!K{S*gu0SiQc*PJoIOBhCkq}-+1eKXYZ5B0An!Q?mAK_dRCs?e668!tn|Xh97zS%aMbMH4EPP9w2EL^@S5fbxVwBOdPC_h!z? zg{Egu#k2}dXGbsyplsOQpb*MrJm3+d$oVd6Jn9)D8?ZIHnBGQFvgZ(4G^1>q)@1lI z??8_%5a~E={7vA?r7*9oSJQt2y=7Fw)41wrsMZq490f-H$g|Oxrw0Z3n)&!zM*DBS z;-B9C+AdlgZTPjQ-%&^lYLS1NcxBI-E5V1~1Rs1I|E^VKouV)*e4E6YY$v*&hD+V` zlS9!PP1Q?APq+!G7G0nN4M6wb#8YKlFVu2o4*P4=httr5r<8SelMj6A1!{@dQ8uc( zvHyN==kAs?4S4_No_#mOq+Q+-?;;i5{0+POr{3u=Qz!nY4*P`kpokJ)H|$+$0KbE# zx=2xlrHS%~yl{p`{eu4}%S(`{>55KPbf@Fj`v3YW1a~>dhrJb#_1k+5o;+!&+v=c=YcF_O1k-F>tuSM2)i=DR6QM_yL16=!aOVf=vJA6pW-T4pO zd`CtDZbXbau0+b>ss1jUfJ}~oS0BX;cr~z=_FRLg$??DM+0!ZXt9mcflYO@>?vA^@ zgM0ms4wfwIqU>DUqb`38y;pxTv)6UDDBGLwQ{1~eH)Rz6!m71@=i1zqq;CcpWNN=# zvoyi%3x`YrTPLnPoP9;dyzw=Qwk?XcB@+X#nLPH(+tx{wf{9g*M>8{@?>U+~TCEcA zy?=RSZTuwl!)7amB@FcgxVp>1_|MLy3nxEOo<3r`YJ#Uc+BE5H8&|mQ0eR0|RbQO^ z@YQ6{ooo8p^g!f|-TsvdY80t#WY4Kv+^EBybm!ba?w4292Va+`{$1Jq^^K|+BocFA ztfb5HuMYwU(_m{{D%9|hKFn)CC$fnP^M-A63p&;^V2*NnTMgaLGLy>;_AD~{SabMa zB3|Ko@Oz09n1(;@1wTFeQI;nRP7t5bZTs)=F6wXG(<0&h2TE|sKaXXH7`vM1e-<=h zovG6P1K9sR!1QqOjNXy%NLc*h{W)3a%f_~XBR^){AeF3)dIbAZcdMbUM>0~3DTH?UE$uP}il%uoqh>{~TeEoE5POWX=-k>(h z%@xm%rP0f0%#m}Cdnl{{TM8_`$nSj7?LV5-SK0QjyvAFF8-7#gRpJy|;KPYYP*e?w z1UQ#1hH~-93+BkFQ@svU?72m3&&Dqps&|P|Fux(WKnJAsOlaRBdZ#gzzwT`x-&HaQ z_xc(@Fpt|2ebw!ex<08(Zw*O&9;TmNdk+unuD0#i)TBl~HdM8s#tY%Ri^0#fP#lt&YvA| zL_f1=zo4!DSK4{#k{fGi=D`HKgS`*Qa&6 zFJSOf*`*WRyS=-(hgO|E?0o|3mjf%89nVQz{tAwNd-DRISjv{RzGn}He%bTG;_%}3 zU<(R89O2}%2c6_|Y4;I&TKKhSO)zOXppCMta9dn6kM z$>uA>Tcc3iU^d|Fr^EjpWj#e)ztdbXK83+zm6gqbN+eq;N1sUV%i=L38*MQ4lvckG zOt~H;Sq;5^lyf-pyu!jpXgkDe4ij`cr_w#wa%9{1t$n+W{KJ_4oqO)b8D1{mUgCj? zHWWfC=L?tJ^$}#|?tRk5i6Q^CRtw@Dg`MfQ;cR3c@8|`K!^Zy`5WjfxsYQeyh2wvk zET{yP+w^vjrW|dGbXMj_w2CUpN1&h;v}Mkt{DyFQccFvb|6KVHnnpX26#-%I28r#lPTr zd!E@s{C>my--BSiPB#4R=olv}RbqML|B)vYo|0{@*fABZXb{lF z5ARQX&7W87OnWR*GgE#NJjKa>>}sWAQL9*{=&fF;^W?a1_vA%YQ}dgfLPhWZ46a}q z&@S~s!cf}w=T^v_PCT4#a-vb4L2hN}ut4otT3l{Efv7ble%2FbR_#3PI)?SMU zQSF~?2Ccyz39oxBrqd6HZ9B4ZQu*;W#-29Wcd&y`Z?*2||1jGf_gxx9J^B85bD{l@ zx$gK&<_Dg}w}o2AR#`{hJD77k=IL{*j3HO=P%Ei)Ls`SMwKP7wFa6E<2Re!t^|0Du z@!OG?Hca0%pscHjx^?`M7rk2^ug~?J|MUAt#Yg`B8Cr%s0h71#Z}R6%k%ndGN>(1h zI#$H9h=3q|Np#j1s)W@%h!UoX2EC8TjD~7b#-b5J!Y-Qn7F&^X^kdlY(JVc8~XSsj^OeWp* zWO)J(HkY9*Vn5@8#x!!eafN>Y1Md6no1D9Hq%(XWq)K2( z_(8RxgGLaZGFc3Ogrxx%GL5T)X*}I7Wm>OcH8UcU$R~l)Fec(okc_Ua0Pug@02`_+ z5OQ^16jL||m0%$|<>v=g(C)3$X(|N8NOA$aK5=Q#T&G%A5nw|rY;P0(r25w7@(ABJg0v``GXim^aMbuUB`>|HM(oj3BMY3|cb@)LUnhcAM` zk|znxS4ws)@fPK5SH;lMi2537@RgCLb@B-F`k#u#2R~qyuTH}aHQ6Y2!_5yobBW+c zL36ZjmSyY%ymOD&hNhk+-v5X=-y&~h$ga=<{VX{bd{MEi}tMZn? z;zPMV;FoqRh&{~P5MWP}Mb*BNPF9-fVM4#Um~$xwV!%#{>1RkjIsQq4k`p#FGg`5D2|Q|cxYTqb-;1a1 z-_Q`-*Vzu)jgwgf)N?!d_Q{Tnjps=c`4c9P9kW2j24bUXy)@(E0(H0UR6p+_m=IW+nicoPReeij0V|1d(U+uju0S(`raQSGfLJ5(^fhRD9(BCFgo8qH#zj z(oHl8!6r5UQJH{ay8a+6dX!Oa1bl$WN1Y|y8b^0O$KVkWK~tyQilBKT)kF2^hz!5x>Ze!Q~he-Ine;2wcS(& zRfK3T9#|y#@+7WS!+{S&UANwy$c|pCQKW4Oy~%X^5o>mxr(54HC5YMe{KsJ{M-8E< z4Sd1%Ex3Vu)o4L5yVp9ALUXSnldzuB((NCyyy& zg-vP0yS?ckOfD0ByU+TXevvHHMNi}-9r7k90}%nqAat;p-6hu zd`@;;t=cA;&zVaDYu=T8c&uiNJcqZa(|2#-gqCGqBFX7=C}aO;3MbzznJug1i~QPp z>i$|%_*vtaOwUe!+JA%MTH520?GvilS}YN^P0UsqrKfLp_u(EUrX7AGaO`Mh%Y|)Z z{T<@GO09&N#34O{3$6Amh=ABTGSknsI%y8L?evs|Y5d0+hKe_I zTg74TA5A?_0a%sMvcvbQ+m&NoSt(e?8xsZVe){@Xc(e|a!TJAL!QVGI2xvvGZodZYQ-5C|x8;z{r_CU?L zoZSyGatrV3cOOg24?3GMbr|{VQ~1%dtb{kVsz1k1f4nV_)p|Fz4%@!8pgO=BvTE|3 zo6-8N(|z-gWAekWLX9dSf)wm5jnca`$_Nubjc1^ty zMb6{9D8d7i(H4RRvsFtsQcKh-$$mR_n>0QS&R)q@oYRBt$3wo=8Q9=asS%K17L3V) z#@05O0QQtH^3LR9v>PWq;^Y+{Dy_9ES=L}Qfd#kqf!W+kj|RM!i6GwCv}iRP#V8dm zJxg|f?v+^-`vKBm`r_nRalNyj#F+T(f_Ev`2xK7qWk6AK%JeBRE%O?=ROt->;oVOz zK}xyh`t`HgaxV>XUSgY_G^x$4^)C$YRihaP(D;KN-yEqHDvJ3}hcg$%39kV@!7Wmn zUifACd1Mv~Q6NBmj0uK(tPXpFR!Z@TYd=G+Xy+luvs2Kbh27y#E0LtVFAqPFC z(2N6JNBr&64jo-yo__aUAP>TeS2-K2Vwe5t=}mo)KyHK8(pQ1y6O8(e>*`VUJF^x= z(3?wHA3x^CyNM;aTn6bX{-Qb%@p}1*v>!^LEQI1WIG^Ut#QuI1WW4|*4fQ&QZ!tV_ zi&W!_uelB|3be}mykGW&YD1q z(dj-CzPhAlm;tt_C1e~Y!IJt3t3tV92C5G)pGe?I!g)NVhJ;|u>Q!xNJ#lRwFAjg) z<)tm5Zhzg2>}}n81E?_xv=k(Qw7!rVQ)RA-WbPvzR^t&&F?16Q`Duds>m>>kKo4Ir z01V^?0TMS6d@)+dIAP0)0PMGmH;xrI zh<(obV3u3{RjDG@e4ZDG8&nMXF>u#mS8WUuU z{g#70-f03{Pacf$DEQU$@qKdNRl$h}P2m9#zxQ|lN6~pkCHb&%_+f(+5%1-7f7^;Ukzq<6{Bmm;_r722{7czk^nZd(WO+$@^J^~U^U(8N?`CEKGU3OMSW$Onw zNB5Z$XE%hdrlBW0Y0kez`%^;S*eHmr_oW_1&~pcj#X%DmeWq= zDFHbOGTQ~6)fzl0#DE=AzQ6UwEVEwmqp|gmFG6u6V(|v}OLRPRL6WwHJ1{DZEhlah z*K{cw~Y{6XEUBUp<7=7F=Bdw@6qo6zfXB8su_SdDs- zv7^5B72ZBa{rKo;+7)y_fAEl9H;sy#*b9)ZzjpW))?*Fprie8GVJ_iNK7fOfooA9# zs2kw$br8CqcWEj|xu1_7?CR%vzv?wqQPObPOXzhPDqI?P&mAmLAsRvCX&`yFYAGNC zaom=j|G-0O6MhDkvcNtGhxyi-|zUkvvt$XCDS zE%&39NRy(DW;IUcZEO|8l01B7+}{XfQXQ$mFsJV9GGqXui0cx&ouRHT9%)29?7~Ji z;Y960Vs7F ztVMkjP8#d?fXyKpj5}Z*G?HO49k2W}O)Yg?>xUse)d>I8$Y|#Tai6J6>pfCqhv%qq zGhH!1_`r?Olz$@v3+$9b#N6`wbC>7@K^w`YdTA~(O)!L5)Rnddl2)HnPEdT6-0GcuK4$ zY~IWMqaEx;Y}Y_7DM{r?BFd$bo1C?Rt;$^zDUt(&WD?F zZ#=x}M*W~d?DJallF-BS*b%R;=JzLD_Ir+zRt%K(`M7uO(~BYBI{3wmEWUj6lJ`rm zaRe~FjB3O_e$}$%%Q#KKo|$?dcj`V9cG9IvQ$fNUfbNz`vF%%h6ic(!ac-dtoh6(e zd*ynJUYMv>Fkks9^d%1lv0CnKe{4D*#J*bbgil?sSse^LRy+vGg})oVR%vbZY1Xc! z1itRKy7F~$+kUEX@OPsV--=oBM~~GV+%=v65Q#L4jmhde)Q5c7bzl*xG2c@BHN2N; z4_Q45_F$vb>P21Z>$s`qoV7F*CvEPMh_saAe;upyQfmcp9#fMgyi54IgF{1!=moY) zSBT&$^&4$bvT2Q#HxP0DfuhRIA1c3Z-T^gAtlhfmqpWkjv0Yqnoe*7H(r8^{JZh#E z-VoFF%_GDjAmS$faf_B-YRl zAp&Tmg^Y$?S3$-l3B$z^*(S1)(&$87jMcu-dwLt@vm^I@$=&nYz+=Jiocgp$oIr>% zFXbmyu6Uh38h3U?B75yRL`J6OLRS$*ejU`kLhP2hbyBg>a$_xTw|-HI z0lrCcT~}GmKkb$s>soNyUrcf+y|%p05u=J1PyguHaJmxu4Qao0^N{K1Y(hQ@^$%gZtr?M`+2vYxTKlkiUP>Rhxhi@n*6*dX? z1vQK#(72#37BRz)AFXg8ZIw1Mn!_)l!^8zE+S(!E0xpF5Feg>_W>U446kqujGB0CF zr88h6IGd<>S;h%6Q^Bt*Xt{^%-ayR2>aHikU3uO8#f}8qdqVouiKf`c*{0e)$6s<2 zwtwk&EFE8EXNl(cbnxP$&u^aP|8ZpK_*9hC3P<(&%i(~&t?izDk6TE&1=j-NRvwKb)CV^68jWWg&`&}^#%QeySJBcjCW^BAPW@_kc=tJ#z37B|cH zefNqjKQHkpsDeVMr;7H{dE_!Dr4XLzNriz_E~JW#p-pFZY$D}i*stKSXMSEGiKo#< zcyUYun4fGVlBHbwfBieF$}3+prP5|f;}nCVu5sy4^?g+)fllHRbK&yvrcfkXqY-L# zQ=&-yIWLpJLYglr-b%U-sa} z5eX7^H^&WwW*HKFK-C>w7MF3141w+26CTqD&nSKi4ojQOkQ$~shtCTf)39k4Oaq{b zTeL`C{B&^=ByoN3o?*Br|MS&m!G*Uo(vw~6RY^o%2(QImH=3Ppbx+gttDDRzQM)ay zyTx&uZR)UXQ);fe)wP?2`BK>H$29OoXvxG(LKrEax`FxxMs!k2&~ zQ|eLoltHhf-t>!=e+c_t{zkNH$CyXHgo^PX6iQvRo0|Dis<)ql{hXe#stHF>RX*mP zPg+kj_j)-rVx1JSOc60f(MOb*MQ!eN^PVpyBoK_r2Re_*)uX)dzA80LF#p$1Wds*r zw`~H@ZxdOd@|6I%kF5@h zJ1C=U-%_wt9THb(OWIBnVgDD&l)Sf9oKodr^$Fd3lH1*QB4R^!JFl_ms(X?7;l|=+ za!&EAAqzB9Dowryk$Z-NqppC2`_o`LLG&$wNTeitYg|8Hu_3(*X{S}wR1nf6l=J~1 zV~+V^`Jz-w#opx((oXboY*zF!ey0G4?!>2kR(x%1>(XKGI@AG3;7M=;Dk z%4d0ywMA0LkK_@&LonxQnaXuc3MyW-v+>vKM$XALmy1sVqPGXy2&Qz=!N;!Wt29*} zo{c+oAcXIRh0k?Pvb*Nwtra)i7L;=qI%G$y6Wad4?dM}|I~zXmcR*hd&DKOEyY^x- zJQnNi^LhDd92mJ54!ZNKlWGGxs(FIs`pBnecCP8fW&WRsE>JQtB%hv({e%yh_doj? zuK9{}#m&SH%({Jh0QlV68xT^ZpVnsAHG32Qi|gAC^?g9S*xgUcZKW4917;_*eJflu|=csO1UGF*OZMP_xmBd;=1v#aja zWIKLw=DU)(#_)D?;PdgeLlAKS=#9rM-+K}um;@zCnFl^~#VUQ!CCEkSje>ZJ7bJm2 zM;@R!>z!5mod1}XY`jW?P2K@(_}Qp%4TuIA)hbxY$C8);D#-W{OYz8VnP6;C&9`v? zK6QaEcq$%d2jz$l48rt2>>R-_C`#OsgqyIsi(9(x=%5 zQQ%guLkZ%f1l-X(pJhV2&c)A#h0K!X>2h-kq-es?!Fomv^C)Rk4uB&L!R3#r*}odG z{}6Bgk!>FdcZeNONHjZ^R4Ejdc?_gMhrols2oa^V;S3h+4 z!d*t~V=l$V%q5}Dqfy^fV3`bXZjW2cH;qD$tB0~%b&n=%J+^w#XAch3@{BX?H`30C zduy-#AzS-;nb8-l_D?U3(4*j=*ot>0PY)8 zt?J$va|1|x17HA?adoyfrWj&3E+vX^uC{(V(>bDOw>uRM9~CiU@cL0Wm;B zjN&1@RaOBGDF)`hPCc7KK*XcPZsP>TsnjpsV$Yb=XI<1tWOWpX-;ORp7P0w9B?3q( z>2gV~uVhZCWWHiedVPqFkCY?}L<>Ds7i=lw7ur)9`mK>}J)!uID&w~)%N_nKQz|7s z29o_pVT8noQJNXUb8sNrj)ax-Yd&g>cRI2t6kD!*c|v}tRPbNCkmb6)ZLUIqUuJT- zP+F5i$>v3H2=C{Y&c23+0C2Zl#6N)l^G{((hT=6Q`8FUhzDhp-UFh>{*6ym(fmR_C z8-=<>p@;$dkCez^e-#ItC?^gwZZG@lcZg4-svgtIIPv$i7(%tMtwZ}vH;%tV{I^|j`i=eyPIAyGf_j8#!YttO&${?mw}QiG=Y ztr!YRXL|nN1EYcgjVtHpPw+lEUL=AwsL&C7;j8aRo~|k@k$YUZqJR%wvg85mVUm7S zg71oNda|mpsusp1Z5Duv4n3&7=OaV*#UTh z0San>8Vqog+;;9+Z0uSr_`bIg;BWcGB6Z5Z-N0)VZ^AA&YcwuB^u?3}z-+!j$%K?gEAT%jaJ4&l z?%Yd*YDs&WJbHDlloX~(QmKy^L68JTz)vdH@HhqvSYLzQSzAK#&uCeh!$=tXkjv`-wTR&P9F_}=N^ zu?I1wK(jrS!od{$#JBZnT={FpeC`GO450+nKoN+8@;`3sodRzJg7}%udFw4tEtyDa zb59PS-lbLos3Sd*MLS;UFwr{E(#GCmY?r=K%?bW^ianqkTA@)k)tW!w%EXBOAQzMy z0nWCu2O-Sp@isV3U+0H~?+q_qcj%_6kzY=-!i$qxzTmqgKfWQT4$H6TRU38$Up{VJ z*xMce@z<(0t@y^tGwu-k?2L}IQ0(QmZsHjn-uX-bM)f4>C z35*I3(8Y<8R)gMZam&ibfq-#+zl}Qxs)u^~ekMAI#X=!REwyz5Z9kxKw8J)@glDE5ugcyo?Q@z4bN))Y zRWBO^Mf{`oKOT`C>^N$w(J%5k!!pf&u3G**S|LQ{l8Mo=6%=mOT=`#u;Hf4DV1DL% z_qpU9T#`1TpQ?Dx;PO;FnY|-?PDW69ccvV!lEJTR)N^quJ~O0}Rkk@9>+U4P19Ybi z`K$-^aMdd6qNCe>4jud9tmp056o~pTk2*7ts%6G#y^C=-px9Gew;seE483(s)fD}q zR@okKAv~*2CN;Lp9B^-D7!%ws+cwq9Cguu61R%r!giu4X(vg z?BM#OG7sG+WhaEiMli#-w#LM3`82(H0jpj-{hrp@qn@&&2b90){=QX|taae-n1RKW z%Fumfrq!VHZ-wY-0D=w_?p4 zkBn?XOAyl<48hOkF7$;-BP=0CvxgDUkn|2js?23tDrA|!Ok5- zJ`48Omv%Pcel1E;WdbHa3b`y>qpVVs-(5{aLzH$x62H;R?oUm=nY!u@@BR?_4-gD? z=TBh57e&OMjMO?qWkch`7Dc9~{N*OC`tJv2EG5_pG3xk}SZ_qaU%O|Uee92iD!l7b z{zn}6*&X@oh0r^iV0`njSCI+}27(Bss2H^3(fEhg;}xd}udKra3gAlf0QXoz%EdCT zM+C6)(aOk_v5h~|LVr+{LIu8K!<6mu ztTNQX-vu0xur(Nd)^Yp%uQ<*S%t(a^o*3O}Ft$if{v@s_N+9f-jEtKAFAP~$mz;|? z_pTubrr|musV;pr@6$!-m2$vK7jmHOsFwus-d4uprfyZm)x)1vA;~Dn!yFic_Ao=B zYM6pp0wG?!Ia}y*?tT61NV3qk6+}intZE2WOijKPd9@fV!~77 z`QQH$xOMmU?2Z7hfNu5dBG%0Kk$3rQDKRl=9rgpL(wH}rjrIfsn9 z_SZcG))B6nR6KiQVJLieNSN9|A{1$6G`-!O*I$V#?#fjZ^D{8^`LlHVzlW3)f^Toj z#M$q-l>+DrymnuE)8n1-^V?LV+2@PZ9EPzj|ijbgW!&8(X zSz*ibubit4J;li^0tYIp?onj zMu!*@rq0);u0_u6A`n5~$S2K59gRl<2VK0m*G#9%Xj7H{pno>nec80zV9LjBZMMPm zoh(89@ad&U@D-#i0hia1nJ4CGqQ6!Lbu1OLE7NgQA|cTW1Q&_m5v8w@IM>XMh+xnt zjDJWsXplv;&E}Vqx7ypL{l9xJji(mZfuJwNDcJfuA$HO47xK_U#S9YarAh#AIXvq^WhFB#_smxybepH?k#o!ZTj~uVa7U6k|pwh=O6|1tP_L@-Z0A`^SHZ+>d5XCcv9@7CAd2YPQb5p`j^lACel6{QW(`Ac>T#-<3 z|EGS9MP^G|yMiIl_udHNSJRaJ0&CB{Sx)^Gb?;)zl7Eg|>XQ2#*|=MWj>*M6iFA-l z>Kb|-6n*V}_)5&R5w(j6Gq=@NqGuh| zc0fS)4EI{*tj>c%xmMIiw=0k0+$@%9m?x<)uT%uFX%a+&o(6Vp~xEx#>TZF3^FE8Sw z6{*O3wzSzS<0eJkGHl$i@N;443mc&*O7Y=p5^Bvo*XdfWy zgsBxcPW%b2WPKA(<&jYrLmW&yuN22#+)!Au%DFvRAsEAI40>bXd+XuR>!&LU4zH2! znO;q}7UdEuJ>w!ESEy>hP~4!Re&@*F2rkSK$&eOp%)Yr;+MT{+TQBn)VfE}YCGjo4 z+kD-&kcf*57*mo4RZ&~>KIjGJyP!>T<&iaEEU3Qct7w-x58o#vs$g}z z4W?>GWVwIyw5vIv6Fc=L-!7n?RKMy zzU2*P7x4{$UT)VGJ;_cwpe)VjW^neF-4W6CmYQF%r)M>yK=8^&`qrizTftOnYP9Ghi`k zcOsA7p`#*+79oh@^agN+CQ(Ge031UYuaOI{n+6F^a%nI;+g4hOKgDp#=Gsd3d+{br?5;R7yhInfqTCesFMcZEf`{$njXRSw_A)`I{Ctbqstoe;&e3(DM zovEb9a#K)RO*PVA1QPXD3E76LU&JOU za1QqSM!Gyi5*+)+@=*fiq>fkpnfr@Zc(NdFd_Mo7jO7=KlZ8Q3PJm7D`Ee8%P1 zysnr(sCE_<@pPT1H9LM`&o6x$ZCMN7AJ+Wr;Q3WcRRiKn7s=sCIHFAkc4{mZqkb?;llw9H z{t+Wu11m^HokHJHV=lcR9#3&I1j1^_AigeHl!E~SRXJGRAPXB%;SrVQF#%&Sk*h`i zoz6`+_cxWK=t8OrJjHivKlEI`X~Zru9%sITKE(ul-O){8e+yac4+KN*+V$} zu8cm}n(l`Nws=`FEveT)irxH#5_%rC${Qxj%D@l;w4wKwO&TTJ?1PH~M{SKBmi3a&a-Pj?ugY|j%qp(c7NyY6jgo>g^KRkz z${X|4rcjq_FIEN0_ic6Db)pH3+!kF$p;m9O-gjJ8)~%#;HaZv1&zHG*b+*>pyOQ#_ zu_ibBST`fWZUIvk57QVLY2eP){GUC-N(Ar>OW|Kb5%=ol-^=-8gTO2F@PD&37|npU z>_vtG0;RM;Z5+g@xZSfpb=u(HXTdEt?j0Gl+d~_BaGmRK|F3kIyG;HWFZgFkmya{- zkjv=Y6ZmNZ{^ztG&C^rkZlf*tS8mc;BU$N^N=xT&LnwSSor1^px}dMT=!ybY$?~xQ zTAIK#;A@n?bz?F>qWlb9bfy&x*4~sA(0T{&2k};f$TUJMpLNT(V*)`u6dodBwJc#T zr(fmnIYaBOk0C_AyLM!wAGPOo(nK{+SX*@8)u`?H(|Ffxh`}uYp;x)U%Sm8FyZ3}8 zTY}7n#SnrFBt5Fx_*vB>BW%m}1%g-z)k%Q(UP2g4ME}81*Z4ycoF!BSA|pqlMof%4 z#A8txVuLTZJtv+?P(RfTKUJ+EU*39J(Z-4ak0fw>PMaP$%}LMYWClw{0If&JHZK=A z#g3y{@2bcUsZx--(Smq{&1i6_?Y@}Nu~xGSKXXpAY%O)5R$gtDPMH^MGPU+V4fhoZ zVKqb@Qt~a@+FsEM8Ppm6*-Nwg&hd9RPutN%FUCwypvWLq9)`Cyb41h5n>3zlY3HGl zNCAh0KbJlQ(^x@@->Y$d2Rq-V=|80wNlOc3_J((q z7~;={#mUn{p~dMg#WXJ-OBW5n@j?7(=}Wh6H3DKvx0HbTv1#CKl}^6PwSo^;;|q^h zk+>g7!8yET@{9ZvW5Z-NUngZ@MjI?C1u_Vjk|6Gv+oiPS1~&N?m{WU*V=Ne9iIMJSeJ7~L9OPnmwdePP zyJmBuNlzHgd<;bvUDD9_O#sd&X4+j#)yx@kuzEVf5dJ_1a!9)G4%+9@*It8J!b7aj z5sXi6k)L0^*u3mW=&u|z@(lXL;uxzkienP<3lTk~78h_KzYQK!HIk%p!K?Oy*YBKD zj_L`!YDui@H7^_7iT8@+L)V?>`OP zH2~EJC{tZvAlF#hqD;fEF^f@XDR7^LB(t8LpuO5_`cDS^;51mfi}7EXgE*j!HSU&{ z1nZD9gJv?UW+7r+p-enEgKx7pIJvBAtWn-FCp>oXPMS}IU`{;YL0^n>lvqgAc0u%v zPw`AyTr%8$FT;*{K2i7SaF9{*6Kl=KqIOeY1*Y$BXfGn|T8qdSWYgE}^HaK^q+idp z@3OJ?PekUE3WMAY{6}NKzAHIJepYu>{Q|{A!XVqm;z#DnjuO?>*7&LJePV@`C4VcI z1ujG@E+^r0k{wOU?OKjEY2{|~T;ZHr;w+^D>VlIMxJ}8EY0t5J;XVn?XV&F0p*-f0 znBUc2^hB)qMG4r&2i|kDiW9g){Ih56$Jgx!1`8Ip7NOXPo4`s1 z+Z|EdHPS2aV#`Bm&+^;P!nI|=aoORu6$Rwf{5JJTXFCpCRd{2+=SnH<{Wm651Gijy z+iX9;TJRK-x&Bw@-g&hJ$G08;NO7IA@2}=UNs8j{BCu9G^hQHw0Tb%T;*p3K)K{%s zlIfzN2GR!7PZ}K;EsLLq()fdgUbj+_&zT#8*A8+71%7S)(kjKYfrXnD{EFyThD&9n zxL^fpCUGz0fW`+7H-muAG#MmF#X&4uA)%`9?k0>Z7a@P=LpUr)0UpvPyRH<@yElfY zxP^TEgE)mlsHKEx=si{2FuK!sSNm2-s@>Bx)j*Mma_*TL!6iT_ep+S~1TzVxWnD?H z>K&t19Pto)v{`_EJ)Pcq<-q%?{SQOmrTFm&LAJFkLo8fBz$*)N#Xo)j7#gXTnr31L zUv^mRE8sMrF5PsDTaJH5Osd*+(R(&VL7aKJdFFGNyU$E0UNW;9V-@wPUiaC-0eB2v z!@8&0xFcYG5mUZi5OVGy3-$b@;M&pf*S0Y%9UIcCL_G#+vW6lTIDGL;cx@c_<5(rj z0=7rWI1kQL9zS7!tVby*Sw3sLG}n1Bljk>5qmhPBX+5$?s0KPPpXMT(_^Tx?)UMoY z{IV}IB{BV0ZBE=)_}bWqwupSQ{OxZl`~O~hMq`PB-~I(&mR+)Z7IOV1;o6r@;k1Rq zU-Qyh8jSZ-z=9_KC*E0Lp;URf?VHWT5k%CoB%-5 zgpjT1Hcf|;1D0AmWg_ysO+@BT(v)8^NW`*RlpkzQhH~^`toJgw)&P`9i7_2y9~$bu z_Zk)~S@CIfO>1pUE^cw=bxgv}2gD7rFSgri=hmO-`KlV1tJYdub{pwHTy$O=xfs>_ zd!Y0(zGmh1HbejdrxeEv@lNWpb(@hI52N=GNmDcNWfH0N zRS3im@Lx7a;#n{g?rj?n`*gv*y|SR;*)iuIdLy=>@S2}>zLjAmV6W${-mC##c~k^- zxqyF0H4%F-LFX7M-i*%U(O=E|JmPvRPuuR6L)}4IZeA6)&LN(m()#9tvPEA`-s7_c zkLU#|zWf9Jmtw>n)Q{`VVJtLru3kO0<+3oLefMg!F(2;}ewO2YaRM#aO8dko2%wlC zy5J1K@c{uOzJ&S(2oH!0D@{Mn3f*kJH0bgsMgBBxNFiPyk6y?l}rK#$y*HjB1PLwKSNTh@c4mq z+&>@{GDX%jYBE>q@r0n9#m#eq#_-r$v)DI%XSoGZUoKA{U*+cgZt<)eOt44?3u+~4 z%r)42deZ3o(7)qA!9YZwR%lgEs7ib&t&hcfP!<>JI()@sGBxy?XU{Xdzfk+1y3)e%U?h!@}yq@P`-g zcLevUCDqRVBmFaWz1dg0c;!fM@Z;BlVU3^S?*2}FE_dtY_EKly)4&z;q%E< zLC)cpn<<}`szcq5?~HjqI)}KFGpWdLltxw(R^l?qO1TC`lmOjAfPqMk-5TMoRCyUi zgaMNdQxD)RI50X?00d(rHvs4?ITYBpUe#D^xw{k!fB~r{rY*+5snlT|PXmkA#b;tD zc%}QQ)k(YeHGCQCdFre}&Z~;cw4ZsEmy(Q{t1gBmI;x&0D`jEq>kR1N1M~hZ7cDWo zAn9o}UXLGzr8Kgjd-#YVkYvqP{XV{%8>Ixe>SmGM#~%?)3)TF{A_L5haIwBL6#zF{ z07RHd6OR!}Lh~4;+O19`Vuu>fAK%qLAz_?9@PzTsFY;|r8eKF-_q{teZ7>J%HYnAD zSa${lktzL-7gs}%fcd!W$zvenc|SG?X~08ZN}P!gL8*r`M{+)KWIn+pt#-uW3UR!D zbac81;C%G~)_aVr$r=e;Y_*G2DRVL63F#wxP`vD^5=zXu%W0G-tx%5UN9C;&@4On2 zQR?TX03e7Ug-VCO3|JuOf(3z?E5%E7V!SDHxsR-UgU5vl=n)Q5=}gxwngD#x5X