diff --git a/.gitignore b/.gitignore index 26402236..c288ab26 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ driver/*.swp cscope.out .vs installer/src/Installer/driver/ +dsneditor/EsOdbcDsnEditor/Debug/ +dsneditor/EsOdbcDsnEditorLauncher/bin/ +dsneditor/EsOdbcDsnEditorLauncher/obj/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d45dc7e7..b4ba77bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,15 +21,16 @@ set(IS_UNICODE 1) #include(GenerateExportHeader) if (${WIN32}) - if (${CMAKE_GENERATOR_PLATFORM} MATCHES x64) + # or: CMAKE_SIZEOF_VOID_P EQUAL 8 + if (${CMAKE_GENERATOR_PLATFORM} MATCHES [Xx]64) set(TARCH x64) # target arch set(BARCH ) # bits architecture (64 is the default, silent) set(PLAT_ARCH windows-x86_64) - else (${CMAKE_GENERATOR_PLATFORM} MATCHES x64) + else (${CMAKE_GENERATOR_PLATFORM} MATCHES [Xx]64) set(TARCH x86) set(BARCH 32) set(PLAT_ARCH windows-x86) - endif (${CMAKE_GENERATOR_PLATFORM} MATCHES x64) + endif (${CMAKE_GENERATOR_PLATFORM} MATCHES [Xx]64) message("Building for Windows, ${TARCH}.") else (${WIN32}) message(FATAL_ERROR "No support for current platform yet") @@ -175,18 +176,20 @@ aux_source_directory(${CTIMESTAMP_PATH_SRC}/ DRV_SRC) # set(LIBCURL_PATH_SRC ${CMAKE_SOURCE_DIR}/libs/curl CACHE PATH "Lib curl source path") -set(LIBCURL_BUILD_TYPE debug CACHE STRING - "Lib curl build type: debug (default) or release") set(LIBCURL_LINK_MODE static CACHE STRING "Lib curl linking mode: static (default) or dll") +set(LIBCURL_BUILD_TYPE debug CACHE STRING + "Lib curl build type: debug (default) or release") -if (${LIBCURL_BUILD_TYPE} MATCHES debug) +if (${LIBCURL_BUILD_TYPE} MATCHES [Dd][Ee][Bb][Uu][Gg]) set(LIBCURL_DEBUG_ENABLED yes) + set(LIBCURL_BUILD_TYPE debug) set(LIBCURL_BUILD_SUFFIX _debug) -else (${LIBCURL_BUILD_TYPE} MATCHES debug) +else (${LIBCURL_BUILD_TYPE} MATCHES [Dd][Ee][Bb][Uu][Gg]) set(LIBCURL_DEBUG_ENABLED no) + set(LIBCURL_BUILD_TYPE release) # empty LIBCURL_BUILD_SUFFIX -endif (${LIBCURL_BUILD_TYPE} MATCHES debug) +endif (${LIBCURL_BUILD_TYPE} MATCHES [Dd][Ee][Bb][Uu][Gg]) set(LIBCURL_LD_PATH # Curl "installs" the .dll and .lib in different directories -> use the @@ -201,7 +204,12 @@ set(LIBCURL_INC_PATH ${LIBCURL_PATH_SRC}/include CACHE PATH # Build libcurl. # Note: this happens at config time as a pre-requisite, for now. This might -# be changed to a build target later (possibly as a CMake subproject). +# be changed to a build target later (possibly as a CMake subproject: re-link +# only if out-of-date, skip building the .exe, allow disabling non-HTTP +# protos, || build, setting output destination). +# Building as a pre-requisite has however the disadvantage of making the +# entire build "single-config", since the build type (rel/dbg) is decided at +# CMake-generation, not along the MSBuild invocation. if (NOT IS_DIRECTORY ${LIBCURL_LD_PATH}) execute_process(COMMAND buildconf.bat RESULT_VARIABLE CMD_RETURN @@ -231,14 +239,14 @@ endif(NOT IS_DIRECTORY ${LIBCURL_LD_PATH}) # add libcurl as dependency if (${WIN32}) - if (${LIBCURL_LINK_MODE} MATCHES dll) + if (${LIBCURL_LINK_MODE} MATCHES [Dd][Ll][Ll]) add_library(libcurl SHARED IMPORTED) set_property(TARGET libcurl PROPERTY IMPORTED_LOCATION ${LIBCURL_LD_PATH}/libcurl${LIBCURL_BUILD_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}) set_property(TARGET libcurl PROPERTY IMPORTED_IMPLIB ${LIBCURL_LD_PATH}/libcurl${LIBCURL_BUILD_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}) # empty LIBCURL_WIN_LIBS - else (${LIBCURL_LINK_MODE} MATCHES dll) + else (${LIBCURL_LINK_MODE} MATCHES [Dd][Ll][Ll]) add_library(libcurl STATIC IMPORTED) set_property(TARGET libcurl PROPERTY IMPORTED_LOCATION ${LIBCURL_LD_PATH}/libcurl_a${LIBCURL_BUILD_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) @@ -246,7 +254,7 @@ if (${WIN32}) # Libraries that libcurl/WinSSL links against. # Removed: wldap32 advapi32 gdi32 user32 (wth current config unused) set(LIBCURL_WIN_LIBS ws2_32 crypt32 normaliz) - endif (${LIBCURL_LINK_MODE} MATCHES dll) + endif (${LIBCURL_LINK_MODE} MATCHES [Dd][Ll][Ll]) else (${WIN32}) set_property(TARGET libcurl PROPERTY IMPORTED_LOCATION ${LIBCURL_LD_PATH}/libcurl${CMAKE_SHARED_LIBRARY_SUFFIX}) @@ -258,41 +266,58 @@ add_custom_target(curlclean WORKING_DIRECTORY "${LIBCURL_PATH_SRC}/winbuild" ) +# +# DSN Config GUI +# +set(DSNBND_LIB_BIN_DIR_BASE ${CMAKE_BINARY_DIR}/dsneditor-${TARCH}) +set(DSNEDITOR_INC_PATH ${CMAKE_SOURCE_DIR}/dsneditor/EsOdbcDsnBinding/) +add_custom_target(dsneditor + COMMAND MSBuild ${CMAKE_SOURCE_DIR}/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj + # place all the build items (by- & products) into a single dir + /p:OutputPath=${DSNBND_LIB_BIN_DIR_BASE}-$/ + /p:IntermediateOutputPath=${DSNBND_LIB_BIN_DIR_BASE}-$/ + /p:OutDir=${DSNBND_LIB_BIN_DIR_BASE}-$/ + /p:IntDir=${DSNBND_LIB_BIN_DIR_BASE}-$/ + /p:Configuration=$ + /p:Platform=${TARCH} # make it explicit, for x-arch builds + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Building the DSN editor libraries.") -message("C flags: ${CMAKE_C_FLAGS} .") -message("Driver source files: ${DRV_SRC} .") -message("Driver include paths: " ${ODBC_INC} ${DRV_SRC_DIR} - ${LIBCURL_INC_PATH} ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC}) - -# generate resource file -configure_file(${DRV_SRC_DIR}/driver.rc.cmake ${CMAKE_BINARY_DIR}/driver.rc) # # finally, set destination library # +# generate resource file +configure_file(${DRV_SRC_DIR}/driver.rc.cmake ${CMAKE_BINARY_DIR}/driver.rc) add_library(${DRV_NAME} SHARED ${DRV_SRC} ${CMAKE_BINARY_DIR}/${DRV_NAME}.def ${CMAKE_BINARY_DIR}/driver.rc) #generate_export_header(${DRV_NAME}) target_compile_definitions(${DRV_NAME} PRIVATE "DRIVER_BUILD") - +add_dependencies(${DRV_NAME} dsneditor) include_directories(${ODBC_INC} ${DRV_SRC_DIR} ${LIBCURL_INC_PATH} - ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC}) + ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC} ${DSNEDITOR_INC_PATH}) +target_link_libraries(${DRV_NAME} odbccp32 legacy_stdio_definitions + ${DSNBND_LIB_BIN_DIR_BASE}-$/esdsnbnd${CMAKE_IMPORT_LIBRARY_SUFFIX} + libcurl ${LIBCURL_WIN_LIBS}) -target_link_libraries(${DRV_NAME} odbccp32 legacy_stdio_definitions libcurl - ${LIBCURL_WIN_LIBS}) -# add testing project/target +# +# Set up the testing project/target +# enable_testing() # ... and testing directory to build add_subdirectory(test) + +# +# Set up the instalation +# set(INSTALL_DIR ${DRIVER_BASE_NAME}-${DRV_VERSION}${VERSION_QUALIFIER}-${PLAT_ARCH} # this must remain a STRING (i.e not PATH), otherwise CPACK won't work CACHE STRING "Directory to install the driver files into") -message("Install target: ${INSTALL_DIR}.") # this will cause the 'install' to no longer depend on 'all', which avoids # building the tests, but this also means that the library must be built # before building the 'install' target. @@ -301,15 +326,24 @@ set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE) install(FILES LICENSE.rtf LICENSE.txt DESTINATION ${INSTALL_DIR}) -if (${LIBCURL_LINK_MODE} MATCHES dll) +# add libcurl if build dynamically +if (${LIBCURL_LINK_MODE} MATCHES [Dd][Ll][Ll]) install(FILES # need to use FILE : https://public.kitware.com/Bug/view.php?id=14311 ${LIBCURL_LD_PATH}/libcurl${LIBCURL_BUILD_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION ${INSTALL_DIR}) -endif (${LIBCURL_LINK_MODE} MATCHES dll) +endif (${LIBCURL_LINK_MODE} MATCHES [Dd][Ll][Ll]) +# add editor DLLs +install(FILES + ${DSNBND_LIB_BIN_DIR_BASE}-$/esdsnedt.dll + ${DSNBND_LIB_BIN_DIR_BASE}-$/esdsnbnd.dll + DESTINATION ${INSTALL_DIR}) install(TARGETS ${DRV_NAME} DESTINATION ${INSTALL_DIR}) +# +# Set up the packaing +# set(CPACK_GENERATOR "ZIP") # don't build ALL (but see CMAKE_SKIP_INSTALL_ALL_DEPENDENCY comment) set(CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY TRUE) @@ -317,4 +351,21 @@ set(CPACK_PACKAGE_FILE_NAME ${DRIVER_BASE_NAME}-${DRV_VERSION}${VERSION_QUALIFIER}-${PLAT_ARCH}) include(CPack) + +# +# Generation summary +# +message(" ***** ") +message("Driver source files: ${DRV_SRC}") +message("Driver compile flags: ${CMAKE_C_FLAGS}") +message("Driver include paths: ${ODBC_INC} ${DRV_SRC_DIR}" + "${LIBCURL_INC_PATH} ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC}" + "${DSNEDITOR_INC_PATH}") +# there's no var for this +message("Driver link libraries: odbccp32 legacy_stdio_definitions esdsnbnd" + "libcurl ${LIBCURL_WIN_LIBS}") +message("Driver install target: ${INSTALL_DIR}.") +message("Driver packaging target: ${CPACK_PACKAGE_FILE_NAME}.") +message(" ***** ") + # vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : diff --git a/build.bat b/build.bat index bbf03330..3913e54e 100644 --- a/build.bat +++ b/build.bat @@ -14,8 +14,6 @@ set DRIVER_BASE_NAME=esodbc set ARG="%*" set SRC_PATH=%~dp0 -REM "funny" fact: removing 'REM X' from above label definition makes 'cmd' -REM no longer find the label -- why? (check with "> build nobuild") call:SET_ARCH call:SET_CMAKE @@ -204,8 +202,7 @@ REM USAGE function: output a usage message echo building (requires 2017 version or later^). echo clean : remove all the files in the build dir. echo proper : clean libs, builds, project dirs and exit. - echo type:T : selects the build type, T, among one of: - echo Debug/Release/RelWithDebInfo/MinSizeRel. + echo type:T : selects the build type, T: Debug or Release. echo tests : run all the defined tests. echo suites : run all the defined tests, individually. echo suite:S : run one test, S. @@ -355,50 +352,70 @@ REM injected into the project files generated by cmake REM BUILDTYPE function: set the build config to feed MSBuild :BUILDTYPE - if /i not [%ARG:type=%] == [%ARG%] ( - REM cycle through the args, look for 'type:' token and use the - REM follow-up token - for %%a in (%ARG:"=%) do ( - set crr=%%a - if /i ["!crr:~0,5!"] == ["type:"] ( - set BUILD_TYPE=!crr:~5! + if not exist ALL_BUILD.vcxproj ( + if /i not [%ARG:type=%] == [%ARG%] ( + REM cycle through the args, look for 'type:' token and use the + REM follow-up token + for %%a in (%ARG:"=%) do ( + set crr=%%a + if /i ["!crr:~0,5!"] == ["type:"] ( + set BUILD_TYPE=!crr:~5! + ) ) + REM no check against empty val (type:) here ) - REM no check against empty val (type:) here - ) - if [%BUILD_TYPE%] == [] ( - set BUILD_TYPE=Debug + if [!BUILD_TYPE!] == [] ( + set BUILD_TYPE=Debug + ) + echo %~nx0: setting the build type to: !BUILD_TYPE!. + ) else if exist %BUILD_DIR%/Release ( + set BUILD_TYPE=Release + echo %~nx0: previously build type set: !BUILD_TYPE!. + ) else if exist %BUILD_DIR%/Debug ( + set BUILD_TYPE=Debug + echo %~nx0: previously build type set: !BUILD_TYPE!. + ) else ( + REM DSN editor libs only support Debug and Release + echo %~nx0: ERROR: unknown previously set build type. + set ERRORLEVEL=1 + goto END ) set MSBUILD_ARGS=/p:Configuration=!BUILD_TYPE! - echo %~nx0: setting the build type to: !BUILD_TYPE!. goto:eof REM BUILD function: build various targets :BUILD + REM set the wanted or previously set build type. + call:BUILDTYPE + if ERRORLEVEL 1 ( + goto END + ) if not exist ALL_BUILD.vcxproj ( echo %~nx0: generating the project files. REM set the wanted build type. - call:BUILDTYPE + rem call:BUILDTYPE set CMAKE_ARGS=-DDRIVER_BASE_NAME=%DRIVER_BASE_NAME% REM no explicit x86 generator and is the default (MSVC2017 only?). set CMAKE_ARGS=!CMAKE_ARGS! -DCMAKE_GENERATOR_PLATFORM=%TARCH:x86=% + if /i not [%ARG:curldll=%] == [%ARG%] ( set CMAKE_ARGS=!CMAKE_ARGS! -DLIBCURL_LINK_MODE=dll ) + if /i [!BUILD_TYPE!] == [Debug] ( + set CMAKE_ARGS=!CMAKE_ARGS! -DLIBCURL_BUILD_TYPE=debug + ) else ( + set CMAKE_ARGS=!CMAKE_ARGS! -DLIBCURL_BUILD_TYPE=release + ) + if not [!INSTALL_DIR!] == [] ( set CMAKE_ARGS=!CMAKE_ARGS! -DINSTALL_DIR=!INSTALL_DIR! ) if not [!PACKAGE_VER!] == [] ( set CMAKE_ARGS=!CMAKE_ARGS! -DVERSION_QUALIFIER=!PACKAGE_VER! ) - if /i [!BUILD_TYPE!] == [Debug] ( - set CMAKE_ARGS=!CMAKE_ARGS! -DLIBCURL_BUILD_TYPE=debug - ) else ( - set CMAKE_ARGS=!CMAKE_ARGS! -DLIBCURL_BUILD_TYPE=release - ) echo %~nx0: cmake params: !CMAKE_ARGS!. %CMAKE% !CMAKE_ARGS! !SRC_PATH! diff --git a/driver/catalogue.c b/driver/catalogue.c index 0ee1b542..88a85a7c 100644 --- a/driver/catalogue.c +++ b/driver/catalogue.c @@ -26,19 +26,22 @@ #define SQL_TABLES \ "SYS TABLES" #define SQL_TABLES_CAT \ - " CATALOG LIKE " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM + " CATALOG LIKE " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM \ + " ESCAPE '" ESODBC_PATTERN_ESCAPE "'" #define SQL_TABLES_TAB \ - " LIKE " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM + " LIKE " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM \ + " ESCAPE '" ESODBC_PATTERN_ESCAPE "'" #define SQL_TABLES_TYP \ " TYPE " WPFWP_LDESC // TODO add schema, when supported #define SQL_COLUMNS(...) "SYS COLUMNS" __VA_ARGS__ \ " TABLE LIKE " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM \ - " ESCAPE '" ESODBC_PATTERN_ESCAPE "' " \ - " LIKE " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM + " ESCAPE '" ESODBC_PATTERN_ESCAPE "'" \ + " LIKE " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM \ + " ESCAPE '" ESODBC_PATTERN_ESCAPE "'" #define SQL_COL_CAT \ - " CATALOG " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM \ + " CATALOG " ESODBC_STRING_DELIM WPFWP_LDESC ESODBC_STRING_DELIM /* writes into 'dest', of size 'room', the current catalog of 'dbc'. @@ -500,7 +503,7 @@ SQLRETURN EsSQLPrimaryKeysW( _In_reads_opt_(cchTableName) SQLWCHAR *szTableName, SQLSMALLINT cchTableName) { - WARNH(hstmt, "no primary keys supported."); + INFOH(hstmt, "no primary keys supported."); STMT_FORCE_NODATA(STMH(hstmt)); return SQL_SUCCESS; } diff --git a/driver/connect.c b/driver/connect.c index 93f58eda..ba59cdb1 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -96,6 +96,10 @@ typedef struct { * HTTP headers used for all requests (Content-Type, Accept). */ static struct curl_slist *http_headers = NULL; +/* counter used to number DBC log files: + * the files are stamped with time (@ second resolution) and PID, which is not + * enough to avoid name clashes. */ +volatile unsigned filelog_cnt = 0; BOOL connect_init() { @@ -116,8 +120,8 @@ BOOL connect_init() assert(curl_info); /* these are available from "age" 0. */ INFO("Using libcurl version: %s, features: 0x%x, SSL ver.: %s.", - curl_info->version, curl_info->features, - curl_info->ssl_version ? curl_info->ssl_version : "NONE"); + curl_info->version, curl_info->features, + curl_info->ssl_version ? curl_info->ssl_version : "NONE"); } http_headers = curl_slist_append(http_headers, HTTP_ACCEPT_JSON); @@ -384,7 +388,7 @@ static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc) } #ifndef NDEBUG - if (LOG_LEVEL_DBG <= _esodbc_log_level) { + if (dbc->hdr.log && LOG_LEVEL_DBG <= dbc->hdr.log->level) { res = curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debug_callback); if (res != CURLE_OK) { ERRH(dbc, "libcurl: failed to set debug callback."); @@ -414,7 +418,7 @@ static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc) curl_easy_strerror(res)); assert(curl); curl_easy_cleanup(curl); - return post_c_diagnostic(&dbc->hdr.diag, SQL_STATE_HY000, + return post_c_diagnostic(dbc, SQL_STATE_HY000, /* propagate CURL's message, if there's any available */ dbc->curl_err_buff[0] ? dbc->curl_err_buff : "failed to init the transport", res); @@ -481,9 +485,8 @@ static BOOL dbc_curl_prepare(esodbc_dbc_st *dbc, SQLULEN tout, if (0 < tout) { dbc->curl_err = curl_easy_setopt(dbc->curl, CURLOPT_TIMEOUT, tout); if (dbc->curl_err != CURLE_OK) { - ERRH(dbc, "libcurl: failed to set timeout=%ld: %s (%d).", tout, - curl_easy_strerror(dbc->curl_err), dbc->curl_err); - return FALSE; + ERRH(dbc, "libcurl: failed to set timeout=%ld.", tout); + goto err; } } @@ -511,6 +514,41 @@ static BOOL dbc_curl_prepare(esodbc_dbc_st *dbc, SQLULEN tout, return FALSE; } +/* post cURL error message: include CURLOPT_ERRORBUFFER if available */ +static void dbc_curl_post_diag(esodbc_dbc_st *dbc) +{ + SQLWCHAR buff[SQL_MAX_MESSAGE_LENGTH] = {1}; + SQLWCHAR *fmt; + int n; + + assert(dbc->curl_err != CURLE_OK); + + /* in some cases ([::1]:0) this buffer will be empty, even though cURL + * returns an error code */ + if (dbc->curl_err_buff[0]) { + fmt = WPFCP_DESC " (code:%d; " WPFCP_DESC ")."; + } else { + fmt = WPFCP_DESC " (code:%d)."; + } + + n = swprintf(buff, sizeof(buff)/sizeof(*buff), fmt, + curl_easy_strerror(dbc->curl_err), dbc->curl_err, + /* this param is present even if there's no spec for it in fmt */ + dbc->curl_err_buff); + /* if printing succeeded, OR failed, but buff is 0-term'd => OK */ + if (n < 0 && !buff[sizeof(buff)/sizeof(*buff) - 1]) { + /* else: swprintf will fail if formatted string would overrun the + * available buffer room, but 0-terminate it; if that's the case. + * retry, skipping formatting. */ + ERRH(dbc, "formatting error message failed; skipping formatting."); + post_c_diagnostic(dbc, SQL_STATE_08S01, + curl_easy_strerror(dbc->curl_err), dbc->curl_err); + } else { + ERRH(dbc, "libcurl failure message: " LWPD ".", buff); + post_diagnostic(dbc, SQL_STATE_08S01, buff, dbc->curl_err); + } +} + /* * Sends a POST request with the given JSON object body. */ @@ -553,9 +591,7 @@ SQLRETURN post_json(esodbc_stmt_st *stmt, const cstr_st *u8body) } } } else { - assert (dbc->curl_err != CURLE_OK); - post_c_diagnostic(&stmt->hdr.diag, SQL_STATE_HY000, dbc->curl_err_buff, - dbc->curl_err); + dbc_curl_post_diag(dbc); code = -1; /* make sure that curl's error will surface */ } /* something went wrong */ @@ -586,8 +622,7 @@ static SQLRETURN test_connect(esodbc_dbc_st *dbc) if (! (dbc_curl_prepare(dbc, dbc->timeout, &u8body) && dbc_curl_perform(dbc, &code, &resp))) { - post_c_diagnostic(&dbc->hdr.diag, SQL_STATE_HY000, dbc->curl_err_buff, - dbc->curl_err); + dbc_curl_post_diag(dbc); cleanup_curl(dbc); code = -1; /* make sure that curl's error will surface */ } @@ -610,13 +645,81 @@ static SQLRETURN test_connect(esodbc_dbc_st *dbc) RET_STATE(dbc->hdr.diag.state); } +static BOOL config_dbc_logging(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) +{ + int cnt, level; + SQLWCHAR ident_buff[MAX_PATH], path_buff[MAX_PATH]; + wstr_st ident, path; + + if (! wstr2bool(&attrs->trace_enabled)) { + return TRUE; + } + + ident.cnt = sizeof(ident_buff)/sizeof(*ident_buff); + ident.str = ident_buff; + cnt = swprintf(ident.str, ident.cnt, + WPFWP_LDESC "_" WPFWP_LDESC "_" "%d-%u", + LWSTR(&attrs->server), LWSTR(&attrs->port), + GetCurrentProcessId(), InterlockedIncrement(&filelog_cnt)); + if (cnt <= 0 || ident.cnt <= cnt) { + ERRH(dbc, "failed to print log file identifier."); + SET_HDIAG(dbc, SQL_STATE_HY000, "failed to print log file ID", 0); + return FALSE; + } else { + ident.cnt = cnt; + } + /* replace reserved characters that could raise issues with the FS */ + for (cnt = 0; cnt < ident.cnt; cnt ++) { + if (ident.str[cnt] < 31) { + ident.str[cnt] = L'_'; + } else { + switch (ident.str[cnt]) { + case L'"': + case L'<': + case L'>': + case L'|': + case L'/': + case L'\\': + case L'?': + case L'*': + case L':': + ident.str[cnt] = L'_'; + } + } + } + + path.cnt = sizeof(path_buff)/sizeof(*path_buff); + path.str = path_buff; + if (! filelog_print_path(&path, &attrs->trace_file, &ident)) { + ERRH(dbc, "failed to print log file path (dir: `" LWPDL "`, ident: `" + LWPDL "`).", LWSTR(&attrs->trace_file), LWSTR(&ident)); + if (attrs->trace_file.cnt) { + SET_HDIAG(dbc, SQL_STATE_HY000, "failed to print log file path", + 0); + } else { + SET_HDIAG(dbc, SQL_STATE_HY000, "no directory path for logging " + "provided", 0); + } + return FALSE; + } + + level = parse_log_level(&attrs->trace_level); + if (! (dbc->hdr.log = filelog_new(&path, level))) { + ERRNH(dbc, "failed to allocate new file logger (file `" LWPDL "`, " + "level: %d).", LWSTR(&path), level); + SET_HDIAG(dbc, SQL_STATE_HY000, "failed to allocate new logger", 0); + return FALSE; + } + + return TRUE; +} + /* * init dbc from configured attributes */ -static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) +SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) { - esodbc_state_et state = SQL_STATE_HY000; - int cnt; + int cnt, ipv6; SQLBIGINT secure; long long timeout, max_body_size, max_fetch_size; SQLWCHAR buff_url[ESODBC_MAX_URL_LEN]; @@ -624,14 +727,26 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) buff_url, /*will be init'ed later*/0 }; + /* + * setup logging + */ + if (! config_dbc_logging(dbc, attrs)) { + /* attempt global logging the error */ + ERRH(dbc, "failed to setup DBC logging"); + goto err; + } + if (! str2bigint(&attrs->secure, /*wide?*/TRUE, &secure)) { ERRH(dbc, "failed to read secure param `" LWPDL "`.", LWSTR(&attrs->secure)); + SET_HDIAG(dbc, SQL_STATE_HY000, "security setting number " + "conversion failure", 0); goto err; } if (secure < ESODBC_SEC_NONE || ESODBC_SEC_MAX <= secure) { ERRH(dbc, "invalid secure param `" LWPDL "` (not within %d - %d).", LWSTR(&attrs->secure), ESODBC_SEC_NONE, ESODBC_SEC_MAX - 1); + SET_HDIAG(dbc, SQL_STATE_HY000, "invalid security setting", 0); goto err; } else { dbc->secure = (long)secure; @@ -642,6 +757,8 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) if (! wstr_to_utf8(&attrs->ca_path, &dbc->ca_path)) { ERRNH(dbc, "failed to convert CA path `" LWPDL "` to UTF8.", LWSTR(&attrs->ca_path)); + SET_HDIAG(dbc, SQL_STATE_HY000, "reading the CA file path " + "failed", 0); goto err; } INFOH(dbc, "CA path: `%s`.", dbc->ca_path.str); @@ -650,20 +767,29 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) /* * URL of the cluster */ + /* Note: libcurl won't check hostname validity, it'll just try to resolve + * whatever it receives, if it can parse the URL */ + ipv6 = wcsnstr(attrs->server.str, attrs->server.cnt, L':') != NULL; cnt = swprintf(url.str, sizeof(buff_url)/sizeof(*buff_url), - L"http" WPFCP_DESC "://" WPFWP_LDESC ":" WPFWP_LDESC - ELASTIC_SQL_PATH, secure ? "s" : "", LWSTR(&attrs->server), + L"http" WPFCP_DESC "://" + WPFCP_DESC WPFWP_LDESC WPFCP_DESC ":" WPFWP_LDESC + ELASTIC_SQL_PATH, + secure ? "s" : "", + ipv6 ? "[" : "", LWSTR(&attrs->server), ipv6 ? "]" : "", LWSTR(&attrs->port)); - if (cnt < 0) { + if (cnt <= 0) { ERRNH(dbc, "failed to print URL out of server: `" LWPDL "` [%zd], " "port: `" LWPDL "` [%zd].", LWSTR(&attrs->server), LWSTR(&attrs->port)); + SET_HDIAG(dbc, SQL_STATE_HY000, "assembling server's URL failed", 0); goto err; } else { url.cnt = (size_t)cnt; } if (! wstr_to_utf8(&url, &dbc->url)) { ERRNH(dbc, "failed to convert URL `" LWPDL "` to UTF8.", LWSTR(&url)); + SET_HDIAG(dbc, SQL_STATE_HY000, "server URL's UTF8 conversion " + "failed", 0); goto err; } INFOH(dbc, "connection URL: `%s`.", dbc->url.str); @@ -675,12 +801,16 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) if (! wstr_to_utf8(&attrs->uid, &dbc->uid)) { ERRH(dbc, "failed to convert username [%zd] `" LWPDL "` to UTF8.", attrs->uid.cnt, LWSTR(&attrs->uid)); + SET_HDIAG(dbc, SQL_STATE_HY000, "username UTF8 conversion " + "failed", 0); goto err; } if (attrs->pwd.cnt) { if (! wstr_to_utf8(&attrs->pwd, &dbc->pwd)) { - ERRH(dbc, "failed to convert username [%zd] `" LWPDL "` to " - "UTF8.", attrs->pwd.cnt, LWSTR(&attrs->pwd)); + ERRH(dbc, "failed to convert password [%zd] (-not shown-) to " + "UTF8.", attrs->pwd.cnt); + SET_HDIAG(dbc, SQL_STATE_HY000, "password UTF8 " + "conversion failed", 0); goto err; } } @@ -696,6 +826,8 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) if (! str2bigint(&attrs->timeout, /*wide?*/TRUE, (SQLBIGINT *)&timeout)) { ERRH(dbc, "failed to convert `" LWPDL "` [%zu] to big int.", LWSTR(&attrs->timeout), attrs->timeout.cnt); + SET_HDIAG(dbc, SQL_STATE_HY000, "timeout setting number " + "conversion failure", 0); goto err; } if (timeout < 0) { @@ -710,13 +842,17 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) */ if (! str2bigint(&attrs->max_body_size, /*wide?*/TRUE, (SQLBIGINT *)&max_body_size)) { - ERRH(dbc, "failed to convert `" LWPDL "` [%zu] to long long.", + ERRH(dbc, "failed to convert max body size `" LWPDL "` [%zu] to LL.", LWSTR(&attrs->max_body_size), attrs->max_body_size.cnt); + SET_HDIAG(dbc, SQL_STATE_HY000, "max body size setting number " + "conversion failure", 0); goto err; } if (max_body_size < 0) { ERRH(dbc, "'%s' setting can't be negative (%ld).", ESODBC_DSN_MAX_BODY_SIZE_MB, max_body_size); + SET_HDIAG(dbc, SQL_STATE_HY000, "invalid max body size setting " + "(negative)", 0); goto err; } else { dbc->amax = (size_t)max_body_size * 1024 * 1024; @@ -728,13 +864,17 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) */ if (! str2bigint(&attrs->max_fetch_size, /*wide?*/TRUE, (SQLBIGINT *)&max_fetch_size)) { - ERRH(dbc, "failed to convert `" LWPDL "` [%zu] to long long.", + ERRH(dbc, "failed to convert max fetch size `" LWPDL "` [%zu] to LL.", LWSTR(&attrs->max_fetch_size), attrs->max_fetch_size.cnt); + SET_HDIAG(dbc, SQL_STATE_HY000, "max fetch size setting number " + "conversion failure", 0); goto err; } if (max_fetch_size < 0) { ERRH(dbc, "'%s' setting can't be negative (%ld).", ESODBC_DSN_MAX_FETCH_SIZE, max_fetch_size); + SET_HDIAG(dbc, SQL_STATE_HY000, "invalid max fetch size setting " + "(negative)", 0); goto err; } else { dbc->fetch.max = (size_t)max_fetch_size; @@ -764,16 +904,16 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) } else { ERRH(dbc, "unknown packing encoding '" LWPDL "'.", LWSTR(&attrs->packing)); + SET_HDIAG(dbc, SQL_STATE_HY000, "invalid packing encoding setting", 0); goto err; } INFOH(dbc, "pack JSON: %s.", dbc->pack_json ? "true" : "false"); return SQL_SUCCESS; err: - if (state == SQL_STATE_HY000) { - RET_HDIAG(dbc, state, "invalid configuration parameter", 0); - } - RET_HDIAGS(dbc, state); + /* release allocated resources before the failure; not the diag, tho */ + cleanup_dbc(dbc); + RET_STATE(dbc->hdr.diag.state); } @@ -833,11 +973,17 @@ void cleanup_dbc(esodbc_dbc_st *dbc) } else { assert(dbc->no_types == 0); } + assert(dbc->abuff == NULL); cleanup_curl(dbc); + + if (dbc->hdr.log && dbc->hdr.log != _gf_log) { + filelog_del(dbc->hdr.log); + dbc->hdr.log = NULL; + } } -static SQLRETURN do_connect(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) +SQLRETURN do_connect(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) { SQLRETURN ret; @@ -845,7 +991,7 @@ static SQLRETURN do_connect(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) cleanup_dbc(dbc); /* set the DBC params based on given attributes (but no validation atp) */ - ret = process_config(dbc, attrs); + ret = config_dbc(dbc, attrs); if (! SQL_SUCCEEDED(ret)) { return ret; } @@ -1186,8 +1332,8 @@ static BOOL bind_types_cols(esodbc_stmt_st *stmt, estype_row_st *type_row) /* Copies the type info from the result-set to array to be associated with the * connection. */ -static void *copy_types_rows(estype_row_st *type_row, SQLULEN rows_fetched, - esodbc_estype_st *types) +static void *copy_types_rows(esodbc_dbc_st *dbc, estype_row_st *type_row, + SQLULEN rows_fetched, esodbc_estype_st *types) { SQLWCHAR *pos; int c; @@ -1252,7 +1398,7 @@ static void *copy_types_rows(estype_row_st *type_row, SQLULEN rows_fetched, pos += types[i].type_name.cnt + /*\0*/1; if ((c = ascii_w2c(types[i].type_name.str, types[i].type_name_c.str, types[i].type_name.cnt)) < 0) { - ERR("failed to convert ES/SQL type `" LWPDL "` to C-str.", + ERRH(dbc, "failed to convert ES/SQL type `" LWPDL "` to C-str.", LWSTR(&types[i].type_name)); return NULL; } else { @@ -1266,7 +1412,7 @@ static void *copy_types_rows(estype_row_st *type_row, SQLULEN rows_fetched, /* notify if scales extremes are different */ if (types[i].maximum_scale != types[i].minimum_scale) { - INFO("type `" LWPDL "` returned with non-equal max/min " + INFOH(dbc, "type `" LWPDL "` returned with non-equal max/min " "scale: %d/%d -- using the max.", LWSTR(&types[i].type_name), types[i].maximum_scale, types[i].minimum_scale); } @@ -1275,11 +1421,11 @@ static void *copy_types_rows(estype_row_st *type_row, SQLULEN rows_fetched, if (! elastic_name2types(&types[i].type_name, &types[i].c_concise_type, &sql_type)) { /* ES version newer than driver's? */ - ERR("failed to convert type name `" LWPDL "` to SQL C type.", + ERRH(dbc, "failed to convert type name `" LWPDL "` to SQL C type.", LWSTR(&types[i].type_name)); return NULL; } - DBG("ES type `" LWPDL "` resolved to C concise: %hd, SQL: %hd.", + DBGH(dbc, "ES type `" LWPDL "` resolved to C concise: %hd, SQL: %hd.", LWSTR(&types[i].type_name), types[i].c_concise_type, sql_type); /* BOOLEAN is used in catalog calls (like SYS TYPES / SQLGetTypeInfo), @@ -1293,8 +1439,8 @@ static void *copy_types_rows(estype_row_st *type_row, SQLULEN rows_fetched, /* .data_type is used in data conversions -> make sure the SQL type * derived from type's name is the same with type reported value */ if (sql_type != types[i].data_type) { - ERR("type `" LWPDL "` derived (%d) and reported (%d) SQL type " - "identifiers differ.", LWSTR(&types[i].type_name), + ERRH(dbc, "type `" LWPDL "` derived (%d) and reported (%d) SQL " + "type identifiers differ.", LWSTR(&types[i].type_name), sql_type, types[i].data_type); return NULL; } @@ -1302,7 +1448,7 @@ static void *copy_types_rows(estype_row_st *type_row, SQLULEN rows_fetched, types[i].meta_type = concise_to_meta(types[i].c_concise_type, /*C type -> AxD*/DESC_TYPE_ARD); - /* fix SQL_DATA_TYPE and SQL_DATETIME_SUB columns TODO: GH issue */ + /* fix SQL_DATA_TYPE and SQL_DATETIME_SUB columns */ concise_to_type_code(types[i].data_type, &types[i].sql_data_type, &types[i].sql_datetime_sub); @@ -1357,26 +1503,31 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) SQLRETURN ret = FALSE; SQLSMALLINT col_cnt; SQLLEN row_cnt; - /* both arrays below must use ESODBC_MAX_ROW_ARRAY_SIZE since no SQLFetch() - * looping is implemented (see check after SQLFetch() below). */ - SQLUSMALLINT row_status[ESODBC_MAX_ROW_ARRAY_SIZE]; - /* a static estype_row_st array is over 350KB and too big for the default - * stack size in certain cases: needs allocation on heap */ - estype_row_st *type_row = NULL; + SQLUSMALLINT *row_status; + estype_row_st *type_row; + void *row_arrays; SQLULEN rows_fetched, i, strs_len; size_t size; esodbc_estype_st *types = NULL; void *pos; - type_row = calloc(ESODBC_MAX_ROW_ARRAY_SIZE, sizeof(estype_row_st)); - if (! type_row) { + /* Both arrays below must be of same size (ESODBC_MAX_NO_TYPES), since no + * SQLFetch() looping is implemented (see check after SQLFetch() below). */ + /* A static estype_row_st array can get too big for the default stack size + * in certain cases: needs allocation on heap. */ + if (! (row_arrays = calloc(ESODBC_MAX_NO_TYPES, + sizeof(*type_row) + sizeof(*row_status)))) { ERRNH(dbc, "OOM"); return FALSE; + } else { + row_status = (SQLUSMALLINT *)row_arrays; + type_row = (estype_row_st *)((char *)row_arrays + + ESODBC_MAX_NO_TYPES * sizeof(*row_status)); } if (! SQL_SUCCEEDED(EsSQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt))) { ERRH(dbc, "failed to alloc a statement handle."); - free(type_row); + free(row_arrays); return FALSE; } assert(stmt); @@ -1423,9 +1574,9 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) } else if (row_cnt <= 0) { ERRH(stmt, "Elasticsearch returned no type as supported."); goto end; - } else if (ESODBC_MAX_ROW_ARRAY_SIZE < row_cnt) { + } else if (ESODBC_MAX_NO_TYPES < row_cnt) { ERRH(stmt, "Elasticsearch returned too many types (%d vs limit %zd).", - row_cnt, ESODBC_MAX_ROW_ARRAY_SIZE); + row_cnt, ESODBC_MAX_NO_TYPES); goto end; } else { DBGH(stmt, "Elasticsearch types rows count: %ld.", row_cnt); @@ -1439,13 +1590,13 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) } /* indicate rowset size */ if (! SQL_SUCCEEDED(EsSQLSetStmtAttrW(stmt, SQL_ATTR_ROW_ARRAY_SIZE, - (SQLPOINTER)ESODBC_MAX_ROW_ARRAY_SIZE, 0))) { + (SQLPOINTER)ESODBC_MAX_NO_TYPES, 0))) { ERRH(stmt, "failed to set rowset size (%zd).", - ESODBC_MAX_ROW_ARRAY_SIZE); + ESODBC_MAX_NO_TYPES); goto end; } /* indicate array to write row status into; initialize with error first */ - for (i = 0; i < ESODBC_MAX_ROW_ARRAY_SIZE; i ++) { + for (i = 0; i < ESODBC_MAX_NO_TYPES; i ++) { row_status[i] = SQL_ROW_ERROR; } if (! SQL_SUCCEEDED(EsSQLSetStmtAttrW(stmt, SQL_ATTR_ROW_STATUS_PTR, @@ -1521,7 +1672,7 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) goto end; } - if (! (pos = copy_types_rows(type_row, rows_fetched, types))) { + if (! (pos = copy_types_rows(dbc, type_row, rows_fetched, types))) { ERRH(dbc, "failed to process recieved ES/SQL data types."); goto end; } @@ -1548,14 +1699,27 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) free(types); types = NULL; } - if (type_row) { - free(type_row); - type_row = NULL; + if (row_arrays) { + free(row_arrays); + row_arrays = NULL; } return ret; } +static int receive_dsn_cb(void *arg, const wchar_t *dsn_str, + wchar_t *err_out, size_t eo_max, unsigned int flags) +{ + assert(arg); + /* The function parses the DSN string into the received attrs, which + * remains assigned past the callback scope */ + return validate_dsn((esodbc_dsn_attrs_st *)arg, dsn_str, err_out, eo_max, + /* Try connect here, to be able to report an error back to the + * user right away: otherwise, the connection will fail later in + * SQLDriverConnect loop, but with no indication as to why. */ + /*connect?*/TRUE); +} + SQLRETURN EsSQLDriverConnectW ( SQLHDBC hdbc, @@ -1586,21 +1750,22 @@ SQLRETURN EsSQLDriverConnectW esodbc_dsn_attrs_st attrs; SQLWCHAR buff_dsn[ESODBC_DSN_MAX_ATTR_LEN]; wstr_st orig_dsn = {buff_dsn, 0}; - BOOL disable_nonconn = FALSE; + BOOL disable_nonconn = TRUE; /* disable non-conn controls by default */ BOOL prompt_user = TRUE; - int res; + long res; size_t cnt; init_dsn_attrs(&attrs); if (szConnStrIn) { - DBGH(dbc, "Input connection string: '"LWPD"'[%d].", szConnStrIn, - cchConnStrIn); +#ifndef NDEBUG /* don't print the PWD */ + DBGH(dbc, "Input connection string: [%hd] `" LWPDL "`.", cchConnStrIn, + (0 <= cchConnStrIn) ? cchConnStrIn : SHRT_MAX, szConnStrIn); +#endif /* NDEBUG */ /* parse conn str into attrs */ if (! parse_connection_string(&attrs, szConnStrIn, cchConnStrIn)) { ERRH(dbc, "failed to parse connection string `" LWPDL "`.", - cchConnStrIn < 0 ? wcslen(szConnStrIn) : cchConnStrIn, - szConnStrIn); + (0 <= cchConnStrIn) ? cchConnStrIn : SHRT_MAX, szConnStrIn); RET_HDIAGS(dbc, SQL_STATE_HY000); } /* original received DSN saved for later query by the app */ @@ -1671,6 +1836,8 @@ SQLRETURN EsSQLDriverConnectW break; case SQL_DRIVER_COMPLETE_REQUIRED: + /* disable non-connection controls, as per the standard (even if + * changing the above default). */ disable_nonconn = TRUE; /* no break */ case SQL_DRIVER_COMPLETE: @@ -1679,13 +1846,14 @@ SQLRETURN EsSQLDriverConnectW /* no break; */ case SQL_DRIVER_PROMPT: /* prompt user first, then try connect */ do { - res = prompt_user ? - prompt_user_config(hdbc, &attrs, FALSE) : 1; + res = prompt_user ? prompt_user_config(hwnd, disable_nonconn, + &attrs, &receive_dsn_cb) : /*need just > 0*/1; if (res < 0) { - ERRH(dbc, "user interaction failed."); + ERRH(dbc, "user GUI interaction failed."); RET_HDIAGS(dbc, SQL_STATE_IM008); } else if (! res) { /* user canceled */ + DBGH(dbc, "user canceled the GUI interaction."); return SQL_NO_DATA; } /* promt user on next iteration */ @@ -1737,17 +1905,92 @@ SQLRETURN EsSQLDriverConnectW res = assign_dsn_attr(&attrs, &MK_WSTR(ESODBC_DSN_DSN), &orig_dsn, /*overwrite?*/TRUE); assert(0 < res); - if (! write_connection_string(&attrs, szConnStrOut, cchConnStrOutMax, - pcchConnStrOut)) { + res = write_connection_string(&attrs, szConnStrOut, cchConnStrOutMax); + if (res < 0) { ERRH(dbc, "failed to build output connection string."); RET_HDIAG(dbc, SQL_STATE_HY000, "failed to build connection " "string", 0); + } else if (pcchConnStrOut) { + *pcchConnStrOut = (SQLSMALLINT)res; } } return SQL_SUCCESS; } +SQLRETURN EsSQLConnectW +( + SQLHDBC hdbc, + _In_reads_(cchDSN) SQLWCHAR *szDSN, + SQLSMALLINT cchDSN, + _In_reads_(cchUID) SQLWCHAR *szUID, + SQLSMALLINT cchUID, + _In_reads_(cchAuthStr) SQLWCHAR *szPWD, + SQLSMALLINT cchPWD +) +{ + esodbc_dsn_attrs_st attrs; + SQLWCHAR buff[sizeof(attrs.buff)/sizeof(*attrs.buff)]; + size_t i; + int res; + SQLSMALLINT written; + esodbc_dbc_st *dbc = DBCH(hdbc); + + /* + * Note: keep the order in sync with loop's switch cases! + */ + wstr_st kw_dsn = MK_WSTR(ESODBC_DSN_DSN); + wstr_st val_dsn = (wstr_st) { + szDSN, cchDSN < 0 ? wcslen(szDSN) : cchDSN + }; + wstr_st kw_uid = MK_WSTR(ESODBC_DSN_UID); + wstr_st val_uid = (wstr_st) { + szUID, cchUID < 0 ? wcslen(szUID) : cchUID + }; + wstr_st kw_pwd = MK_WSTR(ESODBC_DSN_PWD); + wstr_st val_pwd = (wstr_st) { + szPWD, cchPWD < 0 ? wcslen(szPWD) : cchPWD + }; + wstr_st *kws[] = {&kw_dsn, &kw_uid, &kw_pwd}; + wstr_st *vals[] = {&val_dsn, &val_uid, &val_pwd}; + + init_dsn_attrs(&attrs); + + for (i = 0; i < sizeof(kws)/sizeof(*kws); i ++) { + res = assign_dsn_attr(&attrs, kws[i], vals[i], /*overwrite*/FALSE); + if (res < 0) { + ERRH(dbc, "couldn't assign " LWPDL " value [%zu] " + "`" LWPDL "`.", LWSTR(kws[i]), + vals[i]->cnt, LWSTR(vals[i])); + switch (i) { + case 0: /* DSN */ + RET_HDIAGS(hdbc, SQL_STATE_HY090); + case 1: /* UID */ + RET_HDIAGS(hdbc, SQL_STATE_IM010); + case 2: /* PWD */ + RET_HDIAG(hdbc, SQL_STATE_HY000, "Password longer " + "than max (" STR(ESODBC_DSN_MAX_ATTR_LEN) ")", 0); + default: + assert(0); + } + } else { + assert(res == DSN_ASSIGNED); + } + } + + if ((written = (SQLSMALLINT)write_connection_string(&attrs, buff, + sizeof(buff)/sizeof(*buff))) < 0) { + ERRH(dbc, "failed to serialize params as connection string."); + RET_HDIAG(dbc, SQL_STATE_HY000, "failed to serialize connection " + "parameters", 0); + } + + return EsSQLDriverConnectW(dbc, /*win hndlr*/NULL, buff, written, + /*conn str out*/NULL, /*...len*/0, /* out written */NULL, + SQL_DRIVER_NOPROMPT); +} + + /* "Implicitly allocated descriptors can be freed only by calling * SQLDisconnect, which drops any statements or descriptors open on the * connection" */ @@ -1795,23 +2038,9 @@ SQLRETURN EsSQLSetConnectAttrW( * or Unicode. */ INFOH(dbc, "no ANSI/Unicode specific behaviour (app is: %s).", (uintptr_t)Value == SQL_AA_TRUE ? "ANSI" : "Unicode"); - /* TODO: API doesn't require to set a state? */ //state = SQL_STATE_IM001; return SQL_ERROR; /* error means ANSI */ - case SQL_ATTR_LOGIN_TIMEOUT: - if (dbc->es_types) { - ERRH(dbc, "connection already established, can't set " - "connection timeout anymore (to %u).", - (SQLUINTEGER)(uintptr_t)Value); - RET_HDIAG(dbc, SQL_STATE_HY011, "connection established, " - "can't set connection timeout.", 0); - } - INFOH(dbc, "setting connection timeout to: %u, from previous: %u.", - dbc->timeout, (SQLUINTEGER)(uintptr_t)Value); - dbc->timeout = (long)(uintptr_t)Value; - break; - /* https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/automatic-population-of-the-ipd */ case SQL_ATTR_AUTO_IPD: ERRH(dbc, "trying to set read-only attribute AUTO IPD."); @@ -1865,6 +2094,56 @@ SQLRETURN EsSQLSetConnectAttrW( //RET_HDIAGS(dbc, SQL_STATE_HYC00); break; + case SQL_ATTR_ACCESS_MODE: + DBGH(dbc, "setting access mode to: %lu.", + (SQLUINTEGER)(uintptr_t)Value); + if ((SQLUINTEGER)(uintptr_t)Value != SQL_MODE_READ_ONLY) { + WARNH(dbc, "no support for requested access mode."); + } + break; + + case SQL_ATTR_AUTOCOMMIT: + DBGH(dbc, "setting autocommit mode: %lu", + (SQLUINTEGER)(uintptr_t)Value); + if ((SQLUINTEGER)(uintptr_t)Value != SQL_AUTOCOMMIT_ON) { + WARNH(dbc, "no support for manual-commit mode."); + } + break; + + case SQL_ATTR_CONNECTION_DEAD: + RET_HDIAGS(dbc, SQL_STATE_HY092); + break; + + /* coalesce login and connection timeouts for a REST req. */ + case SQL_ATTR_CONNECTION_TIMEOUT: + case SQL_ATTR_LOGIN_TIMEOUT: + DBGH(dbc, "setting login/connection timeout: %lu", + (SQLUINTEGER)(uintptr_t)Value); + if (dbc->timeout && (! Value)) { + /* if one of the values had been set to non-0 (=no timeout), + * keep that value as the timeout. */ + WARNH(dbc, "keeping previous non-0 setting: %lu.", + dbc->timeout); + RET_HDIAGS(dbc, SQL_STATE_01S02); + } /* else: last setting wins */ + dbc->timeout = (SQLUINTEGER)(uintptr_t)Value; + break; + + case SQL_ATTR_TRANSLATE_LIB: + DBGH(dbc, "setting translation to lib: `" LWPD "`.", + (SQLWCHAR *)Value); + ERRH(dbc, "no traslation support available."); + RET_HDIAGS(dbc, SQL_STATE_IM009); + + case SQL_ATTR_TRACE: + case SQL_ATTR_TRACEFILE: /* DM-only */ + case SQL_ATTR_ENLIST_IN_DTC: + case SQL_ATTR_PACKET_SIZE: + case SQL_ATTR_TRANSLATE_OPTION: + case SQL_ATTR_ODBC_CURSORS: /* DM-only & deprecated */ + ERRH(dbc, "unsupported attribute %ld.", Attribute); + RET_HDIAGS(dbc, SQL_STATE_HYC00); + default: ERRH(dbc, "unknown Attribute: %d.", Attribute); RET_HDIAGS(dbc, SQL_STATE_HY092); @@ -1931,22 +2210,48 @@ SQLRETURN EsSQLGetConnectAttrW( break; case SQL_ATTR_ACCESS_MODE: + DBGH(dbc, "getting access mode: %lu", SQL_MODE_READ_ONLY); + *(SQLUINTEGER *)ValuePtr = SQL_MODE_READ_ONLY; + break; + case SQL_ATTR_ASYNC_DBC_EVENT: + ERRH(dbc, "no asynchronous support available."); + RET_HDIAGS(dbc, SQL_STATE_S1118); case SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE: + ERRH(dbc, "no asynchronous support available."); + RET_HDIAGS(dbc, SQL_STATE_HY114); + //case SQL_ATTR_ASYNC_DBC_PCALLBACK: //case SQL_ATTR_ASYNC_DBC_PCONTEXT: + case SQL_ATTR_AUTOCOMMIT: + DBGH(dbc, "getting autocommit mode: %lu", SQL_AUTOCOMMIT_ON); + *(SQLUINTEGER *)ValuePtr = SQL_AUTOCOMMIT_ON; + break; + case SQL_ATTR_CONNECTION_DEAD: + DBGH(dbc, "getting connection state: %lu", SQL_CD_FALSE); + *(SQLUINTEGER *)ValuePtr = SQL_CD_FALSE; + break; + case SQL_ATTR_CONNECTION_TIMEOUT: + case SQL_ATTR_LOGIN_TIMEOUT: + INFOH(dbc, "login == connection timeout."); + DBGH(dbc, "getting login/connection timeout: %lu", dbc->timeout); + *(SQLUINTEGER *)ValuePtr = dbc->timeout; + break; + //case SQL_ATTR_DBC_INFO_TOKEN: + + case SQL_ATTR_TRACE: + case SQL_ATTR_TRACEFILE: /* DM-only */ case SQL_ATTR_ENLIST_IN_DTC: - case SQL_ATTR_LOGIN_TIMEOUT: - case SQL_ATTR_ODBC_CURSORS: case SQL_ATTR_PACKET_SIZE: - case SQL_ATTR_TRACE: - case SQL_ATTR_TRACEFILE: case SQL_ATTR_TRANSLATE_LIB: case SQL_ATTR_TRANSLATE_OPTION: + case SQL_ATTR_ODBC_CURSORS: /* DM-only & deprecated */ + ERRH(dbc, "unsupported attribute %ld.", Attribute); + RET_HDIAGS(dbc, SQL_STATE_HY000); default: // FIXME: add the other attributes diff --git a/driver/connect.h b/driver/connect.h index f2072ca2..c08ddb4a 100644 --- a/driver/connect.h +++ b/driver/connect.h @@ -9,11 +9,15 @@ #include "error.h" #include "handles.h" +#include "dsn.h" BOOL connect_init(); void connect_cleanup(); + SQLRETURN post_json(esodbc_stmt_st *stmt, const cstr_st *u8body); void cleanup_dbc(esodbc_dbc_st *dbc); +SQLRETURN do_connect(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs); +SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs); SQLRETURN EsSQLDriverConnectW @@ -27,7 +31,16 @@ SQLRETURN EsSQLDriverConnectW _Out_opt_ SQLSMALLINT *pcchConnStrOut, SQLUSMALLINT fDriverCompletion ); - +SQLRETURN EsSQLConnectW +( + SQLHDBC hdbc, + _In_reads_(cchDSN) SQLWCHAR *szDSN, + SQLSMALLINT cchDSN, + _In_reads_(cchUID) SQLWCHAR *szUID, + SQLSMALLINT cchUID, + _In_reads_(cchAuthStr) SQLWCHAR *szPWD, + SQLSMALLINT cchPWD +); SQLRETURN EsSQLDisconnect(SQLHDBC ConnectionHandle); SQLRETURN EsSQLSetConnectAttrW( diff --git a/driver/convert.c b/driver/convert.c index a3d4da02..b5c9787b 100644 --- a/driver/convert.c +++ b/driver/convert.c @@ -11,7 +11,7 @@ #include -#include "log.h" +#include "handles.h" #define JSON_VAL_NULL "null" #define JSON_VAL_TRUE "true" @@ -430,7 +430,7 @@ static size_t buff_octet_size( /* apply "network" truncation first, if need to */ if (0 < max && max < avail) { - INFO("applying 'network' truncation %zd -> %zd.", avail, max); + INFOH(stmt, "applying 'network' truncation %zd -> %zd.", avail, max); max_copy = max; /* no truncation indicated for this case */ } else { @@ -441,7 +441,7 @@ static size_t buff_octet_size( /* Note: this should only be tested/applied if ARD.meta_type == STR||BIN */ // FIXME: check note above if (room < max_copy) { - INFO("applying buffer truncation %zd -> %zd.", max_copy, room); + INFOH(stmt, "applying buffer truncation %zd -> %zd.", max_copy, room); max_copy = room; *state = SQL_STATE_01004; } @@ -451,7 +451,7 @@ static size_t buff_octet_size( max_copy -= max_copy % unit_size; } - DBG("avail=%zd, room=%zd, attr_max=%zd, metatype:%d => " + DBGH(stmt, "avail=%zd, room=%zd, attr_max=%zd, metatype:%d => " "max_copy=%zd, state=%d.", avail, room, attr_max, ird_mt, max_copy, *state); return max_copy; @@ -476,7 +476,8 @@ static inline void write_out_octets( size_t max; if (! octet_len_ptr) { - DBG("NULL octet len pointer, length (%zd) not indicated.", avail); + DBGH(stmt, "NULL octet len pointer, length (%zu) not indicated.", + avail); return; } @@ -490,14 +491,14 @@ static inline void write_out_octets( * occupies after conversion: "the driver has no way of * figuring out what the actual length is" */ *octet_len_ptr = max; - DBG("max length (%zd) attribute enforced.", max); + DBGH(stmt, "max length (%zd) attribute enforced.", max); } else { /* if no "network" truncation done, indicate data's length, no * matter if truncated to buffer's size or not */ *octet_len_ptr = avail; } - DBG("length of data available for transfer: %ld", *octet_len_ptr); + DBGH(stmt, "length of data available for transfer: %ld", *octet_len_ptr); } /* if an application doesn't specify the conversion, use column's type */ @@ -1359,8 +1360,8 @@ static SQLRETURN wstr_to_wstr(esodbc_rec_st *arec, esodbc_rec_st *irec, * xstr needs to be trimmed to exact data (no padding, no 0-term counted). * If ts_buff is non-NULL, the xstr will be copied (possibly W-to-C converted) * into it. */ -static BOOL xstr_to_timestamp_struct(xstr_st *xstr, TIMESTAMP_STRUCT *tss, - cstr_st *ts_buff) +static BOOL xstr_to_timestamp_struct(esodbc_stmt_st *stmt, xstr_st *xstr, + TIMESTAMP_STRUCT *tss, cstr_st *ts_buff) { /* need the 0-term in the buff, since ascii_w2c will write it */ char buff[sizeof(ESODBC_ISO8601_TEMPLATE)/*+\0*/]; @@ -1378,18 +1379,20 @@ static BOOL xstr_to_timestamp_struct(xstr_st *xstr, TIMESTAMP_STRUCT *tss, } if (xstr->wide) { - DBG("converting ISO 8601 `" LWPDL "` to timestamp.", LWSTR(&xstr->w)); + DBGH(stmt, "converting ISO 8601 `" LWPDL "` to timestamp.", + LWSTR(&xstr->w)); if (sizeof(ESODBC_ISO8601_TEMPLATE) - 1 < xstr->w.cnt) { - ERR("`" LWPDL "` not a TIMESTAMP.", LWSTR(&xstr->w)); + ERRH(stmt, "`" LWPDL "` not a TIMESTAMP.", LWSTR(&xstr->w)); return FALSE; } /* convert the W-string to C-string; also, copy it directly into out * ts_buff, if given (thus saving one extra copying) */ ts_ptr->cnt = ascii_w2c(xstr->w.str, ts_ptr->str, xstr->w.cnt) - 1; } else { - DBG("converting ISO 8601 `" LCPDL "` to timestamp.", LCSTR(&xstr->c)); + DBGH(stmt, "converting ISO 8601 `" LCPDL "` to timestamp.", + LCSTR(&xstr->c)); if (sizeof(ESODBC_ISO8601_TEMPLATE) - 1 < xstr->c.cnt) { - ERR("`" LCPDL "` not a TIMESTAMP.", LCSTR(&xstr->c)); + ERRH(stmt, "`" LCPDL "` not a TIMESTAMP.", LCSTR(&xstr->c)); return FALSE; } /* no conversion needed; but copying to the out ts_buff, if given */ @@ -1404,13 +1407,14 @@ static BOOL xstr_to_timestamp_struct(xstr_st *xstr, TIMESTAMP_STRUCT *tss, /* len counts the 0-term */ if (ts_ptr->cnt <= 1 || timestamp_parse(ts_ptr->str, ts_ptr->cnt, &tsp) || (! timestamp_to_tm_local(&tsp, &tmp))) { - ERR("data `" LCPDL "` not an ANSI ISO 8601 format.", LCSTR(ts_ptr)); + ERRH(stmt, "data `" LCPDL "` not an ANSI ISO 8601 format.", + LCSTR(ts_ptr)); return FALSE; } TM_TO_TIMESTAMP_STRUCT(&tmp, tss); tss->fraction = tsp.nsec / 1000000; - DBG("parsed ISO 8601: `%04d-%02d-%02dT%02d:%02d:%02d.%u+%dm`.", + DBGH(stmt, "parsed ISO 8601: `%04d-%02d-%02dT%02d:%02d:%02d.%u+%dm`.", tss->year, tss->month, tss->day, tss->hour, tss->minute, tss->second, tss->fraction, tsp.offset); @@ -1419,8 +1423,8 @@ static BOOL xstr_to_timestamp_struct(xstr_st *xstr, TIMESTAMP_STRUCT *tss, } -static BOOL parse_timedate(xstr_st *xstr, TIMESTAMP_STRUCT *tss, - SQLSMALLINT *format, cstr_st *ts_buff) +static BOOL parse_timedate(esodbc_stmt_st *stmt, xstr_st *xstr, + TIMESTAMP_STRUCT *tss, SQLSMALLINT *format, cstr_st *ts_buff) { /* template buffer: date or time values will be copied in place and * evaluated as a timestamp (needs to be valid) */ @@ -1433,7 +1437,7 @@ static BOOL parse_timedate(xstr_st *xstr, TIMESTAMP_STRUCT *tss, /* is this a TIMESTAMP? */ if (sizeof(ESODBC_TIME_TEMPLATE) - 1 < XSTR_LEN(xstr)) { /* longer than a date-value -> try a timestamp */ - if (! xstr_to_timestamp_struct(xstr, tss, ts_buff)) { + if (! xstr_to_timestamp_struct(stmt, xstr, tss, ts_buff)) { return FALSE; } if (format) { @@ -1461,8 +1465,8 @@ static BOOL parse_timedate(xstr_st *xstr, TIMESTAMP_STRUCT *tss, templ[sizeof(ESODBC_DATE_TEMPLATE) - 1 + td.cnt] = 'Z'; xtd.c.str = templ; xtd.c.cnt = td.cnt + sizeof(ESODBC_DATE_TEMPLATE); - if (! xstr_to_timestamp_struct(&xtd, tss, ts_buff)) { - ERR("`" LCPDL "` not a TIME.", LCSTR(&td)); + if (! xstr_to_timestamp_struct(stmt, &xtd, tss, ts_buff)) { + ERRH(stmt, "`" LCPDL "` not a TIME.", LCSTR(&td)); return FALSE; } else { tss->year = tss->month = tss->day = 0; @@ -1480,8 +1484,8 @@ static BOOL parse_timedate(xstr_st *xstr, TIMESTAMP_STRUCT *tss, memcpy(templ, td.str, td.cnt); xtd.c.str = templ; xtd.c.cnt = sizeof(templ)/sizeof(templ[0]) - 1; - if (! xstr_to_timestamp_struct(&xtd, tss, ts_buff)) { - ERR("`" LCPDL "` not a DATE.", LCSTR(&td)); + if (! xstr_to_timestamp_struct(stmt, &xtd, tss, ts_buff)) { + ERRH(stmt, "`" LCPDL "` not a DATE.", LCSTR(&td)); return FALSE; } else { tss->hour = tss->minute = tss->second = 0; @@ -1493,7 +1497,7 @@ static BOOL parse_timedate(xstr_st *xstr, TIMESTAMP_STRUCT *tss, return TRUE; } - ERR("`" LCPDL "` not a Time/Date/Timestamp.", LCSTR(&td)); + ERRH(stmt, "`" LCPDL "` not a Time/Date/Timestamp.", LCSTR(&td)); return FALSE; } @@ -1527,7 +1531,7 @@ static SQLRETURN wstr_to_timestamp(esodbc_rec_st *arec, esodbc_rec_st *irec, switch (irec->concise_type) { case SQL_TYPE_TIMESTAMP: - if (! xstr_to_timestamp_struct(&xstr, tss, NULL)) { + if (! xstr_to_timestamp_struct(stmt, &xstr, tss, NULL)) { RET_HDIAGS(stmt, SQL_STATE_22018); } if (format) { @@ -1535,7 +1539,7 @@ static SQLRETURN wstr_to_timestamp(esodbc_rec_st *arec, esodbc_rec_st *irec, } break; case SQL_VARCHAR: - if (! parse_timedate(&xstr, tss, format, NULL)) { + if (! parse_timedate(stmt, &xstr, tss, format, NULL)) { RET_HDIAGS(stmt, SQL_STATE_22018); } break; @@ -1957,8 +1961,8 @@ SQLRETURN sql2c_convertible(esodbc_stmt_st *stmt) * needs to be set; * Returns success of conversion and pointer to trimmed number str * representation. */ -static BOOL xstr_to_number(void *data_ptr, SQLLEN *octet_len_ptr, - xstr_st *xstr, SQLSMALLINT dest_type, void *dest) +static BOOL xstr_to_number(esodbc_stmt_st *stmt, void *data_ptr, + SQLLEN *octet_len_ptr, xstr_st *xstr, SQLSMALLINT dest_type, void *dest) { BOOL res; @@ -1985,7 +1989,7 @@ static BOOL xstr_to_number(void *data_ptr, SQLLEN *octet_len_ptr, if (xstr->wide) { wtrim_ws(&xstr->w); - DBG("converting paramter value `" LWPDL "` to number.", + DBGH(stmt, "converting paramter value `" LWPDL "` to number.", LWSTR(&xstr->w)); switch (dest_type) { case SQL_C_SBIGINT: @@ -2002,7 +2006,7 @@ static BOOL xstr_to_number(void *data_ptr, SQLLEN *octet_len_ptr, } } else { trim_ws(&xstr->c); - DBG("converting paramter value `" LCPDL "` to number.", + DBGH(stmt, "converting paramter value `" LCPDL "` to number.", LCSTR(&xstr->c)); switch (dest_type) { case SQL_C_SBIGINT: @@ -2021,10 +2025,10 @@ static BOOL xstr_to_number(void *data_ptr, SQLLEN *octet_len_ptr, if (! res) { if (xstr->wide) { - ERR("can't convert `" LWPDL "` to type %hd number.", + ERRH(stmt, "can't convert `" LWPDL "` to type %hd number.", LWSTR(&xstr->w), dest_type); } else { - ERR("can't convert `" LCPDL "` to type %hd number.", + ERRH(stmt, "can't convert `" LCPDL "` to type %hd number.", LCSTR(&xstr->c), dest_type); } return FALSE; @@ -2093,8 +2097,8 @@ SQLRETURN c2sql_boolean(esodbc_rec_st *arec, esodbc_rec_st *irec, octet_len_ptr = deferred_address(SQL_DESC_OCTET_LENGTH_PTR, pos, arec); xstr.wide = ctype == SQL_C_WCHAR; - if (! xstr_to_number(data_ptr, octet_len_ptr, &xstr, SQL_C_DOUBLE, - &dbl)) { + if (! xstr_to_number(stmt, data_ptr, octet_len_ptr, &xstr, + SQL_C_DOUBLE, &dbl)) { RET_HDIAGS(stmt, SQL_STATE_22018); } ret = double_to_bool(stmt, dbl, &val); @@ -2193,7 +2197,7 @@ static SQLRETURN string_to_number(esodbc_rec_st *arec, esodbc_rec_st *irec, xstr.wide = wide; /* do a conversion check: use double, as a capture all cases * value: ES/SQL will accept a float for an INTEGER param */ - if (! xstr_to_number(data_ptr, octet_len_ptr, &xstr, + if (! xstr_to_number(stmt, data_ptr, octet_len_ptr, &xstr, SQL_C_DOUBLE, dest ? &dbl : NULL)) { ERRH(stmt, "failed to convert param value to a double."); RET_HDIAGS(stmt, SQL_STATE_22018); @@ -2536,7 +2540,7 @@ static SQLRETURN convert_str_to_timestamp(esodbc_stmt_st *stmt, assert(dest); ts_buff.str = dest; ts_buff.cnt = sizeof(ESODBC_ISO8601_TEMPLATE) - 1; - if (! parse_timedate(&xstr, &tss, &format, &ts_buff)) { + if (! parse_timedate(stmt, &xstr, &tss, &format, &ts_buff)) { ERRH(stmt, "failed to parse input as Time/Date/Timestamp"); RET_HDIAGS(stmt, SQL_STATE_22008); } else if (format == SQL_TYPE_TIME) { @@ -2599,7 +2603,7 @@ static SQLRETURN convert_ts_to_timestamp(esodbc_stmt_st *stmt, tss->hour, tss->minute, tss->second, tss->fraction); if (cnt < 0) { ERRH(stmt, "failed printing timestamp struct: %s.", strerror(errno)); - SET_HDIAG(stmt, SQL_STATE_HY000, "C runtime error", 0); + RET_HDIAG(stmt, SQL_STATE_HY000, "C runtime error", 0); } *len = cnt; diff --git a/driver/defs.h b/driver/defs.h index ea2144be..943ca27b 100644 --- a/driver/defs.h +++ b/driver/defs.h @@ -17,16 +17,19 @@ #define ESODBC_LOG_FILE_PREFIX "esodbc" #define ESODBC_LOG_FILE_SUFFIX ".log" /* Environment variable name holding the log directory name */ -#define ESODBC_ENV_VAR_LOG_DIR "ESODBC_LOG_DIR" +#define ESODBC_LOG_DIR_ENV_VAR "ESODBC_LOG_DIR" +/* number of consecutive logging failures that will disable logging */ +#define ESODBC_LOG_MAX_RETRY 5 -// FIXME: review@alpha -/* TODO: should there be a max? */ -#define ESODBC_MAX_ROW_ARRAY_SIZE 128 +#define ESODBC_MAX_ROW_ARRAY_SIZE USHRT_MAX +/* max number of ES/SQL types supported */ +#define ESODBC_MAX_NO_TYPES 64 #define ESODBC_DEF_ARRAY_SIZE 1 /* max cols or args to bind; needs to stay <= SHRT_MAX */ -#define ESODBC_MAX_DESC_COUNT 128 -/* number of static records for SQLGetData() */ -#define ESODBC_GD_DESC_COUNT ESODBC_MAX_DESC_COUNT +#define ESODBC_MAX_DESC_COUNT SHRT_MAX +/* number of static records for SQLGetData(): if using SQLGetData() with more + * columns than this def, recs will be allocated dynamically. */ +#define ESODBC_GD_DESC_COUNT 128 /* values for SQL_ATTR_MAX_LENGTH statement attribute */ #define ESODBC_UP_MAX_LENGTH 0 // USHORT_MAX #define ESODBC_LO_MAX_LENGTH 0 @@ -45,15 +48,13 @@ #define ESODBC_QUOTE_CHAR "\"" #define ESODBC_PATTERN_ESCAPE "\\" #define ESODBC_CATALOG_SEPARATOR ":" -#define ESODBC_CATALOG_TERM "clusterName" -#define ESODBC_TABLE_TERM "type" // TODO: or table? +#define ESODBC_CATALOG_TERM "catalog" +#define ESODBC_TABLE_TERM "table" #define ESODBC_SCHEMA_TERM "schema" #define ESODBC_PARAM_MARKER "?" -/* maximum identifer length */ -/* TODO: review@alpha */ -/* match 'keyword' ES type length */ -#define ESODBC_MAX_IDENTIFIER_LEN 256 +/* maximum identifer length: match ES/Lucene byte max */ +#define ESODBC_MAX_IDENTIFIER_LEN SHRT_MAX /* "the relationship between the columns in the GROUP BY clause and the * nonaggregated columns in the select list" */ #define ESODBC_GROUP_BY SQL_GB_NO_RELATION @@ -119,8 +120,8 @@ /* driver version ex. 7.0.0(b0a34b4,u,d) */ #define ESODBC_DRIVER_VER STR(DRV_VERSION) \ "(" STR(DRV_SRC_VER) "," STR(DRV_ENCODING) "," STR(DRV_BUILD_TYPE) ")" -/* TODO: learn it from ES */ -#define ESODBC_ELASTICSEARCH_VER "7.x" +/* TODO: POST / (together with cluster "sniffing") */ +#define ESODBC_ELASTICSEARCH_VER "7.0.0" #define ESODBC_ELASTICSEARCH_NAME "Elasticsearch" /* @@ -144,8 +145,11 @@ #define ESODBC_DEF_FOLLOW "yes" /* packing of REST bodies (JSON or CBOR) */ #define ESODBC_DEF_PACKING "JSON" +/* default tracing activation */ +#define ESODBC_DEF_TRACE_ENABLED "0" /* default tracing level */ #define ESODBC_DEF_TRACE_LEVEL "WARN" +#define ESODBC_PWD_VAL_SUBST "" /* * @@ -172,8 +176,7 @@ #define ESODBC_CATALOG_USAGE (0 | \ SQL_CU_DML_STATEMENTS | SQL_CU_PROCEDURE_INVOCATION) /* what's allowed in an identifier name (eg. table, column, index) except - * [a-zA-Z0-9_], accepted in a delimited specification -> all printable ASCII - * (assuming ASCII is the limitation?? TODO ), 0x20-0x7E. */ + * [a-zA-Z0-9_], accepted in a delimited specification. */ #define ESODBC_SPECIAL_CHARACTERS " !\"#$%&'()*+,-./" /*[0-9]*/ \ ";<=>?@" /*[A-Z]*/ "[\\]^" /*[_]*/ "`" /*[a-z]*/ "{|}~" /* SQLFetchScroll() and SQLSetPos() capabilities. @@ -204,37 +207,50 @@ /* * String functions support: - * - supported: none. - * - not supported: ASCII, BIT_LENGTH, CHAR, CHAR_LENGTH, CHARACTER_LENGTH, - * CONCAT, DIFFERENCE, INSERT, LCASE, LEFT, LENGTH, LOCATE, LTRIM, - * OCTET_LENGTH, POSITION, REPEAT, REPLACE, RIGHT, RTRIM, SOUNDEX, SPACE, - * SUBSTRING, UCASE. + * - supported: ASCII, BIT_LENGTH, CHAR, CHAR_LENGTH, CHARACTER_LENGTH, + * CONCAT, INSERT, LCASE, LEFT, LENGTH, LOCATE, LTRIM, OCTET_LENGTH, + * POSITION, REPEAT, REPLACE, RIGHT, RTRIM, SPACE, SUBSTRING, UCASE. + * - not supported: DIFFERENCE, SOUNDEX. */ -#define ESODBC_STRING_FUNCTIONS 0LU +#define ESODBC_STRING_FUNCTIONS (0LU | \ + SQL_FN_STR_ASCII | SQL_FN_STR_BIT_LENGTH | SQL_FN_STR_CHAR | \ + SQL_FN_STR_CHAR_LENGTH | SQL_FN_STR_CHARACTER_LENGTH | \ + SQL_FN_STR_CONCAT | SQL_FN_STR_INSERT | SQL_FN_STR_LCASE | \ + SQL_FN_STR_LEFT | SQL_FN_STR_LENGTH | SQL_FN_STR_LOCATE | \ + SQL_FN_STR_LTRIM | SQL_FN_STR_OCTET_LENGTH | SQL_FN_STR_POSITION | \ + SQL_FN_STR_REPEAT | SQL_FN_STR_REPLACE | SQL_FN_STR_RIGHT | \ + SQL_FN_STR_RTRIM | SQL_FN_STR_SPACE | SQL_FN_STR_SUBSTRING | \ + SQL_FN_STR_UCASE) /* * Numeric functions support: - * - supported: ABS, ACOS, ASIN, ATAN, CEIL, COS, DEGREES, EXP, FLOOR, LOG, - * LOG10, PI, RADIANS, ROUND, SIN, SQRT, TAN; - * - not supported: ATAN2, COT, MOD, POWER, RAND, SIGN, TRUNCATE. - * Note: CEIL supported, CEILING not. + * - supported: ABS, ACOS, ASIN, ATAN, ATAN2, CEIL, COS, COT, DEGREES, EXP, + * FLOOR, LOG, LOG10, MOD, PI, POWER, RADIANS, RAND, ROUND, SIGN, SIN, SQRT, + * TAN, TRUNCATE; + * - not supported: none. */ -#define ESODBC_NUMERIC_FUNCTIONS (0 | \ +#define ESODBC_NUMERIC_FUNCTIONS (0LU | \ SQL_FN_NUM_ABS | SQL_FN_NUM_ACOS | SQL_FN_NUM_ASIN | SQL_FN_NUM_ATAN |\ - SQL_FN_NUM_CEILING | SQL_FN_NUM_COS | SQL_FN_NUM_DEGREES | \ - SQL_FN_NUM_EXP | SQL_FN_NUM_FLOOR | SQL_FN_NUM_LOG | \ - SQL_FN_NUM_LOG10 | SQL_FN_NUM_PI | SQL_FN_NUM_RADIANS | \ - SQL_FN_NUM_ROUND | SQL_FN_NUM_SIN | SQL_FN_NUM_SQRT | SQL_FN_NUM_TAN) + SQL_FN_NUM_ATAN2 | SQL_FN_NUM_CEILING | SQL_FN_NUM_COS | \ + SQL_FN_NUM_COT | SQL_FN_NUM_DEGREES | SQL_FN_NUM_EXP | \ + SQL_FN_NUM_FLOOR | SQL_FN_NUM_LOG | SQL_FN_NUM_LOG10 | \ + SQL_FN_NUM_MOD | SQL_FN_NUM_PI | SQL_FN_NUM_POWER | \ + SQL_FN_NUM_RADIANS | SQL_FN_NUM_RAND | SQL_FN_NUM_ROUND | \ + SQL_FN_NUM_SIGN | SQL_FN_NUM_SIN | SQL_FN_NUM_SQRT | SQL_FN_NUM_TAN | \ + SQL_FN_NUM_TRUNCATE) /* * Timedate functions support: - * - supported: EXTRACT, HOUR, MINUTE, MONTH, SECOND, WEEK, YEAR; + * - supported: DAYNAME, DAYOFMONTH, DAYOFWEEK, DAYOFYEAR, EXTRACT, HOUR, + * MINUTE, MONTH, MONTHNAME, QUARTER, SECOND, WEEK, YEAR; * - not supported: CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURDATE, - * CURTIME, DAYNAME, MONTHNAME, NOW, QUARTER, TIMESTAMPADD, TIMESTAMPDIFF. - * Wrong `_` name?: DAYOFMONTH, DAYOFWEEK, DAYOFYEAR (mysql, db2, oracle) + * CURTIME, NOW, TIMESTAMPADD, TIMESTAMPDIFF. */ -#define ESODBC_TIMEDATE_FUNCTIONS (0 | \ - SQL_FN_TD_EXTRACT | SQL_FN_TD_HOUR | SQL_FN_TD_MINUTE | \ - SQL_FN_TD_MONTH | SQL_FN_TD_SECOND | SQL_FN_TD_WEEK | SQL_FN_TD_YEAR) -//SQL_FN_TD_DAYOFMONTH | SQL_FN_TD_DAYOFWEEK | SQL_FN_TD_DAYOFYEAR +#define ESODBC_TIMEDATE_FUNCTIONS (0LU | \ + SQL_FN_TD_DAYNAME | SQL_FN_TD_DAYOFMONTH | SQL_FN_TD_DAYOFWEEK | \ + SQL_FN_TD_DAYOFYEAR | SQL_FN_TD_EXTRACT | SQL_FN_TD_HOUR | \ + SQL_FN_TD_MINUTE | SQL_FN_TD_MONTH | SQL_FN_TD_MONTHNAME | \ + SQL_FN_TD_QUARTER | SQL_FN_TD_SECOND | SQL_FN_TD_WEEK | \ + SQL_FN_TD_YEAR) + /* * TIMESTAMPDIFF timestamp intervals: * - supported: none. diff --git a/driver/dsn.c b/driver/dsn.c index dfbb83fc..c7a5d3ff 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -9,13 +9,16 @@ #include "dsn.h" #include "log.h" +#include "connect.h" +#include "info.h" +#include "util.h" #define ODBC_REG_SUBKEY_PATH "SOFTWARE\\ODBC\\ODBC.INI" #define REG_HKLM "HKEY_LOCAL_MACHINE" #define REG_HKCU "HKEY_CURRENT_USER" -void init_dsn_attrs(esodbc_dsn_attrs_st *attrs) +void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs) { size_t i; wstr_st *wstr; @@ -28,6 +31,15 @@ void init_dsn_attrs(esodbc_dsn_attrs_st *attrs) } } +/* used with LWSTR() macro, so invoked twice, but it is: + * - executed only if the message level is low enough (i.e. it's logged); + * - more compact this way. */ +static inline wstr_st *mask_pwd(wstr_st *attr, wstr_st *val) +{ + static wstr_st subst = WSTR_INIT(ESODBC_PWD_VAL_SUBST); + return EQ_CASE_WSTR(attr, &MK_WSTR(ESODBC_DSN_PWD)) ? &subst : val; +} + #define DSN_NOT_MATCHED 0 #define DSN_NOT_OVERWRITTEN 1 #define DSN_ASSIGNED 2 @@ -65,12 +77,16 @@ int assign_dsn_attr(esodbc_dsn_attrs_st *attrs, {&MK_WSTR(ESODBC_DSN_PACKING), &attrs->packing}, {&MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE), &attrs->max_fetch_size}, {&MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), &attrs->max_body_size}, + {&MK_WSTR(ESODBC_DSN_TRACE_ENABLED), &attrs->trace_enabled}, {&MK_WSTR(ESODBC_DSN_TRACE_FILE), &attrs->trace_file}, {&MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &attrs->trace_level}, {NULL, NULL} }; int ret; + assert(sizeof(map)/sizeof(*iter) - /* {NULL,NULL} terminator */1 == + ESODBC_DSN_ATTRS_COUNT); + if (ESODBC_DSN_MAX_ATTR_LEN < value->cnt) { ERR("attribute value lenght too large: %zu; max=%zu.", value->cnt, ESODBC_DSN_MAX_ATTR_LEN); @@ -86,16 +102,18 @@ int assign_dsn_attr(esodbc_dsn_attrs_st *attrs, if (! overwrite) { INFO("keyword '" LWPDL "' already assigned; " "ignoring new `" LWPDL "`, keeping previous `" LWPDL "`.", - LWSTR(iter->kw), LWSTR(value), LWSTR(iter->val)); + LWSTR(iter->kw), LWSTR(mask_pwd(iter->kw, value)), + LWSTR(mask_pwd(iter->kw, iter->val))); return DSN_NOT_OVERWRITTEN; } INFO("keyword '" LWPDL "' already assigned: " "overwriting previous `" LWPDL "` with new `" LWPDL "`.", - LWSTR(iter->kw), LWSTR(iter->val), LWSTR(value)); + LWSTR(iter->kw), LWSTR(mask_pwd(iter->kw, iter->val)), + LWSTR(mask_pwd(iter->kw, value))); ret = DSN_OVERWRITTEN; } else { INFO("keyword '" LWPDL "' new assignment: `" LWPDL "`.", - LWSTR(iter->kw), LWSTR(value)); + LWSTR(iter->kw), LWSTR(mask_pwd(iter->kw, value))); ret = DSN_ASSIGNED; } memcpy(iter->val->str, value->str, value->cnt * sizeof(*value->str)); @@ -272,7 +290,7 @@ static SQLWCHAR *parse_separator(SQLWCHAR **pos, SQLWCHAR *end) * * `{` and `}` allowed within {} * * brances need to be returned to out-str; */ -BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, +BOOL TEST_API parse_connection_string(esodbc_dsn_attrs_st *attrs, SQLWCHAR *szConnStrIn, SQLSMALLINT cchConnStrIn) { @@ -282,6 +300,7 @@ BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, /* parse and assign attributes in connection string */ pos = szConnStrIn; + /* if NTS, parse till skip_ws() encounters the \0 */ end = pos + (cchConnStrIn == SQL_NTS ? SHRT_MAX : cchConnStrIn); while (skip_ws(&pos, end, TRUE)) { @@ -302,7 +321,7 @@ BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, } if (! skip_ws(&pos, end, FALSE)) { - return FALSE; + continue; /* empty values are acceptable */ } if (! parse_token(TRUE, &pos, end, &value)) { @@ -312,7 +331,7 @@ BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, } DBG("read connection string attribute: `" LWPDL "` = `" LWPDL "`.", - LWSTR(&keyword), LWSTR(&value)); + LWSTR(&keyword), LWSTR(mask_pwd(&keyword, &value))); if (assign_dsn_attr(attrs, &keyword, &value, TRUE) < 0) { ERRN("failed to assign keyword `" LWPDL "` with val `" LWPDL "`.", LWSTR(&keyword), LWSTR(&value)); @@ -323,7 +342,7 @@ BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, return TRUE; } -BOOL parse_00_list(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00) +BOOL TEST_API parse_00_list(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00) { SQLWCHAR *pos; size_t cnt; @@ -343,6 +362,98 @@ BOOL parse_00_list(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00) return TRUE; } +static inline BOOL needs_braces(wstr_st *token) +{ + size_t i; + + for (i = 0; i < token->cnt; i ++) { + switch(token->str[i]) { + case ' ': + case '\t': + case '\r': + case '\n': + case '=': + case ';': + return TRUE; + } + } + return FALSE; +} + +long TEST_API write_00_list(esodbc_dsn_attrs_st *attrs, + SQLWCHAR *list00, size_t cnt00) +{ + struct { + wstr_st *kw; + wstr_st *val; + } *iter, map[] = { + {&MK_WSTR(ESODBC_DSN_DRIVER), &attrs->driver}, + {&MK_WSTR(ESODBC_DSN_DESCRIPTION), &attrs->description}, + {&MK_WSTR(ESODBC_DSN_DSN), &attrs->dsn}, + {&MK_WSTR(ESODBC_DSN_PWD), &attrs->pwd}, + {&MK_WSTR(ESODBC_DSN_UID), &attrs->uid}, + {&MK_WSTR(ESODBC_DSN_SAVEFILE), &attrs->savefile}, + {&MK_WSTR(ESODBC_DSN_FILEDSN), &attrs->filedsn}, + {&MK_WSTR(ESODBC_DSN_SERVER), &attrs->server}, + {&MK_WSTR(ESODBC_DSN_PORT), &attrs->port}, + {&MK_WSTR(ESODBC_DSN_SECURE), &attrs->secure}, + {&MK_WSTR(ESODBC_DSN_CA_PATH), &attrs->ca_path}, + {&MK_WSTR(ESODBC_DSN_TIMEOUT), &attrs->timeout}, + {&MK_WSTR(ESODBC_DSN_FOLLOW), &attrs->follow}, + {&MK_WSTR(ESODBC_DSN_CATALOG), &attrs->catalog}, + {&MK_WSTR(ESODBC_DSN_PACKING), &attrs->packing}, + {&MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE), &attrs->max_fetch_size}, + {&MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), &attrs->max_body_size}, + {&MK_WSTR(ESODBC_DSN_TRACE_ENABLED), &attrs->trace_enabled}, + {&MK_WSTR(ESODBC_DSN_TRACE_FILE), &attrs->trace_file}, + {&MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &attrs->trace_level}, + {NULL, NULL} + }; + size_t pos; + BOOL add_braces; + + /* check that the esodbc_dsn_attrs_st stays in sync with the above */ + assert(sizeof(map)/sizeof(*iter) - /* {NULL,NULL} terminator */1 == + ESODBC_DSN_ATTRS_COUNT); + + + for (iter = &map[0], pos = 0; iter->val; iter ++) { + if (! iter->val->cnt) { + continue; + } + /* the braces aren't really needed in a doubly-null-terminated list, + * but would make a conversion to a "normal" (`;` or `|` separated) + * connection string easy */ + add_braces = needs_braces(iter->val); + if (cnt00 - /*final \0*/1 < pos + iter->kw->cnt + /*`=`*/1 + + (add_braces ? 2 : 0) + iter->val->cnt) { + ERR("not enough room in destination buffer."); + return -1; + } + /* copy keyword */ + memcpy(list00 + pos, iter->kw->str, iter->kw->cnt * sizeof(*list00)); + pos += iter->kw->cnt; + /* copy attribute value separator (`=`) and brace if needed */ + list00[pos ++] = L'='; + if (add_braces) { + list00[pos ++] = L'{'; + } + /* copy value */ + memcpy(list00 + pos, iter->val->str, iter->val->cnt * sizeof(*list00)); + pos += iter->val->cnt; + /* close any open brace */ + if (add_braces) { + list00[pos ++] = L'}'; + } + /* close current attribute */ + list00[pos ++] = L'\0'; + } + assert(pos < cnt00); + list00[pos ++] = L'\0'; + + return (long)pos; +} + static void log_installer_err() { DWORD ecode; @@ -352,10 +463,49 @@ static void log_installer_err() while (SQL_SUCCEEDED(SQLInstallerError(++ i, &ecode, buff, sizeof(buff)/sizeof(buff[0]), &msg_len))) { - ERR("#%i: errcode=%d: " LWPDL ".", i, ecode, msg_len, buff); + ERR("#%i: errcode=%d: " LWPDL ".", i, ecode, + msg_len/sizeof(*buff), buff); } } +size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max) +{ + DWORD ecode; + SQLWCHAR buff[SQL_MAX_MESSAGE_LENGTH]; + WORD msg_len; + int i, res; + size_t pos; + + i = 0; + pos = 0; + while (SQL_SUCCEEDED(SQLInstallerError(++ i, &ecode, buff, + sizeof(buff)/sizeof(buff[0]), &msg_len))) { + /* Note: msg_len is actually count of chars, current doc is wrong */ + /* if message is larger than buffer, the untruncated size is returned*/ + if (sizeof(buff)/sizeof(buff[0]) <= msg_len) { + msg_len = sizeof(buff)/sizeof(buff[0]) ; + } + + assert(pos <= eb_max); + DBG("error #%d: " LWPD " [%d].", i, buff, msg_len); + res = swprintf(err_buff + pos, eb_max - pos, L"%.*s. (code: %d)\n", + msg_len, buff, ecode); + if (res < 0) { + ERR("failed to copy: #%i: `" LWPDL "` (%d).", i, msg_len, buff, + ecode); + /* allow execution to continue, though */ + } else { + pos += (size_t)res; + if (pos <= msg_len) { + WARN("reached error buffer end (%zu) before copying all " + "errors.", eb_max); + break; + } + } + } + return pos; +} + /* * Checks if a DSN entry with the given name exists already. * Returns: @@ -384,13 +534,10 @@ int system_dsn_exists(wstr_st *dsn) * Reads the system entries for a DSN given into a doubly null-terminated * attributes list. * - * The defaults are always filled in. - * - * The list - as received by ConfigDSN() - seems to only contain the 'DSN' - * keyword, though the documentation mentions a full list. However, if a full - * is provided, the values are going to be taken into account, but possibly - * overwritten by registry entries (which theoretically should be the same - * anyways). + * The list - as received by ConfigDSN() - only contains the 'DSN' keyword, + * though it could contain other attributes too. These are going to be taken + * into account, but possibly overwritten by registry entries (which + * theoretically should be the same anyways). */ BOOL load_system_dsn(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00) { @@ -445,7 +592,7 @@ BOOL load_system_dsn(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00) } /* assign it to the config */ DBG("read DSN attribute: `" LWPDL "` = `" LWPDL "`.", - LWSTR(&keyword), LWSTR(&value)); + LWSTR(&keyword), LWSTR(mask_pwd(&keyword, &value))); /* assign attributes not yet given in the 00-list */ if (assign_dsn_attr(attrs, &keyword, &value, /*over?*/FALSE) < 0) { ERR("keyword '" LWPDL "' couldn't be assigned.", @@ -455,222 +602,249 @@ BOOL load_system_dsn(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00) } } - if (! assign_dsn_defaults(attrs)) { - ERR("OOM assigning defaults"); - return FALSE; - } + /* Provide the user no defaults atp. (i.e. start from empty config). The + * GUI will provide some defaults (ex. 9200) and config is validated + * before saving. => no assign_dsn_defaults(attrs); */ return TRUE; } -BOOL write_system_dsn(esodbc_dsn_attrs_st *attrs, BOOL create_new) +BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs, + esodbc_dsn_attrs_st *old_attrs) { struct { wstr_st *kw; - wstr_st *val; + wstr_st *new; + wstr_st *old; } *iter, map[] = { /* Driver */ - {&MK_WSTR(ESODBC_DSN_DESCRIPTION), &attrs->description}, + { + &MK_WSTR(ESODBC_DSN_DESCRIPTION), &new_attrs->description, + old_attrs ? &old_attrs->description : NULL + }, /* DSN */ - {&MK_WSTR(ESODBC_DSN_PWD), &attrs->pwd}, - {&MK_WSTR(ESODBC_DSN_UID), &attrs->uid}, + { + &MK_WSTR(ESODBC_DSN_PWD), &new_attrs->pwd, + old_attrs ? &old_attrs->pwd : NULL + }, + { + &MK_WSTR(ESODBC_DSN_UID), &new_attrs->uid, + old_attrs ? &old_attrs->uid : NULL + }, /* SAVEILE */ /* FILEDSN */ - {&MK_WSTR(ESODBC_DSN_SERVER), &attrs->server}, - {&MK_WSTR(ESODBC_DSN_PORT), &attrs->port}, - {&MK_WSTR(ESODBC_DSN_SECURE), &attrs->secure}, - {&MK_WSTR(ESODBC_DSN_CA_PATH), &attrs->ca_path}, - {&MK_WSTR(ESODBC_DSN_TIMEOUT), &attrs->timeout}, - {&MK_WSTR(ESODBC_DSN_FOLLOW), &attrs->follow}, - {&MK_WSTR(ESODBC_DSN_CATALOG), &attrs->catalog}, - {&MK_WSTR(ESODBC_DSN_PACKING), &attrs->packing}, - {&MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE), &attrs->max_fetch_size}, - {&MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), &attrs->max_body_size}, - {&MK_WSTR(ESODBC_DSN_TRACE_FILE), &attrs->trace_file}, - {&MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &attrs->trace_level}, - {NULL, NULL} + { + &MK_WSTR(ESODBC_DSN_SERVER), &new_attrs->server, + old_attrs ? &old_attrs->server : NULL + }, + { + &MK_WSTR(ESODBC_DSN_PORT), &new_attrs->port, + old_attrs ? &old_attrs->port : NULL + }, + { + &MK_WSTR(ESODBC_DSN_SECURE), &new_attrs->secure, + old_attrs ? &old_attrs->secure : NULL + }, + { + &MK_WSTR(ESODBC_DSN_CA_PATH), &new_attrs->ca_path, + old_attrs ? &old_attrs->ca_path : NULL + }, + { + &MK_WSTR(ESODBC_DSN_TIMEOUT), &new_attrs->timeout, + old_attrs ? &old_attrs->timeout : NULL + }, + { + &MK_WSTR(ESODBC_DSN_FOLLOW), &new_attrs->follow, + old_attrs ? &old_attrs->follow : NULL + }, + { + &MK_WSTR(ESODBC_DSN_CATALOG), &new_attrs->catalog, + old_attrs ? &old_attrs->catalog : NULL + }, + { + &MK_WSTR(ESODBC_DSN_PACKING), &new_attrs->packing, + old_attrs ? &old_attrs->packing : NULL + }, + { + &MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE), &new_attrs->max_fetch_size, + old_attrs ? &old_attrs->max_fetch_size : NULL + }, + { + &MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), &new_attrs->max_body_size, + old_attrs ? &old_attrs->max_body_size : NULL + }, + { + &MK_WSTR(ESODBC_DSN_TRACE_ENABLED), &new_attrs->trace_enabled, + old_attrs ? &old_attrs->trace_enabled : NULL + }, + { + &MK_WSTR(ESODBC_DSN_TRACE_FILE), &new_attrs->trace_file, + old_attrs ? &old_attrs->trace_file : NULL + }, + { + &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &new_attrs->trace_level, + old_attrs ? &old_attrs->trace_level : NULL + }, + {NULL, NULL, NULL} }; - if (create_new) { - if (! SQLValidDSNW(attrs->dsn.str)) { - ERR("invalid DSN value `" LWPDL "`.", LWSTR(&attrs->dsn)); - return FALSE; - } - INFO("creating new DSN `" LWPDL "` for driver ` " LWPDL " `.", - LWSTR(&attrs->dsn), LWSTR(&attrs->driver)); - if (! SQLWriteDSNToIniW(attrs->dsn.str, attrs->driver.str)) { - ERR("failed to add DSN `" LWPDL "` for driver ` " LWPDL " ` to " - ".INI.", LWSTR(&attrs->dsn), LWSTR(&attrs->driver)); - return FALSE; - } - } + /* check that the esodbc_dsn_attrs_st stays in sync with the above */ + assert(sizeof(map)/sizeof(map[0]) /* {NULL,NULL, NULL} terminator */- 1 + /*Driver,DSN,SAVEFILE,FILEDSN*/+ 4 == ESODBC_DSN_ATTRS_COUNT); for (iter = &map[0]; iter->kw; iter ++) { - if (! iter->val->cnt) { - DBG("value `" LWPDL "` not provisioned.", LWSTR(iter->kw)); - continue; - } - if (! SQLWritePrivateProfileStringW(attrs->dsn.str, - iter->kw->str, iter->val->str, MK_WPTR(SUBKEY_ODBC))) { - ERR("failed to write key `" LWPDL "` with value `" LWPDL "`.", - LWSTR(iter->kw), LWSTR(iter->val)); - return FALSE; + if (iter->old) { + if (EQ_WSTR(iter->new, iter->old)) { + DBG("DSN `" LWPDL "` attribute " LWPDL " maintained " + "value `" LWPDL "`.", LWSTR(&new_attrs->dsn), + LWSTR(iter->kw), LWSTR(mask_pwd(iter->kw, iter->new))); + continue; + } + if (! SQLWritePrivateProfileStringW(new_attrs->dsn.str, + iter->kw->str, + /* "If this argument is NULL, the key pointed to by the + * lpszEntry argument is deleted." */ + iter->new->cnt ? iter->new->str : NULL, + MK_WPTR(SUBKEY_ODBC))) { + ERR("failed to write key `" LWPDL "` with value `" LWPDL "`.", + LWSTR(iter->kw), LWSTR(mask_pwd(iter->kw, iter->new))); + return FALSE; + } + INFO("DSN `" LWPDL "` attribute " LWPDL " set to `" LWPDL "`%s.", + LWSTR(&new_attrs->dsn), LWSTR(iter->kw), + LWSTR(mask_pwd(iter->kw, iter->new)), + iter->new->cnt ? "" : " (deleted)"); + } else if (iter->new->cnt) { + if (! SQLWritePrivateProfileStringW(new_attrs->dsn.str, + iter->kw->str, iter->new->str, MK_WPTR(SUBKEY_ODBC))) { + ERR("failed to write key `" LWPDL "` with value `" LWPDL "`.", + LWSTR(iter->kw), LWSTR(mask_pwd(iter->kw, iter->new))); + return FALSE; + } + INFO("DSN `" LWPDL "` attribute " LWPDL " set to `" LWPDL "`.", + LWSTR(&new_attrs->dsn), LWSTR(iter->kw), + LWSTR(mask_pwd(iter->kw, iter->new))); } - DBG("key `" LWPDL "` with value `" LWPDL "` written to " SUBKEY_ODBC - ".", LWSTR(iter->kw), LWSTR(iter->val)); } return TRUE; } -static inline BOOL needs_braces(wstr_st *token) -{ - size_t i; - - for (i = 0; i < token->cnt; i ++) { - switch(token->str[i]) { - case ' ': - case '\t': - case '\r': - case '\n': - case '=': - case ';': - return TRUE; - } - } - return FALSE; -} - /* build a connection string to be written in the DSN files */ -BOOL write_connection_string(esodbc_dsn_attrs_st *attrs, - SQLWCHAR *szConnStrOut, SQLSMALLINT cchConnStrOutMax, - SQLSMALLINT *pcchConnStrOut) +long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, + SQLWCHAR *szConnStrOut, SQLSMALLINT cchConnStrOutMax) { int n, braces; size_t pos; wchar_t *format; struct { wstr_st *val; - char *kw; + wstr_st *kw; } *iter, map[] = { - {&attrs->driver, ESODBC_DSN_DRIVER}, - /* Description */ - {&attrs->dsn, ESODBC_DSN_DSN}, - {&attrs->pwd, ESODBC_DSN_PWD}, - {&attrs->uid, ESODBC_DSN_UID}, - {&attrs->savefile, ESODBC_DSN_SAVEFILE}, - {&attrs->filedsn, ESODBC_DSN_FILEDSN}, - {&attrs->server, ESODBC_DSN_SERVER}, - {&attrs->port, ESODBC_DSN_PORT}, - {&attrs->secure, ESODBC_DSN_SECURE}, - {&attrs->ca_path, ESODBC_DSN_CA_PATH}, - {&attrs->timeout, ESODBC_DSN_TIMEOUT}, - {&attrs->follow, ESODBC_DSN_FOLLOW}, - {&attrs->catalog, ESODBC_DSN_CATALOG}, - {&attrs->packing, ESODBC_DSN_PACKING}, - {&attrs->max_fetch_size, ESODBC_DSN_MAX_FETCH_SIZE}, - {&attrs->max_body_size, ESODBC_DSN_MAX_BODY_SIZE_MB}, - {&attrs->trace_file, ESODBC_DSN_TRACE_FILE}, - {&attrs->trace_level, ESODBC_DSN_TRACE_LEVEL}, + {&attrs->driver, &MK_WSTR(ESODBC_DSN_DRIVER)}, + {&attrs->description, &MK_WSTR(ESODBC_DSN_DESCRIPTION)}, + {&attrs->dsn, &MK_WSTR(ESODBC_DSN_DSN)}, + {&attrs->pwd, &MK_WSTR(ESODBC_DSN_PWD)}, + {&attrs->uid, &MK_WSTR(ESODBC_DSN_UID)}, + {&attrs->savefile, &MK_WSTR(ESODBC_DSN_SAVEFILE)}, + {&attrs->filedsn, &MK_WSTR(ESODBC_DSN_FILEDSN)}, + {&attrs->server, &MK_WSTR(ESODBC_DSN_SERVER)}, + {&attrs->port, &MK_WSTR(ESODBC_DSN_PORT)}, + {&attrs->secure, &MK_WSTR(ESODBC_DSN_SECURE)}, + {&attrs->ca_path, &MK_WSTR(ESODBC_DSN_CA_PATH)}, + {&attrs->timeout, &MK_WSTR(ESODBC_DSN_TIMEOUT)}, + {&attrs->follow, &MK_WSTR(ESODBC_DSN_FOLLOW)}, + {&attrs->catalog, &MK_WSTR(ESODBC_DSN_CATALOG)}, + {&attrs->packing, &MK_WSTR(ESODBC_DSN_PACKING)}, + {&attrs->max_fetch_size, &MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE)}, + {&attrs->max_body_size, &MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB)}, + {&attrs->trace_enabled, &MK_WSTR(ESODBC_DSN_TRACE_ENABLED)}, + {&attrs->trace_file, &MK_WSTR(ESODBC_DSN_TRACE_FILE)}, + {&attrs->trace_level, &MK_WSTR(ESODBC_DSN_TRACE_LEVEL)}, {NULL, NULL} }; + /* check that the esodbc_dsn_attrs_st stays in sync with the above */ + assert(sizeof(map)/sizeof(*iter) - /* {NULL,NULL} terminator */1 == + ESODBC_DSN_ATTRS_COUNT); + assert(0 <= cchConnStrOutMax); + for (iter = &map[0], pos = 0; iter->val; iter ++) { if (iter->val->cnt) { braces = needs_braces(iter->val) ? 2 : 0; if (cchConnStrOutMax && szConnStrOut) { - /* swprintf will fail if formated string would overrun the - * buffer size */ - if (cchConnStrOutMax - pos < iter->val->cnt + braces) { - /* indicate that we've reached buffer limits: only account - * for how long the string would be */ - cchConnStrOutMax = 0; - pos += iter->val->cnt + braces; - continue; - } - if (braces) { - format = WPFCP_DESC "={" WPFWP_LDESC "};"; - } else { - format = WPFCP_DESC "=" WPFWP_LDESC ";"; - } - n = swprintf(szConnStrOut + pos, cchConnStrOutMax - pos, - format, iter->kw, LWSTR(iter->val)); - if (n < 0) { - ERRN("failed to outprint connection string (space " - "left: %d; needed: %d).", cchConnStrOutMax - pos, - iter->val->cnt); - return FALSE; - } else { - pos += n; + /* is there still room in the buffer? */ + if ((SQLSMALLINT)pos < cchConnStrOutMax) { + if (braces) { + format = WPFWP_LDESC "={" WPFWP_LDESC "};"; + } else { + format = WPFWP_LDESC "=" WPFWP_LDESC ";"; + } + n = swprintf(szConnStrOut + pos, cchConnStrOutMax - pos, + format, LWSTR(iter->kw), LWSTR(iter->val)); + if (n < 0) { + ERRN("failed to outprint connection string (keyword: " + LWPDL ", room: %hd, position: %zu).", + LWSTR(iter->kw), cchConnStrOutMax, pos); + return -1; + } } - } else { - /* simply increment the counter, since the untruncated length - * need to be returned to the app */ - pos += iter->val->cnt + braces; } + /* update the write position with what would be written (not what + * has been), since the untruncated length needs to always be + * returned to the app */ + pos += iter->kw->cnt + /*`=`*/1 + + iter->val->cnt + braces + /*`;`*/1; } } - *pcchConnStrOut = (SQLSMALLINT)pos; - - DBG("Output connection string: `" LWPD "`; out len: %d.", - szConnStrOut, pos); - return TRUE; +#ifndef NDEBUG /* don't print the PWD */ + DBG("new connection string: `" LWPD "`; out len: %zu.", szConnStrOut, pos); +#endif /* NDEBUG */ + assert(pos < LONG_MAX); + return (long)pos; } -BOOL assign_dsn_defaults(esodbc_dsn_attrs_st *attrs) +/* assign defaults where not assigned and applicable */ +void assign_dsn_defaults(esodbc_dsn_attrs_st *attrs) { - /* assign defaults where not assigned and applicable */ - if (assign_dsn_attr(attrs, + int res = 0; + + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_SERVER), &MK_WSTR(ESODBC_DEF_SERVER), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } - if (assign_dsn_attr(attrs, + /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_PORT), &MK_WSTR(ESODBC_DEF_PORT), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } - if (assign_dsn_attr(attrs, + /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_SECURE), &MK_WSTR(ESODBC_DEF_SECURE), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } - if (assign_dsn_attr(attrs, + /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_TIMEOUT), &MK_WSTR(ESODBC_DEF_TIMEOUT), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } - if (assign_dsn_attr(attrs, + /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_FOLLOW), &MK_WSTR(ESODBC_DEF_FOLLOW), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } + /*overwrite?*/FALSE); - if (assign_dsn_attr(attrs, + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_PACKING), &MK_WSTR(ESODBC_DEF_PACKING), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } - if (assign_dsn_attr(attrs, + /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_MAX_FETCH_SIZE), &MK_WSTR(ESODBC_DEF_FETCH_SIZE), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } - if (assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), + /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB), &MK_WSTR(ESODBC_DEF_MAX_BODY_SIZE_MB), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } + /*overwrite?*/FALSE); /* default: no trace file */ - if (assign_dsn_attr(attrs, - &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &MK_WSTR(ESODBC_DEF_TRACE_LEVEL), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } - - return TRUE; + res |= assign_dsn_attr(attrs, + &MK_WSTR(ESODBC_DSN_TRACE_ENABLED), + &MK_WSTR(ESODBC_DEF_TRACE_ENABLED), /*overwrite?*/FALSE); + res |= assign_dsn_attr(attrs, + &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), + &MK_WSTR(ESODBC_DEF_TRACE_LEVEL), /*overwrite?*/FALSE); + + assert(0 <= res); } @@ -792,60 +966,111 @@ BOOL read_system_info(esodbc_dsn_attrs_st *attrs) #error "unsupported platform" /* TODO */ #endif /* defined(_WIN32) || defined (WIN32) */ - -// asks user for the config data -// returns: -// . negative: on failure -// . 0: user canceled -// . positive: user provided input -int prompt_user_config(HWND hwndParent, esodbc_dsn_attrs_st *attrs, - /* disable non-connect-related controls? */ - BOOL disable_nonconn) +int validate_dsn(esodbc_dsn_attrs_st *attrs, const wchar_t *dsn_str, + wchar_t *err_out, size_t eo_max, BOOL on_connect) { - static int attempts = 0; + int ret; + esodbc_dbc_st dbc; - if (! hwndParent) { - INFO("no window handler provided -- configuration skipped."); - return 1; + if (! dsn_str) { + ERR("invalid NULL DSN string received."); + /* internal error, no user-relevant message */ + return ESODBC_DSN_ISNULL_ERROR; } - TRACE; - - if (assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_DSN), - &MK_WSTR("My Elasticsearch ODBC DSN"), FALSE) <= 0) { - //&MK_WSTR("My Elasticsearch ODBC DSN"), TRUE, TRUE) <= 0) { - return -1; +#ifdef ESODBC_DSN_API_WITH_00_LIST + /* this won't be "complete" if using 00-list */ +#ifndef NDEBUG /* don't print the PWD */ + DBG("received DSN string starting with: `" LWPD "`.", dsn_str); +#endif /* NDEBUG */ + if (! parse_00_list(attrs, (SQLWCHAR *)dsn_str)) { +#else +#ifndef NDEBUG /* don't print the PWD */ + DBG("received DSN string: `" LWPD "`.", dsn_str); +#endif /* NDEBUG */ + if (! parse_connection_string(attrs, (SQLWCHAR *)dsn_str, SQL_NTS)) { +#endif /* ESODBC_DSN_API_WITH_00_LIST */ + ERR("failed to parse received DSN string."); + swprintf(err_out, eo_max, L"DSN string parsing error."); + return ESODBC_DSN_INVALID_ERROR; } -#if 1 - if (assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), - &MK_WSTR("INFO"), FALSE) <= 0) { - return -1; + + /* + * check on the minimum DSN set requirements + */ + if (! attrs->server.cnt) { + ERR("received empty server name"); + swprintf(err_out, eo_max, L"Server hostname cannot be empty."); + return ESODBC_DSN_INVALID_ERROR; } -#endif - if (1 < attempts ++) { - /* prevent infinite loops */ - return 0; + if (!on_connect && !attrs->dsn.cnt) { + ERR("received empty DSN name"); + swprintf(err_out, eo_max, L"DSN name cannot be empty."); + return ESODBC_DSN_INVALID_ERROR; } - if (SQL_MAX_DSN_LENGTH < attrs->dsn.cnt) { - ERR("DSN name longer than max (%d).", SQL_MAX_DSN_LENGTH); + /* fill in whatever's missing */ + assign_dsn_defaults(attrs); + + init_dbc(&dbc, NULL); + ret = on_connect ? do_connect(&dbc, attrs) : config_dbc(&dbc, attrs); + + if (! SQL_SUCCEEDED(ret)) { + ret = EsSQLGetDiagFieldW(SQL_HANDLE_DBC, &dbc, /*rec#*/1, + SQL_DIAG_MESSAGE_TEXT, err_out, (SQLSMALLINT)eo_max, + /*written len*/NULL/*err_out is 0-term'd*/); + /* function should not fail with given params. */ + assert(SQL_SUCCEEDED(ret)); + ERR("test DBC %s failed: " LWPD ".", + on_connect ? "connection" : "configuration", err_out); + ret = ESODBC_DSN_GENERIC_ERROR; + } else { + ret = ESODBC_DSN_NO_ERROR; // 0 } - return 1; + cleanup_dbc(&dbc); + return ret; } -// asks user if we should overwrite the existing DSN -// returns: -// . negative on failure -// . 0 on false -// . positive on true -int prompt_user_overwrite(HWND hwndParent, wstr_st *dsn) +static int test_connect(void *arg, const wchar_t *dsn_str, + wchar_t *err_out, size_t eo_max, unsigned int _) { - if (! hwndParent) { - INFO("no window handler provided -- forcing overwrite."); - return 1; + esodbc_dsn_attrs_st attrs; + + assert(! arg); /* change santinel */ + init_dsn_attrs(&attrs); + return validate_dsn(&attrs, dsn_str, err_out, eo_max, /*on conn*/TRUE); +} + +/* + * Return: + * < 0: error + * ==0: user cancels + * > 0: success + */ +int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, + driver_callback_ft save_cb) +{ + int ret; + wchar_t dsn_str[sizeof(attrs->buff)/sizeof(*attrs->buff)]; + +#if 0 + if (write_00_list(attrs, (SQLWCHAR *)dsn_str, + sizeof(dsn_str)/sizeof(*dsn_str)) <= 0) { +#else + if (write_connection_string(attrs, (SQLWCHAR *)dsn_str, + sizeof(dsn_str)/sizeof(*dsn_str)) <= 0) { +#endif + ERR("failed to serialize attributes into a DSN string."); + return FALSE; } - TRACE; - return 1; + + ret = EsOdbcDsnEdit(hwnd, on_conn, dsn_str, &test_connect, NULL, + save_cb, attrs); + DBG("DSN editor returned: %d.", ret); + if (ret < 0) { + ERR("failed to bring up the GUI; code:%d.", ret); + } + return ret; } diff --git a/driver/dsn.h b/driver/dsn.h index f7da7617..9a48dc5f 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -7,6 +7,7 @@ #ifndef __DSN_H__ #define __DSN_H__ +#include "EsOdbcDsnBinding.h" #include "util.h" #include "defs.h" @@ -31,6 +32,7 @@ #define ESODBC_DSN_PACKING "Packing" #define ESODBC_DSN_MAX_FETCH_SIZE "MaxFetchSize" #define ESODBC_DSN_MAX_BODY_SIZE_MB "MaxBodySizeMB" +#define ESODBC_DSN_TRACE_ENABLED "TraceEnabled" #define ESODBC_DSN_TRACE_FILE "TraceFile" #define ESODBC_DSN_TRACE_LEVEL "TraceLevel" @@ -53,31 +55,51 @@ typedef struct { wstr_st packing; wstr_st max_fetch_size; wstr_st max_body_size; + wstr_st trace_enabled; wstr_st trace_file; wstr_st trace_level; -#define ESODBC_DSN_ATTRS_COUNT 19 +#define ESODBC_DSN_ATTRS_COUNT 20 SQLWCHAR buff[ESODBC_DSN_ATTRS_COUNT * ESODBC_DSN_MAX_ATTR_LEN]; } esodbc_dsn_attrs_st; -void init_dsn_attrs(esodbc_dsn_attrs_st *attrs); -BOOL assign_dsn_defaults(esodbc_dsn_attrs_st *attrs); +/* assign_dsn_attr() success codes. */ +#define DSN_NOT_MATCHED 0 +#define DSN_NOT_OVERWRITTEN 1 +#define DSN_ASSIGNED 2 +#define DSN_OVERWRITTEN 3 + +void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs); +void assign_dsn_defaults(esodbc_dsn_attrs_st *attrs); BOOL assign_dsn_attr(esodbc_dsn_attrs_st *attrs, wstr_st *keyword, wstr_st *value, BOOL overwrite); +BOOL TEST_API parse_00_list(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00); +long TEST_API write_00_list(esodbc_dsn_attrs_st *attrs, + SQLWCHAR *list00, size_t cnt00); + +/* "system" from "system information" (cf. SQLDriverConnect), not as + * in User/System DSN */ BOOL read_system_info(esodbc_dsn_attrs_st *attrs); int system_dsn_exists(wstr_st *dsn); BOOL load_system_dsn(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00); -BOOL write_system_dsn(esodbc_dsn_attrs_st *attrs, BOOL create_new); +BOOL write_system_dsn(esodbc_dsn_attrs_st *crr, esodbc_dsn_attrs_st *old); -BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, +BOOL TEST_API parse_connection_string(esodbc_dsn_attrs_st *attrs, SQLWCHAR *szConnStrIn, SQLSMALLINT cchConnStrIn); -BOOL write_connection_string(esodbc_dsn_attrs_st *attrs, - SQLWCHAR *szConnStrOut, SQLSMALLINT cchConnStrOutMax, - SQLSMALLINT *pcchConnStrOut); +long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, + SQLWCHAR *szConnStrOut, SQLSMALLINT cchConnStrOutMax); + +size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max); +int validate_dsn(esodbc_dsn_attrs_st *attrs, const wchar_t *dsn_str, + wchar_t *err_out, size_t eo_max, BOOL try_connect); +int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, + driver_callback_ft save_cb); -BOOL prompt_user_config(HWND hwndParent, esodbc_dsn_attrs_st *attrs, - BOOL disable_nonconn); -int prompt_user_overwrite(HWND hwndParent, wstr_st *dsn); +/* Uncomment to enable 00-list format (vs. connection string, + * `;`/`|`-separated) at the interface with the GUI API. + * The .NET framework has an ODBC connection string parser, so the that format + * will be used on Windows. */ +//#define ESODBC_DSN_API_WITH_00_LIST #endif /* __DSN_H__ */ diff --git a/driver/error.c b/driver/error.c index 0e1f3834..973c719b 100644 --- a/driver/error.c +++ b/driver/error.c @@ -16,11 +16,11 @@ void init_diagnostic(esodbc_diag_st *dest) dest->column_number = SQL_NO_COLUMN_NUMBER; } -/* TODO: must the diagnostic be "cleared" after a succesful invokation?? */ -SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, - SQLWCHAR *text, SQLINTEGER code) +SQLRETURN post_diagnostic(SQLHANDLE hnd, esodbc_state_et state, + const SQLWCHAR *text, SQLINTEGER code) { size_t pos, tcnt, ebufsz; + esodbc_diag_st *dest = &HDRH(hnd)->diag; ebufsz = sizeof(dest->text)/sizeof(dest->text[0]); @@ -46,16 +46,16 @@ SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, wcsncpy(dest->text + pos, text, tcnt + /* 0-term */1); dest->text_len = (SQLUSMALLINT)(pos + tcnt); } - DBG("diagnostic message: `" LWPD "` [%d], native code: %d.", + DBGH(hnd, "diagnostic message: `" LWPD "` [%d], native code: %d.", dest->text, dest->text_len, dest->native_code); RET_STATE(state); } -SQLRETURN post_c_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, - SQLCHAR *text, SQLINTEGER code) +SQLRETURN post_c_diagnostic(SQLHANDLE hnd, esodbc_state_et state, + const SQLCHAR *text, SQLINTEGER code) { - SQLWCHAR wtext[sizeof(dest->text)/sizeof(*dest->text)], *ptr; + SQLWCHAR wtext[SQL_MAX_MESSAGE_LENGTH], *ptr; if (text) { if (ascii_c2w(text, wtext, sizeof(wtext)/sizeof(*wtext)) < 0) { ERR("failed to convert diagnostic message `%s`.", text); @@ -65,13 +65,14 @@ SQLRETURN post_c_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, } else { ptr = NULL; } - return post_diagnostic(dest, state, ptr, code); + return post_diagnostic(hnd, state, ptr, code); } -SQLRETURN post_row_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, +SQLRETURN post_row_diagnostic(SQLHANDLE hnd, esodbc_state_et state, SQLWCHAR *text, SQLINTEGER code, SQLLEN nrow, SQLINTEGER ncol) { + esodbc_diag_st *dest = &HDRH(hnd)->diag; dest->row_number = nrow; dest->column_number = ncol; return post_diagnostic(dest, state, text, code); diff --git a/driver/error.h b/driver/error.h index 71d0f5d2..c2f7b49b 100644 --- a/driver/error.h +++ b/driver/error.h @@ -17,7 +17,7 @@ typedef struct { /* Note: keep in sync with esodbc_errors[] */ typedef enum { - /* non-standard, must be 0/first position (calloc'ing) */ + /* must be 0/first position (calloc'ing) */ /* diagnostic meaning: no diag posted */ SQL_STATE_00000 = 0, @@ -443,18 +443,12 @@ typedef struct { void init_diagnostic(esodbc_diag_st *dest); -SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, - SQLWCHAR *text, SQLINTEGER code); -SQLRETURN post_c_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, - SQLCHAR *text, SQLINTEGER code); -/* post state into the diagnostic and return state's return code */ -#define RET_DIAG(_d/*est*/, _s/*tate*/, _t/*ext*/, _c/*ode*/) \ - return post_diagnostic(_d, _s, _t, _c) -/* same as above, but take C-strings as messages */ -#define RET_CDIAG(_d/*est*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \ - RET_DIAG(_d, _s, MK_WPTR(_t), _c) +SQLRETURN post_diagnostic(SQLHANDLE hnd, esodbc_state_et state, + const SQLWCHAR *text, SQLINTEGER code); +SQLRETURN post_c_diagnostic(SQLHANDLE hnd, esodbc_state_et state, + const SQLCHAR *text, SQLINTEGER code); -SQLRETURN post_row_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, +SQLRETURN post_row_diagnostic(SQLHANDLE hnd, esodbc_state_et state, SQLWCHAR *text, SQLINTEGER code, SQLLEN nrow, SQLINTEGER ncol); #endif /* __ERROR_H__ */ diff --git a/driver/handles.c b/driver/handles.c index 7e790432..8eb9e699 100644 --- a/driver/handles.c +++ b/driver/handles.c @@ -53,6 +53,47 @@ static void init_hheader(esodbc_hhdr_st *hdr, SQLSMALLINT type, void *parent) init_diagnostic(&hdr->diag); ESODBC_MUX_INIT(&hdr->mutex); hdr->parent = parent; + + /* init logging helpers */ + switch(type) { + case SQL_HANDLE_ENV: + hdr->typew = MK_WSTR("ENV"); + hdr->log = _gf_log; /* use global logger */ + break; + case SQL_HANDLE_DBC: + hdr->typew = MK_WSTR("DBC"); + hdr->log = _gf_log; /* use global logger, by default */ + break; + case SQL_HANDLE_STMT: + hdr->typew = MK_WSTR("STMT"); + hdr->log = HDRH(parent)->log; /* inherit */ + break; + case SQL_HANDLE_DESC: + hdr->typew = MK_WSTR("DESC"); + hdr->log = HDRH(parent)->log; /* inherit */ + break; + default: + assert(0); + } +} + +/* + * "un-static" the initializers as needed + */ +static inline void init_env(esodbc_env_st *env) +{ + memset(env, 0, sizeof(*env)); + init_hheader(HDRH(env), SQL_HANDLE_ENV, NULL); +} + +void init_dbc(esodbc_dbc_st *dbc, SQLHANDLE InputHandle) +{ + memset(dbc, 0, sizeof(*dbc)); + init_hheader(HDRH(dbc), SQL_HANDLE_DBC, InputHandle); + + dbc->metadata_id = SQL_FALSE; + ESODBC_MUX_INIT(&dbc->curl_mux); + /* rest of initialization done at connect time */ } static void init_desc(esodbc_desc_st *desc, esodbc_stmt_st *stmt, @@ -72,6 +113,7 @@ static void init_desc(esodbc_desc_st *desc, esodbc_stmt_st *stmt, if (DESC_TYPE_IS_APPLICATION(type)) { desc->bind_type = SQL_BIND_BY_COLUMN; } + // XXX: assign/chain to statement? } static void clear_desc(esodbc_desc_st *desc, BOOL reinit) @@ -101,6 +143,33 @@ static void clear_desc(esodbc_desc_st *desc, BOOL reinit) } } +static void init_stmt(esodbc_stmt_st *stmt, SQLHANDLE InputHandle) +{ + memset(stmt, 0, sizeof(*stmt)); + init_hheader(HDRH(stmt), SQL_HANDLE_STMT, InputHandle); + + init_desc(&stmt->i_ard, stmt, DESC_TYPE_ARD, SQL_DESC_ALLOC_AUTO); + init_desc(&stmt->i_ird, stmt, DESC_TYPE_IRD, SQL_DESC_ALLOC_AUTO); + init_desc(&stmt->i_apd, stmt, DESC_TYPE_APD, SQL_DESC_ALLOC_AUTO); + init_desc(&stmt->i_ipd, stmt, DESC_TYPE_IPD, SQL_DESC_ALLOC_AUTO); + + /* "When a statement is allocated, four descriptor handles are + * automatically allocated and associated with the statement." */ + stmt->ard = &stmt->i_ard; + stmt->ird = &stmt->i_ird; + stmt->apd = &stmt->i_apd; + stmt->ipd = &stmt->i_ipd; + + /* set option defaults */ + /* TODO: change to SQL_UB_DEFAULT when supporting bookmarks */ + stmt->bookmarks = SQL_UB_OFF; + /* inherit this connection-statement attributes + * Note: these attributes won't propagate at statement level when + * set at connection level. */ + stmt->metadata_id = DBCH(InputHandle)->metadata_id; + stmt->sql2c_conversion = CONVERSION_UNCHECKED; +} + void dump_record(esodbc_rec_st *rec) { DBGH(rec->desc, "Dumping REC@0x%p", rec); @@ -145,7 +214,6 @@ void dump_record(esodbc_rec_st *rec) #undef DUMP_FIELD } - /* * The Driver Manager does not call the driver-level environment handle * allocation function until the application calls SQLConnect, @@ -167,10 +235,22 @@ void dump_record(esodbc_rec_st *rec) SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, _Out_ SQLHANDLE *OutputHandle) { - esodbc_env_st *env; - esodbc_dbc_st *dbc; - esodbc_stmt_st *stmt; - esodbc_hhdr_st *hdr; + if (! OutputHandle) { + ERR("null output handle provided."); + RET_STATE(SQL_STATE_HY009); + } + + if (HandleType != SQL_HANDLE_ENV) { + if (! InputHandle) { + ERR("null input handle provided"); + RET_STATE(SQL_STATE_HY009); + } + } else { + if (InputHandle) { + ERR("not null (@0x%p) input handle for Environment.", InputHandle); + /* not fatal a.t.p., tho it'll likely fail (->error level) */ + } + } switch(HandleType) { /* @@ -186,21 +266,11 @@ SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType, * """ */ case SQL_HANDLE_ENV: /* Environment Handle */ - if (InputHandle != SQL_NULL_HANDLE) { - WARN("passed InputHandle not null (=0x%p).",InputHandle); - /* not fatal a.t.p. */ + *OutputHandle = (SQLHANDLE)malloc(sizeof(esodbc_env_st)); + if (*OutputHandle) { + init_env(*OutputHandle); + DBG("new Environment handle @0x%p.", *OutputHandle); } - if (! OutputHandle) { - ERR("null output handle provided."); - RET_STATE(SQL_STATE_HY009); - } - *OutputHandle = (SQLHANDLE *)calloc(1, sizeof(esodbc_env_st)); - if (! *OutputHandle) { - ERRN("failed to callocate env handle."); - RET_STATE(SQL_STATE_HY001); - } - - DBG("new Environment handle allocated @0x%p.",*OutputHandle); break; case SQL_HANDLE_DBC: /* Connection Handle */ @@ -212,90 +282,57 @@ SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType, * return SQLSTATE HY010 (Function sequence error). * """ */ - env = ENVH(InputHandle); - if (! env->version) { - ERRH(env, "no version set in env when allocating DBC."); - RET_HDIAG(env, SQL_STATE_HY010, - "enviornment has no version set yet", 0); + if (! ENVH(InputHandle)->version) { + ERRH(InputHandle, "no version set in ENV."); + RET_HDIAG(InputHandle, SQL_STATE_HY010, + "enviornment has no version set", 0); } - dbc = (esodbc_dbc_st *)calloc(1, sizeof(esodbc_dbc_st)); - *OutputHandle = (SQLHANDLE *)dbc; - if (! dbc) { - ERRNH(env, "failed to callocate connection handle."); - RET_HDIAGS(env, SQL_STATE_HY001); + *OutputHandle = (SQLHANDLE)malloc(sizeof(esodbc_dbc_st)); + if (*OutputHandle) { + init_dbc(*OutputHandle, InputHandle); + DBGH(InputHandle, "new Connection @0x%p.", *OutputHandle); } - dbc->metadata_id = SQL_FALSE; - - /* rest of initialization done at connect time */ - - DBGH(env, "new Connection handle allocated @0x%p.", *OutputHandle); break; case SQL_HANDLE_STMT: /* Statement Handle */ - dbc = DBCH(InputHandle); - stmt = (esodbc_stmt_st *)calloc(1, sizeof(esodbc_stmt_st)); - *OutputHandle = stmt; - if (! stmt) { - ERRNH(dbc, "failed to callocate statement handle."); - RET_HDIAGS(dbc, SQL_STATE_HY001); + *OutputHandle = (SQLHANDLE)malloc(sizeof(esodbc_stmt_st)); + if (*OutputHandle) { + init_stmt(*OutputHandle, InputHandle); + DBGH(InputHandle, "new Statement @0x%p.", *OutputHandle); } - - init_desc(&stmt->i_ard, stmt, DESC_TYPE_ARD, SQL_DESC_ALLOC_AUTO); - init_desc(&stmt->i_ird, stmt, DESC_TYPE_IRD, SQL_DESC_ALLOC_AUTO); - init_desc(&stmt->i_apd, stmt, DESC_TYPE_APD, SQL_DESC_ALLOC_AUTO); - init_desc(&stmt->i_ipd, stmt, DESC_TYPE_IPD, SQL_DESC_ALLOC_AUTO); - - /* "When a statement is allocated, four descriptor handles are - * automatically allocated and associated with the statement." */ - stmt->ard = &stmt->i_ard; - stmt->ird = &stmt->i_ird; - stmt->apd = &stmt->i_apd; - stmt->ipd = &stmt->i_ipd; - - /* set option defaults */ - /* TODO: change to SQL_UB_DEFAULT when supporting bookmarks */ - stmt->bookmarks = SQL_UB_OFF; - /* inherit this connection-statement attributes - * Note: these attributes won't propagate at statement level when - * set at connection level. */ - stmt->metadata_id = dbc->metadata_id; - stmt->sql2c_conversion = CONVERSION_UNCHECKED; - - ESODBC_MUX_INIT(&dbc->curl_mux); - - DBGH(dbc, "new Statement handle allocated @0x%p.", *OutputHandle); break; - case SQL_HANDLE_DESC: - stmt = STMH(InputHandle); - *OutputHandle = (SQLHANDLE *)calloc(1, sizeof(esodbc_desc_st)); - if (! *OutputHandle) { - ERRNH(stmt, "failed to callocate descriptor handle."); - RET_HDIAGS(stmt, SQL_STATE_HY001); + case SQL_HANDLE_DESC: /* Descriptor Handle */ + *OutputHandle = (SQLHANDLE)malloc(sizeof(esodbc_desc_st)); + if (*OutputHandle) { + init_desc(*OutputHandle, InputHandle, DESC_TYPE_ANON, + SQL_DESC_ALLOC_USER); + DBGH(InputHandle, "new Statement @0x%p.", *OutputHandle); } - init_desc(*OutputHandle, InputHandle, DESC_TYPE_ANON, - SQL_DESC_ALLOC_USER); - DBGH(stmt, "new Descriptor handle allocated @0x%p.",*OutputHandle); - // FIXME: assign/chain to statement? break; case SQL_HANDLE_SENV: /* Shared Environment Handle */ - FIXME; // FIXME - //break; -#if 0 - case SQL_HANDLE_DBC_INFO_TOKEN: - //break; -#endif + // case SQL_HANDLE_DBC_INFO_TOKEN: + ERR("handle type %hd not implemented.", HandleType); + RET_STATE(SQL_STATE_HYC00); // optional feature + default: ERR("unknown HandleType: %d.", HandleType); return SQL_INVALID_HANDLE; } - /* - * Initialize new handle's header. - */ - hdr = HDRH(*OutputHandle); - init_hheader(hdr, HandleType, InputHandle); + if (! *OutputHandle) { + ERRN("OOM for handle type %hd, on input handle@0x%p.", HandleType, + InputHandle); + if (InputHandle) { + RET_HDIAGS(InputHandle, SQL_STATE_HY001); + } else { + RET_STATE(SQL_STATE_HY001); + } + } else { + /* new handle has been init'ed */ + assert(HDRH(*OutputHandle)->type); + } return SQL_SUCCESS; } @@ -387,7 +424,7 @@ SQLRETURN EsSQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option) * doing nothing, it might leak mem. */ ERRH(stmt, "DROPing is deprecated -- no action taken! " "(might leak memory)"); - //return SQLFreeStmt(SQL_HANDLE_STMT, (SQLHANDLE)StatementHandle); + //return SQLFreeHandle(SQL_HANDLE_STMT,(SQLHANDLE)StatementHandle); break; /* "This does not unbind the bookmark column; to do that, the @@ -476,7 +513,6 @@ SQLRETURN EsSQLSetEnvAttr(SQLHENV EnvironmentHandle, assert(sizeof(SQLINTEGER) == 4); switch (Attribute) { - /* TODO: connection pooling? */ case SQL_ATTR_CONNECTION_POOLING: case SQL_ATTR_CP_MATCH: RET_HDIAG(ENVH(EnvironmentHandle), SQL_STATE_HYC00, @@ -484,7 +520,6 @@ SQLRETURN EsSQLSetEnvAttr(SQLHENV EnvironmentHandle, case SQL_ATTR_ODBC_VERSION: switch ((intptr_t)Value) { - // FIXME: review@alpha // supporting applications of 2.x and 3.x<3.8 needs extensive // review of the options. case SQL_OV_ODBC2: @@ -524,7 +559,6 @@ SQLRETURN EsSQLGetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLINTEGER BufferLength, _Out_opt_ SQLINTEGER *StringLength) { switch (Attribute) { - /* TODO: connection pooling? */ case SQL_ATTR_CONNECTION_POOLING: case SQL_ATTR_CP_MATCH: RET_HDIAG(ENVH(EnvironmentHandle), SQL_STATE_HYC00, @@ -845,6 +879,14 @@ SQLRETURN EsSQLSetStmtAttrW( } break; + /* SQL Server non-standard attributes */ + case 1226: + case 1227: + case 1228: + ERRH(stmt, "non-standard attribute: %d.", Attribute); + /* "Invalid attribute/option identifier" */ + RET_HDIAGS(stmt, SQL_STATE_HY092); + default: // FIXME BUGH(stmt, "unknown Attribute: %d.", Attribute); @@ -1199,13 +1241,14 @@ static esodbc_state_et check_buff(SQLSMALLINT field_id, SQLPOINTER buff, * IxD must have es_type pointer set and it's value is not checked at * header/field access time -> only this function guards against NP deref'ing. */ -static BOOL check_access(desc_type_et desc_type, SQLSMALLINT field_id, +static BOOL check_access(esodbc_desc_st *desc, SQLSMALLINT field_id, char mode /* O_RDONLY | O_RDWR */) { BOOL ret; + desc_type_et desc_type = desc->type; if (desc_type == DESC_TYPE_ANON) { - BUG("can't check permissions against ANON descryptor type."); + BUGH(desc, "can't check permissions against ANON descryptor type."); return FALSE; } assert(mode == O_RDONLY || mode == O_RDWR); @@ -1295,10 +1338,10 @@ static BOOL check_access(desc_type_et desc_type, SQLSMALLINT field_id, break; default: - BUG("unknown field identifier: %d.", field_id); + BUGH(desc, "unknown field identifier: %d.", field_id); ret = FALSE; } - LOG(ret ? LOG_LEVEL_DBG : LOG_LEVEL_ERR, + LOGH(desc, ret ? LOG_LEVEL_DBG : LOG_LEVEL_ERR, /*werr*/0, "Descriptor type: %d, Field ID: %d, mode=%s => grant: %s.", desc_type, field_id, mode == O_RDONLY ? "read" : "read/write", @@ -1358,7 +1401,7 @@ SQLRETURN update_rec_count(esodbc_desc_st *desc, SQLSMALLINT new_count) } if (desc->count == new_count) { - LOGH(new_count ? LOG_LEVEL_INFO : LOG_LEVEL_DBG, 0, desc, + LOGH(desc, new_count ? LOG_LEVEL_INFO : LOG_LEVEL_DBG, 0, "new descriptor count equals old one, %d.", new_count); return SQL_SUCCESS; } @@ -1376,7 +1419,6 @@ SQLRETURN update_rec_count(esodbc_desc_st *desc, SQLSMALLINT new_count) free_desc_recs(desc); recs = NULL; } else { - /* TODO: change to a list implementation? review@alpha */ recs = (esodbc_rec_st *)realloc(desc->recs, sizeof(esodbc_rec_st) * new_count); if (! recs) { @@ -1447,7 +1489,7 @@ esodbc_desc_st *getdata_set_ard(esodbc_stmt_st *stmt, esodbc_desc_st *gd_ard, if (colno < count) { /* can the static recs be used? */ /* need to init all records, not only the single one that will be - * bound, since data compat. check will run against all bound recs. */ + * bound, since data covert. check will run against all bound recs. */ for (i = 0; i < count; i ++) { init_rec(&recs[i], gd_ard); } @@ -1455,6 +1497,7 @@ esodbc_desc_st *getdata_set_ard(esodbc_stmt_st *stmt, esodbc_desc_st *gd_ard, gd_ard->count = count; gd_ard->recs = recs; } + /* else: recs will be alloc'd later when binding the column */ DBGH(stmt, "GD ARD @0x%p, records allocated %s.", gd_ard, colno < count ? "statically" : "dynamically"); @@ -1504,8 +1547,7 @@ SQLRETURN EsSQLGetDescFieldW( SQLINTEGER intgr; esodbc_rec_st *rec; - if (! check_access(desc->type, FieldIdentifier, - O_RDONLY)) { + if (! check_access(desc, FieldIdentifier, O_RDONLY)) { int llev; #if 0 /* @@ -1521,7 +1563,7 @@ SQLRETURN EsSQLGetDescFieldW( llev = LOG_LEVEL_WARN; state = SQL_STATE_01000; #endif /* 0 */ - LOGH(llev, 0, desc, "field (%d) access check failed: not defined for " + LOGH(desc, llev, 0, "field (%d) access check failed: not defined for " "desciptor (type: %d).", FieldIdentifier, desc->type); RET_HDIAG(desc, state, "field type not defined for descriptor", 0); @@ -2233,7 +2275,7 @@ SQLRETURN EsSQLSetDescFieldW( SQLULEN ulen; size_t wlen; - if (! check_access(desc->type, FieldIdentifier, O_RDWR)) { + if (! check_access(desc, FieldIdentifier, O_RDWR)) { /* "The SQL_DESC_DATA_PTR field of an IPD is not normally set; * however, an application can do so to force a consistency check of * IPD fields." @@ -2593,52 +2635,4 @@ SQLRETURN EsSQLSetDescFieldW( } -#if 0 -/* - * "When the application sets the SQL_DESC_TYPE field, the driver checks that - * other fields that specify the type are valid and consistent." AND: - * - * "A consistency check is performed by the driver automatically whenever an - * application sets the SQL_DESC_DATA_PTR field of the APD, ARD, or IPD. - * Whenever this field is set, the driver checks that the value of the - * SQL_DESC_TYPE field and the values applicable to the SQL_DESC_TYPE field in - * the same record are valid and consistent. - * - * The SQL_DESC_DATA_PTR field of an IPD is not normally set; however, an - * application can do so to force a consistency check of IPD fields. The value - * that the SQL_DESC_DATA_PTR field of the IPD is set to is not actually - * stored and cannot be retrieved by a call to SQLGetDescField or - * SQLGetDescRec; the setting is made only to force the consistency check. A - * consistency check cannot be performed on an IRD." - */ -SQLRETURN EsSQLSetDescRec( - SQLHDESC DescriptorHandle, - SQLSMALLINT RecNumber, - SQLSMALLINT Type, - SQLSMALLINT SubType, - SQLLEN Length, - SQLSMALLINT Precision, - SQLSMALLINT Scale, - _Inout_updates_bytes_opt_(Length) SQLPOINTER Data, - _Inout_opt_ SQLLEN *StringLength, - _Inout_opt_ SQLLEN *Indicator) -{ - /* - * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/column-wise-binding : - * "When using column-wise binding, an application binds one or two, or in - * some cases three, arrays to each column for which data is to be - * returned. The first array holds the data values, and the second array - * holds length/indicator buffers. Indicators and length values can be - * stored in separate buffers by setting the SQL_DESC_INDICATOR_PTR and - * SQL_DESC_OCTET_LENGTH_PTR descriptor fields to different values; if - * this is done, a third array is bound. Each array contains as many - * elements as there are rows in the rowset." - */ - - // TODO: needs to trigger consistency_check - - RET_NOT_IMPLEMENTED; -} -#endif //0 - /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ diff --git a/driver/handles.h b/driver/handles.h index c57233c3..2631ba99 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -12,6 +12,7 @@ #include "error.h" #include "defs.h" +#include "log.h" /* forward declarations */ struct struct_env; @@ -37,6 +38,9 @@ typedef struct struct_hheader { /* handle header */ struct struct_stmt *stmt; void *parent; }; + /* logging helpers */ + wstr_st typew; /* ENV/DBC/STMT/DESC as w-string */ + esodbc_filelog_st *log; /* logger: owned by a DBC; ENV uses global */ } esodbc_hhdr_st; /* @@ -51,7 +55,6 @@ typedef struct struct_hheader { /* handle header */ typedef struct struct_env { esodbc_hhdr_st hdr; SQLUINTEGER version; /* versions defined as UL (see SQL_OV_ODBC3) */ - // TODO?: connections } esodbc_env_st; /* meta data types (same for both SQL_C_ and SQL_ types) */ @@ -146,7 +149,7 @@ typedef struct struct_dbc { char *str; /* as string */ char slen; /* string's length (w/o terminator) */ } fetch; - BOOL pack_json; /* should JSON be used in REST bodies? (vs. CBOR) *///TODO + BOOL pack_json; /* should JSON be used in REST bodies? (vs. CBOR) */ esodbc_estype_st *es_types; /* array with ES types */ SQLULEN no_types; /* number of types in array */ @@ -262,8 +265,7 @@ typedef struct struct_desc { SQLULEN *rows_processed_ptr; /* /header fields */ - /* array of records of .count cardinality - * TODO: list? binding occurs seldomly, compared to execution, tho. */ + /* array of records of .count cardinality */ esodbc_rec_st *recs; } esodbc_desc_st; @@ -358,12 +360,13 @@ SQLRETURN update_rec_count(esodbc_desc_st *desc, SQLSMALLINT new_count); esodbc_rec_st *get_record(esodbc_desc_st *desc, SQLSMALLINT rec_no, BOOL grow); void dump_record(esodbc_rec_st *rec); +void init_dbc(esodbc_dbc_st *dbc, SQLHANDLE InputHandle); + esodbc_desc_st *getdata_set_ard(esodbc_stmt_st *stmt, esodbc_desc_st *gd_ard, SQLUSMALLINT colno, esodbc_rec_st *recs, SQLUSMALLINT count); void getdata_reset_ard(esodbc_stmt_st *stmt, esodbc_desc_st *ard, SQLUSMALLINT colno, esodbc_rec_st *recs, SQLUSMALLINT count); -/* TODO: move to some utils.h */ void concise_to_type_code(SQLSMALLINT concise, SQLSMALLINT *type, SQLSMALLINT *code); esodbc_metatype_et concise_to_meta(SQLSMALLINT concise_type, @@ -465,17 +468,17 @@ SQLRETURN EsSQLSetDescRec( #define HND_UNLOCK(_h) ESODBC_MUX_UNLOCK(&HDRH(_h)->mutex) -/* wraper of RET_CDIAG, compatible with any defined handle */ +/* post state into the diagnostic and return state's return code */ #define RET_HDIAG(_hp/*handle ptr*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \ - RET_CDIAG(&(_hp)->hdr.diag, _s, _t, _c) + return post_diagnostic(_hp, _s, MK_WPTR(_t), _c) /* similar to RET_HDIAG, but only post the state */ #define RET_HDIAGS(_hp/*handle ptr*/, _s/*tate*/) \ - RET_DIAG(&(_hp)->hdr.diag, _s, NULL, 0) + return post_diagnostic(_hp, _s, NULL, 0) /* copy the diagnostics from one handle to the other */ #define HDIAG_COPY(_s, _d) (_d)->hdr.diag = (_s)->hdr.diag /* set a diagnostic to a(ny) handle */ #define SET_HDIAG(_hp/*handle ptr*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \ - post_diagnostic(&(_hp)->hdr.diag, _s, MK_WPTR(_t), _c) + post_diagnostic(_hp, _s, MK_WPTR(_t), _c) /* return the code associated with the given state (and debug-log) */ #define RET_STATE(_s) \ @@ -494,6 +497,27 @@ SQLRETURN EsSQLSetDescRec( (rec)->indicator_ptr != NULL || \ (rec)->octet_length_ptr != NULL) +/* + * Logging with handle + */ + +#define LOGH(hnd, lvl, werrn, fmt, ...) \ + _LOG(HDRH(hnd)->log, lvl, werrn, "[" LWPDL "@0x%p] " fmt, \ + LWSTR(&HDRH(hnd)->typew), hnd, __VA_ARGS__) + +#define ERRNH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_ERR, 1, fmt, __VA_ARGS__) +#define ERRH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_ERR, 0, fmt, __VA_ARGS__) +#define WARNH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_WARN, 0, fmt, __VA_ARGS__) +#define INFOH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_INFO, 0, fmt, __VA_ARGS__) +#define DBGH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_DBG, 0, fmt, __VA_ARGS__) + +#define BUGH(hnd, fmt, ...) \ + do { \ + ERRH(hnd, "[BUG] " fmt, __VA_ARGS__); \ + assert(0); \ + } while (0) + + #endif /* __HANDLES_H__ */ diff --git a/driver/info.c b/driver/info.c index 66b7f6da..8f7c8573 100644 --- a/driver/info.c +++ b/driver/info.c @@ -19,15 +19,16 @@ #define ORIG_CLASS_ODBC "ODBC 3.0" #if defined(_WIN32) || defined (WIN32) -/* DRV_NAME is a define */ +/* DRV_NAME defined in CMakeLists.txt */ #define DRIVER_NAME STR(DRV_NAME) ".dll" #else /* win32 */ #endif /* win32 */ -/* List of supported functions in the driver */ -// FIXME: review at alpha what's implemented +/* List of supported functions in the driver. + * Advertize them as being all implemented and fail at call time, to prevent + * an early failure in the client application. */ static SQLUSMALLINT esodbc_functions[] = { SQL_API_SQLALLOCHANDLE, SQL_API_SQLBINDCOL, @@ -75,7 +76,7 @@ static SQLUSMALLINT esodbc_functions[] = { SQL_API_SQLSTATISTICS, SQL_API_SQLTABLES, SQL_API_SQLBINDPARAMETER, - SQL_API_SQLBROWSECONNECT, + /* SQL_API_SQLBROWSECONNECT, */ /* SQL_API_SQLBULKOPERATIONS, */ SQL_API_SQLCOLUMNPRIVILEGES, SQL_API_SQLDESCRIBEPARAM, @@ -93,7 +94,6 @@ static SQLUSMALLINT esodbc_functions[] = { #define ESODBC_FUNC_SIZE \ (sizeof(esodbc_functions)/sizeof(esodbc_functions[0])) -// TODO: are these def'd in sql.h?? #define SQL_FUNC_SET(pfExists, uwAPI) \ *(((UWORD*) (pfExists)) + ((uwAPI) >> 4)) |= (1 << ((uwAPI) & 0x000F)) #define SQL_API_ODBC2_ALL_FUNCTIONS_SIZE 100 @@ -196,9 +196,6 @@ static SQLRETURN getinfo_driver( case SQL_FILE_USAGE: /* JDBC[0]: usesLocalFilePerTable() */ DBGH(dbc, "requested: file usage: table."); - /* TODO: JDBC indicates true for file per table; howerver, this - * can be apparently used to ask GUI user to ask 'file' or - * 'table'; elastic uses index => files? */ *(SQLUSMALLINT *)InfoValue = SQL_FILE_TABLE; return SQL_SUCCESS; case SQL_GETDATA_EXTENSIONS: @@ -347,7 +344,6 @@ static SQLRETURN getinfo_data_source( DBGH(dbc, "requested: accessible tables (`Y`)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); - // FIXME: review along with cursor review case SQL_BOOKMARK_PERSISTENCE: DBGH(dbc, "requested bookmark persistence (none)."); *(SQLUINTEGER *)InfoValue = 0; /* no support */ @@ -357,7 +353,6 @@ static SQLRETURN getinfo_data_source( DBGH(dbc, "requested: catalog term (`%s`).", ESODBC_CATALOG_TERM); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_CATALOG_TERM), BufferLength, StringLengthPtr); - // FIXME: review at alpha case SQL_COLLATION_SEQ: DBGH(dbc, "requested: collation seq (`UTF8`)."); return write_wstr(dbc, InfoValue, &MK_WSTR("UTF8"), @@ -869,7 +864,7 @@ SQLRETURN EsSQLGetInfoW( } -/* TODO: see error.h: esodbc_errors definition note (2.x apps support) */ +/* TODO: see error.h: esodbc_errors definition note on 2.x apps support */ /* Note: with SQL_DIAG_SQLSTATE DM provides a NULL StringLengthPtr */ SQLRETURN EsSQLGetDiagFieldW( SQLSMALLINT HandleType, @@ -885,16 +880,14 @@ SQLRETURN EsSQLGetDiagFieldW( esodbc_env_st dummy; SQLSMALLINT used; size_t len; + void *srcptr; wstr_st *wstrp, wstr; SQLRETURN ret; if (RecNumber <= 0) { ERRH(Handle, "record number must be >=1; received: %d.", RecNumber); return SQL_ERROR; - } - if (1 < RecNumber) { - /* XXX: does it make sense to have error FIFOs? see: EsSQLGetDiagRec */ - // WARN("no error lists supported (yet)."); + } else if (1 < RecNumber) { return SQL_NO_DATA; } @@ -902,41 +895,37 @@ SQLRETURN EsSQLGetDiagFieldW( ERR("null handle provided."); return SQL_INVALID_HANDLE; } - diag = &HDRH(Handle)->diag; /* GetDiagField can't set diagnostics itself, so use a dummy */ *HDRH(&dummy) = *HDRH(Handle); /* need a valid hhdr struct */ /*INDENT-OFF*/ - switch(DiagIdentifier) { + switch (DiagIdentifier) { /* Header Fields */ case SQL_DIAG_NUMBER: - if (StringLengthPtr) { - *StringLengthPtr = sizeof(SQLINTEGER); - } - if (DiagInfoPtr) { - // FIXME: check HandleType's record count (1 or 0) - *(SQLINTEGER *)DiagInfoPtr = 0; - DBGH(Handle, "available diagnostics: %d.", - *(SQLINTEGER *)DiagInfoPtr); - } else { - DBGH(Handle, "no DiagInfo buffer provided - returning "); - return SQL_SUCCESS_WITH_INFO; + if (! DiagInfoPtr) { + ERRH(Handle, "NULL DiagInfo with SQL_DIAG_NUMBER"); + return SQL_ERROR; } - FIXME; // FIXME - break; + *(SQLINTEGER *)DiagInfoPtr = + (diag->state != SQL_STATE_00000) ? 1 : 0; + DBGH(Handle, "available diagnostics count: %ld.", + *(SQLINTEGER *)DiagInfoPtr); + return SQL_SUCCESS; + case SQL_DIAG_CURSOR_ROW_COUNT: case SQL_DIAG_DYNAMIC_FUNCTION: case SQL_DIAG_DYNAMIC_FUNCTION_CODE: case SQL_DIAG_ROW_COUNT: + /* should be handled by DM */ if (HandleType != SQL_HANDLE_STMT) { ERRH(Handle, "DiagIdentifier %d called with non-statement " "handle type %d.", DiagIdentifier, HandleType); return SQL_ERROR; } - // FIXME - FIXME; - //break; + ERRH(Handle, "DiagIdentifier %hd is not supported."); + return SQL_ERROR; + /* case SQL_DIAG_RETURNCODE: break; -- DM only */ /* Record Fields */ @@ -1035,16 +1024,44 @@ SQLRETURN EsSQLGetDiagFieldW( dbc->hdr.diag = bak; return ret; - case SQL_DIAG_MESSAGE_TEXT: //break; - case SQL_DIAG_NATIVE: //break; - case SQL_DIAG_COLUMN_NUMBER: //break; - case SQL_DIAG_ROW_NUMBER: //break; - FIXME; // FIXME + case SQL_DIAG_MESSAGE_TEXT: + wstr.str = diag->text; + wstr.cnt = diag->text_len; + return write_wstr(Handle, DiagInfoPtr, &wstr, + BufferLength * sizeof(*diag->text), StringLengthPtr); + + do { + case SQL_DIAG_NATIVE: + len = sizeof(diag->native_code); + srcptr = &diag->native_code; + break; + case SQL_DIAG_COLUMN_NUMBER: + len = sizeof(diag->column_number); + srcptr = &diag->column_number; + break; + case SQL_DIAG_ROW_NUMBER: + len = sizeof(diag->row_number); + srcptr = &diag->row_number; + break; + } while (0); + if (BufferLength != SQL_IS_POINTER) { + WARNH(Handle, "BufferLength param not indicating a ptr type."); + } + if (! DiagInfoPtr) { + ERRH(Handle, "integer diagnostic field %hd asked for, but " + "NULL destination provided."); + RET_HDIAGS(Handle, SQL_STATE_HY009); + } else { + memcpy(DiagInfoPtr, srcptr, len); + } + return SQL_SUCCESS; case SQL_DIAG_SQLSTATE: if (diag->state == SQL_STATE_00000) { DBGH(Handle, "no diagnostic available for handle type %d.", HandleType); + /* "The function also returns SQL_NO_DATA for any positive + * RecNumber if there are no diagnostic records for Handle" */ return SQL_NO_DATA; } wstr.str = esodbc_errors[diag->state].code; @@ -1079,7 +1096,7 @@ SQLRETURN EsSQLGetDiagFieldW( * """ * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/implementing-sqlgetdiagrec-and-sqlgetdiagfield : */ -/* TODO: see error.h: esodbc_errors definition note (2.x apps support) */ +/* TODO: see error.h: esodbc_errors definition note on 2.x apps support */ SQLRETURN EsSQLGetDiagRecW ( SQLSMALLINT HandleType, @@ -1143,7 +1160,7 @@ SQLRETURN EsSQLGetDiagRecW } wstr.str = diag->text; - wstr.cnt = wcslen(wstr.str); + wstr.cnt = diag->text_len; *HDRH(&dummy) = *HDRH(Handle); /* need a valid hhdr struct */ ret = write_wstr(&dummy, MessageText, &wstr, BufferLength * sizeof(*MessageText), &used); diff --git a/driver/log.c b/driver/log.c index 5ff40d5c..da293e78 100644 --- a/driver/log.c +++ b/driver/log.c @@ -27,152 +27,247 @@ */ #define LOG_ERRNO_BUF_SIZE 128 -/* log level; disabled by default */ -int _esodbc_log_level = LOG_LEVEL_DISABLED; -/* log file path -- process variable */ -static TCHAR *log_path = NULL; -/* log file mutex -- process variable */ -static esodbc_mutex_lt log_mux = ESODBC_MUX_SINIT; - - -static inline HANDLE log_file_handle(BOOL open) -{ - static HANDLE log_handle = INVALID_HANDLE_VALUE; - if (open) { - if (log_handle == INVALID_HANDLE_VALUE) { - log_handle = CreateFile( - log_path, /* file name ("path") */ - GENERIC_WRITE, /* desired access */ - FILE_SHARE_WRITE, /* share mode */ - NULL, /* security attributes */ - OPEN_ALWAYS, /* creation disposition */ - FILE_ATTRIBUTE_NORMAL, /* flags & attributes */ - NULL /* template */); - } - } else { - if (log_handle != INVALID_HANDLE_VALUE) { - CloseHandle(log_handle); - log_handle = INVALID_HANDLE_VALUE; - } - } - return log_handle; -} +/* global file log */ +esodbc_filelog_st *_gf_log = NULL; BOOL log_init() { - int pos; - /* - * Fully qualified path name of the log file: - * \__ - * Example: - * C:\Users\username\AppData\Local\Temp\esodbc_20181231235959_233.log - */ - static TCHAR path[MAX_PATH]; - TCHAR *qmark; /* question mark position */ - struct tm *then; - time_t now = time(NULL); + int cnt; + wchar_t *qmark; /* question mark position */ + wstr_st str_level; + int log_level; + /* PID buffer */ + wchar_t pid_buff[sizeof("4294967295")]; + wstr_st pid = (wstr_st) { + pid_buff, sizeof(pid_buff)/sizeof(*pid_buff) + }; + /* directory path */ + wchar_t dpath_buff[MAX_PATH + 1]; + wstr_st dpath = (wstr_st) { + dpath_buff, sizeof(dpath_buff)/sizeof(*dpath_buff) + }; + /* full file path */ + wchar_t fpath_buff[MAX_PATH + 1]; + wstr_st fpath = (wstr_st) { + fpath_buff, sizeof(fpath_buff)/sizeof(*fpath_buff) + }; - pos = GetEnvironmentVariable(_T(ESODBC_ENV_VAR_LOG_DIR), path, - sizeof(path)/sizeof(path[0])); - if (! pos) { /* 0 means error */ + cnt = GetEnvironmentVariable(MK_WPTR(ESODBC_LOG_DIR_ENV_VAR), + dpath.str, (DWORD)dpath.cnt); + if (! cnt) { /* 0 means error */ /* env var wasn't defined OR error occured (which we can't log). */ return GetLastError() == ERROR_ENVVAR_NOT_FOUND; - } - if (sizeof(path)/sizeof(path[0]) < pos) { + } else if (dpath.cnt <= cnt) { /* path buffer too small */ assert(0); return FALSE; } /* is there a log level specified? */ - if ((qmark = TSTRCHR(path, LOG_LEVEL_SEPARATOR))) { + if ((qmark = wcschr(dpath.str, LOG_LEVEL_SEPARATOR))) { *qmark = 0; /* end the path here */ - pos = (int)(qmark - path); /* adjust the length of path */ - /* first letter will indicate the log level, with the default being - * debug, since this is mostly a tracing functionality */ - switch (qmark[1]) { - case 'e': - case 'E': - _esodbc_log_level = LOG_LEVEL_ERR; - break; - case 'w': - case 'W': - _esodbc_log_level = LOG_LEVEL_WARN; - break; - case 'i': - case 'I': - _esodbc_log_level = LOG_LEVEL_INFO; - break; - default: - _esodbc_log_level = LOG_LEVEL_DBG; - } + dpath.cnt = qmark - dpath.str; /* adjust the length of path */ + str_level.str = qmark + 1; + str_level.cnt = cnt - dpath.cnt - /* separator */1; + log_level = parse_log_level(&str_level); } else { + dpath.cnt = cnt; /* default log level, if not specified, is debug */ - _esodbc_log_level = LOG_LEVEL_DBG; + log_level = LOG_LEVEL_DBG; } - /* break down time to date-time */ - if (! (then = localtime(&now))) { + pid.cnt = i64tot((int64_t)GetCurrentProcessId(), pid.str, /*w?*/TRUE); + if (pid.cnt <= 0) { assert(0); - return FALSE; /* should not happen */ + return FALSE; } - /* build the log path name */ - path[pos ++] = FILE_PATH_SEPARATOR; - pos = SNTPRINTF(path + pos, sizeof(path)/sizeof(path[0]), - "%c" STPD "_%d%.2d%.2d%.2d%.2d%.2d_%u" STPD, - FILE_PATH_SEPARATOR, _T(ESODBC_LOG_FILE_PREFIX), - then->tm_year + 1900, then->tm_mon + 1, then->tm_mday, - then->tm_hour, then->tm_min, then->tm_sec, - GetCurrentProcessId(), - _T(ESODBC_LOG_FILE_SUFFIX)); - if (sizeof(path)/sizeof(path[0]) < pos) { - /* path buffer is too small */ - assert(0); + if (! filelog_print_path(&fpath, &dpath, &pid)) { return FALSE; } - /* save the file path and open the file, to check path validity */ - log_path = path; - return (log_file_handle(/* open*/TRUE) != INVALID_HANDLE_VALUE); + _gf_log = filelog_new(&fpath, log_level); + return _gf_log != NULL; } void log_cleanup() { - log_file_handle(/*open?*/FALSE); - ESODBC_MUX_DEL(&log_mux); + if (_gf_log) { + filelog_del(_gf_log); + _gf_log = NULL; + } +} +int parse_log_level(wstr_st *level) +{ + if (level->cnt < 1) { + return LOG_LEVEL_DISABLED; + } + /* first letter will indicate the log level */ + switch ((unsigned)level->str[0] | 0x20) { + case 'e': + return LOG_LEVEL_ERR; + case 'w': + return LOG_LEVEL_WARN; + case 'i': + return LOG_LEVEL_INFO; + case 'd': + return LOG_LEVEL_DBG; + } + return LOG_LEVEL_DISABLED; } -static void log_file_write(char *buff, size_t pos) +/* + * Fully qualified path name of the log file: + * \__ + * Example: + * C:\Users\username\AppData\Local\Temp\esodbc_20181231235959_233.log + */ +BOOL filelog_print_path(wstr_st *dest, wstr_st *dir_path, wstr_st *ident) { - HANDLE log_handle; - DWORD written; + wstr_st dir = *dir_path; + int cnt; + time_t now = time(NULL); + struct tm *then = localtime(&now); /* "single tm structure per thread" */ + + if (! then) { + assert(0); /* should not happen */ + return FALSE; + } + + /* strip trailing path separator */ + for (; 0 < dir.cnt; dir.cnt --) { + if (dir.str[dir.cnt - 1] != MK_WPTR(FILE_PATH_SEPARATOR)) { + break; + } + } + if (dir.cnt <= 0) { + /* input was just '\' (or empty) */ + return FALSE; + } + + /* build the log full path name */ + cnt = _snwprintf(dest->str, dest->cnt, + L"%.*s" "%c" "%s" "_%d%.2d%.2d%.2d%.2d%.2d_" "%.*s" "%s", + (int)dir.cnt, dir.str, + FILE_PATH_SEPARATOR, + MK_WPTR(ESODBC_LOG_FILE_PREFIX), + then->tm_year + 1900, then->tm_mon + 1, then->tm_mday, + then->tm_hour, then->tm_min, then->tm_sec, + (int)ident->cnt, ident->str, + MK_WPTR(ESODBC_LOG_FILE_SUFFIX)); - log_handle = log_file_handle(/*open*/TRUE); - if (log_handle == INVALID_HANDLE_VALUE) { + if (cnt <= 0 || dest->cnt <= cnt) { + /* fpath buffer is too small */ + return FALSE; + } else { + dest->cnt = cnt; + } + + return TRUE; +} + +BOOL filelog_reset(esodbc_filelog_st *log) +{ + if (ESODBC_LOG_MAX_RETRY < log->fails) { + /* disable logging alltogether on this logger */ + log->level = LOG_LEVEL_DISABLED; + log->handle = INVALID_HANDLE_VALUE; + return FALSE; + } + if (log->handle != INVALID_HANDLE_VALUE) { + CloseHandle(log->handle); + } + log->handle = CreateFile( + log->path, /* file name ("path") */ + GENERIC_WRITE, /* desired access */ + FILE_SHARE_WRITE, /* share mode */ + NULL, /* security attributes */ + OPEN_ALWAYS, /* creation disposition */ + FILE_ATTRIBUTE_NORMAL, /* flags & attributes */ + NULL /* template */); + if (log->handle == INVALID_HANDLE_VALUE) { + log->fails ++; + return FALSE; + } + return TRUE; +} + +esodbc_filelog_st *filelog_new(wstr_st *path, int level) +{ + esodbc_filelog_st *log; + + if (! (log = malloc(sizeof(*log) + + (path->cnt + /*\0*/1) * sizeof(*log->path)))) { + return NULL; + } + memset(log, 0, sizeof(*log)); + + log->path = (wchar_t *)((char *)log + sizeof(*log)); + wcsncpy(log->path, path->str, path->cnt); + log->path[path->cnt] = L'\0'; + + log->level = level; + log->handle = INVALID_HANDLE_VALUE; + ESODBC_MUX_INIT(&log->mux); + + if (LOG_LEVEL_INFO <= level) { +#ifndef NDEBUG + _LOG(log, LOG_LEVEL_INFO, /*werr*/0, "level: %d, file: " LWPDL ".", + level, LWSTR(path)); +#endif /* NDEBUG */ + _LOG(log, LOG_LEVEL_INFO, /*werr*/0, "driver version: %s.", + ESODBC_DRIVER_VER); + } + + return log; +} + +void filelog_del(esodbc_filelog_st *log) +{ + if (! log) { return; } + if (log->handle != INVALID_HANDLE_VALUE) { + CloseHandle(log->handle); + } + ESODBC_MUX_DEL(&log->mux); + free(log); +} + +static BOOL filelog_write(esodbc_filelog_st *log, char *buff, size_t cnt) +{ + DWORD written; /* write the buffer to file */ if (! WriteFile( - log_handle, /*handle*/ + log->handle, /*handle*/ buff, /* buffer */ - (DWORD)(pos * sizeof(buff[0])), /*bytes to write */ + (DWORD)(cnt * sizeof(buff[0])), /*bytes to write */ &written /* bytes written */, NULL /*overlap*/)) { - log_file_handle(/* close */FALSE); + log->fails ++; + if (filelog_reset(log)) { + /* reattempt the write, if reset is successfull */ + if (filelog_write(log, buff, cnt)) { + log->fails = 0; + return TRUE; + } + } + return FALSE; } else { #ifndef NDEBUG #ifdef _WIN32 - //FlushFileBuffers(log_handle); + //FlushFileBuffers(log->handle); #endif /* _WIN32 */ #endif /* NDEBUG */ } + return TRUE; } -static inline void log_file(int level, int werrno, const char *func, - const char *srcfile, int lineno, const char *fmt, va_list args) +static inline void filelog_log(esodbc_filelog_st *log, + int level, int werrno, const char *func, const char *srcfile, int lineno, + const char *fmt, va_list args) { time_t now = time(NULL); int ret; @@ -184,7 +279,6 @@ static inline void log_file(int level, int werrno, const char *func, static const char *level2str[] = { "ERROR", "WARN", "INFO", "DEBUG", }; assert(level < sizeof(level2str)/sizeof(level2str[0])); - /* FIXME: 4!WINx */ if (ctime_s(buff, sizeof(buff), &now)) { /* writing failed */ pos = 0; @@ -201,13 +295,12 @@ static inline void log_file(int level, int werrno, const char *func, assert(pos == 24); } - /* drop path from file name */ + /* drop path from source file name */ for (next = srcfile; next; next = strchr(sfile, FILE_PATH_SEPARATOR)) { sfile = next + 1; } /* write the debugging prefix */ - /* XXX: add release (file o.a.) printing? */ if ((ret = snprintf(buff + pos, sizeof(buff) - pos, " - [%s] %s()@%s:%d ", level2str[level], func, sfile, lineno)) < 0) { return; @@ -224,7 +317,6 @@ static inline void log_file(int level, int werrno, const char *func, pos += ret; } - /* FIXME: 4!WINx */ if (strerror_s(ebuff, sizeof(ebuff), errno)) { return; } @@ -254,17 +346,17 @@ static inline void log_file(int level, int werrno, const char *func, } assert(pos <= sizeof(buff)); - ESODBC_MUX_LOCK(&log_mux); - log_file_write(buff, pos); - ESODBC_MUX_UNLOCK(&log_mux); + ESODBC_MUX_LOCK(&log->mux); + filelog_write(log, buff, pos); + ESODBC_MUX_UNLOCK(&log->mux); } -void _esodbc_log(int lvl, int werrno, const char *func, - const char *srcfile, int lineno, const char *fmt, ...) +void _esodbc_log(esodbc_filelog_st *log, int lvl, int werrno, + const char *func, const char *srcfile, int lineno, const char *fmt, ...) { va_list args; va_start(args, fmt); - log_file(lvl, werrno, func, srcfile, lineno, fmt, args); + filelog_log(log, lvl, werrno, func, srcfile, lineno, fmt, args); va_end(args); } diff --git a/driver/log.h b/driver/log.h index 67bde196..5f53da89 100644 --- a/driver/log.h +++ b/driver/log.h @@ -13,7 +13,6 @@ #include "util.h" #include "error.h" -#include "handles.h" /* @@ -68,18 +67,35 @@ enum osodbc_log_levels { BOOL log_init(); void log_cleanup(); -void _esodbc_log(int lvl, int werrno, const char *func, - const char *srcfile, int lineno, const char *fmt, ...); -extern int _esodbc_log_level; +typedef struct struct_filelog { + int level; + HANDLE handle; + wchar_t *path; + unsigned char fails; + esodbc_mutex_lt mux; +} esodbc_filelog_st; -#define _LOG(lvl, werr, fmt, ...) \ - if ((lvl) <= _esodbc_log_level) \ - _esodbc_log(lvl, werr, __func__, __FILE__, __LINE__, fmt, __VA_ARGS__) +esodbc_filelog_st *filelog_new(wstr_st *path, int level); +void filelog_del(esodbc_filelog_st *log); +int parse_log_level(wstr_st *level); +BOOL filelog_print_path(wstr_st *dest, wstr_st *dir_path, wstr_st *ident); -#define LOG(lvl, fmt, ...) _LOG(lvl, 0, fmt, __VA_ARGS__) +extern esodbc_filelog_st *_gf_log; +void _esodbc_log(esodbc_filelog_st *log, int lvl, int werrno, + const char *func, const char *srcfile, int lineno, const char *fmt, ...); + +#define _LOG(logp, lvl, werr, fmt, ...) \ + do { \ + if ((logp) && ((lvl) <= (logp)->level)) { \ + _esodbc_log(logp, lvl, werr, __func__, __FILE__, __LINE__, \ + fmt, __VA_ARGS__); \ + } \ + } while (0) + +#define LOG(lvl, fmt, ...) _LOG(_gf_log, lvl, 0, fmt, __VA_ARGS__) +#define ERRN(fmt, ...) _LOG(_gf_log, LOG_LEVEL_ERR, 1, fmt, __VA_ARGS__) -#define ERRN(fmt, ...) _LOG(LOG_LEVEL_ERR, 1, fmt, __VA_ARGS__) #define ERR(fmt, ...) LOG(LOG_LEVEL_ERR, fmt, __VA_ARGS__) #define WARN(fmt, ...) LOG(LOG_LEVEL_WARN, fmt, __VA_ARGS__) #define INFO(fmt, ...) LOG(LOG_LEVEL_INFO, fmt, __VA_ARGS__) @@ -99,47 +115,6 @@ extern int _esodbc_log_level; #define TS_NULL MK_CPTR("") -/* - * Logging with handle - */ - -/* get handle type prefix */ -static inline char *_hhtype2str(SQLHANDLE handle) -{ - if (! handle) { - return ""; - } - switch (HDRH(handle)->type) { - case SQL_HANDLE_ENV: - return "ENV"; - case SQL_HANDLE_DBC: - return "DBC"; - case SQL_HANDLE_STMT: - return "STMT"; - case SQL_HANDLE_DESC: - return "DESC"; - } - /* likely mem corruption, it'll probably crash */ - BUG("unknown handle (@0x%p) type (%d)", handle, HDRH(handle)->type); - return "???"; -} - -#define LOGH(lvl, werrn, hnd, fmt, ...) \ - _LOG(lvl, werrn, "[%s@0x%p] " fmt, _hhtype2str(hnd), hnd, __VA_ARGS__) - -#define ERRNH(hnd, fmt, ...) LOGH(LOG_LEVEL_ERR, 1, hnd, fmt, __VA_ARGS__) -#define ERRH(hnd, fmt, ...) LOGH(LOG_LEVEL_ERR, 0, hnd, fmt, __VA_ARGS__) -#define WARNH(hnd, fmt, ...) LOGH(LOG_LEVEL_WARN, 0, hnd, fmt, __VA_ARGS__) -#define INFOH(hnd, fmt, ...) LOGH(LOG_LEVEL_INFO, 0, hnd, fmt, __VA_ARGS__) -#define DBGH(hnd, fmt, ...) LOGH(LOG_LEVEL_DBG, 0, hnd, fmt, __VA_ARGS__) - -#define BUGH(hnd, fmt, ...) \ - do { \ - ERRH(hnd, "[BUG] " fmt, __VA_ARGS__); \ - assert(0); \ - } while (0) - - #endif /* __LOG_H__ */ diff --git a/driver/odbc.c b/driver/odbc.c index 6b746e64..aacd709a 100644 --- a/driver/odbc.c +++ b/driver/odbc.c @@ -16,21 +16,18 @@ //#include "elasticodbc_export.h" //#define SQL_API ELASTICODBC_EXPORT SQL_API -// compile in empty functions (less unref'd params when leaving them out) -#define WITH_EMPTY 1 - -#define RET_NOT_IMPLEMENTED \ +#define RET_NOT_IMPLEMENTED(hnd) \ do { \ - ERR("not implemented.");\ - return SQL_ERROR; \ + ERR("not implemented."); \ + RET_HDIAGS(hnd, SQL_STATE_HYC00); \ } while (0) static BOOL driver_init() { if (log_init()) { - INFO("initializing driver %s.", ESODBC_DRIVER_VER); + INFO("initializing driver."); convert_init(); return connect_init(); } @@ -87,14 +84,14 @@ SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, _Out_ SQLHANDLE *OutputHandle) { SQLRETURN ret; - TRACE3(_IN, "dpp", HandleType, InputHandle, OutputHandle); + TRACE3(_IN, InputHandle, "dpp", HandleType, InputHandle, OutputHandle); /* no synchronization required */ ret = EsSQLAllocHandle(HandleType, InputHandle, OutputHandle); - TRACE4(_OUT, "ddpp", ret, HandleType, InputHandle, *OutputHandle); + TRACE4(_OUT, InputHandle, "dhpp", ret, HandleType, InputHandle, + *OutputHandle); return ret; } -#if WITH_EMPTY /* * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/unicode-drivers : * """ @@ -113,37 +110,59 @@ SQLRETURN SQL_API SQLConnectW SQLSMALLINT cchAuthStr ) { - RET_NOT_IMPLEMENTED; +#ifndef NDEBUG /* don't print the PWD */ + const char *fmt_in = "pWhWhWh"; + const char *fmt_out = "dpWhWhWh"; +#else /* NDEBUG */ + const char *fmt_in = "pWhWhph"; + const char *fmt_out = "dpWhWhph"; +#endif /* NDEBUG */ + SQLRETURN ret; + TRACE7(_IN, hdbc, fmt_in, hdbc, szDSN, cchDSN, szUID, cchUID, + szAuthStr, cchAuthStr); + HND_LOCK(hdbc); + ret = EsSQLConnectW(hdbc, szDSN, cchDSN, szUID, cchUID, + szAuthStr, cchAuthStr); + HND_UNLOCK(hdbc); + TRACE8(_OUT, hdbc, fmt_out, ret, hdbc, szDSN, cchDSN, szUID, cchUID, + szAuthStr, cchAuthStr); + return ret; } -#endif /* WITH_EMPTY */ - SQLRETURN SQL_API SQLDriverConnectW ( SQLHDBC hdbc, SQLHWND hwnd, - _In_reads_(cchConnStrIn) SQLWCHAR *szConnStrIn, + _In_reads_(cchConnStrIn) + SQLWCHAR *szConnStrIn, SQLSMALLINT cchConnStrIn, - _Out_writes_opt_(cchConnStrOutMax) SQLWCHAR *szConnStrOut, + _Out_writes_opt_(cchConnStrOutMax) + SQLWCHAR *szConnStrOut, SQLSMALLINT cchConnStrOutMax, - _Out_opt_ SQLSMALLINT *pcchConnStrOut, + _Out_opt_ + SQLSMALLINT *pcchConnStrOut, SQLUSMALLINT fDriverCompletion ) { +#ifndef NDEBUG /* don't print the PWD */ + const char *fmt_in = "ppWhphpH"; + const char *fmt_out = "dppWhWhtH"; +#else /* NDEBUG */ + const char *fmt_in = "ppphphpH"; + const char *fmt_out = "dppphWhtH"; +#endif /* NDEBUG */ SQLRETURN ret; - TRACE8(_IN, "pppdpdpd", hdbc, hwnd, szConnStrIn, cchConnStrIn, + TRACE8(_IN, hdbc, fmt_in, hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); HND_LOCK(hdbc); ret = EsSQLDriverConnectW(hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); HND_UNLOCK(hdbc); - TRACE9(_OUT, "dppWdWdtd", ret, hdbc, hwnd, szConnStrIn, cchConnStrIn, + TRACE9(_OUT, hdbc, fmt_out, ret, hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); return ret; } -#if WITH_EMPTY - SQLRETURN SQL_API SQLBrowseConnectW ( SQLHDBC hdbc, @@ -155,53 +174,9 @@ SQLRETURN SQL_API SQLBrowseConnectW SQLSMALLINT *pcchConnStrOut ) { - RET_NOT_IMPLEMENTED; -} - - - -/* - * - * Obtaining information about a driver and data source - * - */ - -SQLRETURN SQL_API SQLDataSourcesW -( - SQLHENV henv, - SQLUSMALLINT fDirection, - _Out_writes_opt_(cchDSNMax) SQLWCHAR *szDSN, - SQLSMALLINT cchDSNMax, - _Out_opt_ - SQLSMALLINT *pcchDSN, - _Out_writes_opt_(cchDescriptionMax) SQLWCHAR *wszDescription, - SQLSMALLINT cchDescriptionMax, - _Out_opt_ - SQLSMALLINT *pcchDescription -) -{ - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hdbc); } -SQLRETURN SQL_API SQLDriversW -( - SQLHENV henv, - SQLUSMALLINT fDirection, - _Out_writes_opt_(cchDriverDescMax) SQLWCHAR *szDriverDesc, - SQLSMALLINT cchDriverDescMax, - _Out_opt_ - SQLSMALLINT *pcchDriverDesc, - _Out_writes_opt_(cchDrvrAttrMax) SQLWCHAR *szDriverAttributes, - SQLSMALLINT cchDrvrAttrMax, - _Out_opt_ - SQLSMALLINT *pcchDrvrAttr -) -{ - RET_NOT_IMPLEMENTED; -} - -#endif /* WITH_EMPTY */ - SQLRETURN SQL_API SQLGetInfoW(SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, @@ -209,8 +184,8 @@ SQLRETURN SQL_API SQLGetInfoW(SQLHDBC ConnectionHandle, _Out_opt_ SQLSMALLINT *StringLengthPtr) { SQLRETURN ret; - TRACE5(_IN, "pupdp", ConnectionHandle, InfoType, InfoValue, - BufferLength, StringLengthPtr); + TRACE5(_IN, ConnectionHandle, "pHphp", ConnectionHandle, InfoType, + InfoValue, BufferLength, StringLengthPtr); /* Note_sync: no synchronization really required for setting/getting of * integer attributes (atomic) or string ones (= reading static string * locations), but error handling involves posting an SQL state & string, @@ -219,7 +194,7 @@ SQLRETURN SQL_API SQLGetInfoW(SQLHDBC ConnectionHandle, ret = EsSQLGetInfoW(ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); HND_UNLOCK(ConnectionHandle); - TRACE6(_OUT, "dpupdt", ret, ConnectionHandle, InfoType, + TRACE6(_OUT, ConnectionHandle, "dpHpht", ret, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); return ret; } @@ -231,10 +206,12 @@ SQLRETURN SQL_API SQLGetFunctions(SQLHDBC ConnectionHandle, SQLUSMALLINT *Supported) { SQLRETURN ret; - TRACE3(_IN, "pdp", ConnectionHandle, FunctionId, Supported); + TRACE3(_IN, ConnectionHandle, "pHp", ConnectionHandle, FunctionId, + Supported); /* no synchronization required */ ret = EsSQLGetFunctions(ConnectionHandle, FunctionId, Supported); - TRACE4(_IN, "dpdT", ret, ConnectionHandle, FunctionId, Supported); + TRACE4(_IN, ConnectionHandle, "dpHT", ret, ConnectionHandle, FunctionId, + Supported); return ret; } @@ -243,13 +220,12 @@ SQLRETURN SQL_API SQLGetTypeInfoW( SQLSMALLINT DataType) { SQLRETURN ret; - TRACE2(_IN, "pd", StatementHandle, DataType); + TRACE2(_IN, StatementHandle, "ph", StatementHandle, DataType); HND_LOCK(StatementHandle); ret = EsSQLGetTypeInfoW(StatementHandle, DataType); HND_UNLOCK(StatementHandle); - TRACE3(_OUT, "dpd", ret, StatementHandle, DataType); + TRACE3(_OUT, StatementHandle, "dph", ret, StatementHandle, DataType); return ret; - //RET_NOT_IMPLEMENTED; } @@ -266,13 +242,14 @@ SQLRETURN SQL_API SQLSetConnectAttrW( SQLINTEGER StringLength) { SQLRETURN ret; - TRACE4(_IN, "pdpd", ConnectionHandle, Attribute, Value, StringLength); + TRACE4(_IN, ConnectionHandle, "plpl", ConnectionHandle, Attribute, + Value, StringLength); HND_LOCK(ConnectionHandle); /* see Note_sync above */ - ret = EsSQLSetConnectAttrW(ConnectionHandle, Attribute, Value, - StringLength); + ret = EsSQLSetConnectAttrW(ConnectionHandle, Attribute, + Value, StringLength); HND_UNLOCK(ConnectionHandle); - TRACE5(_OUT, "dpdpd", ret, ConnectionHandle, Attribute, Value, - StringLength); + TRACE5(_OUT, ConnectionHandle, "dplpl", ret, ConnectionHandle, Attribute, + Value, StringLength); return ret; } @@ -284,47 +261,51 @@ SQLRETURN SQL_API SQLGetConnectAttrW( _Out_opt_ SQLINTEGER *StringLengthPtr) { SQLRETURN ret; - TRACE5(_IN, "pdpdp", ConnectionHandle, Attribute, ValuePtr, - BufferLength, StringLengthPtr); + TRACE5(_IN, ConnectionHandle, "plplp", ConnectionHandle, Attribute, + ValuePtr, BufferLength, StringLengthPtr); HND_LOCK(ConnectionHandle); - ret = EsSQLGetConnectAttrW(ConnectionHandle, Attribute, ValuePtr, - BufferLength, StringLengthPtr); + ret = EsSQLGetConnectAttrW(ConnectionHandle, Attribute, + ValuePtr, BufferLength, StringLengthPtr); HND_UNLOCK(ConnectionHandle); - TRACE6(_OUT, "dpdpdg", ret, ConnectionHandle, Attribute, ValuePtr, - BufferLength, StringLengthPtr); + TRACE6(_OUT, ConnectionHandle, "dplplg", ret, ConnectionHandle, Attribute, + ValuePtr, BufferLength, StringLengthPtr); return ret; } -SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV EnvironmentHandle, +SQLRETURN SQL_API SQLSetEnvAttr( + SQLHENV EnvironmentHandle, SQLINTEGER Attribute, _In_reads_bytes_opt_(StringLength) SQLPOINTER Value, SQLINTEGER StringLength) { SQLRETURN ret; - TRACE4(_IN, "pdpd", EnvironmentHandle, Attribute, Value, StringLength); + TRACE4(_IN, EnvironmentHandle, "plpl", EnvironmentHandle, Attribute, Value, + StringLength); HND_LOCK(EnvironmentHandle); /* see Note_sync above */ ret = EsSQLSetEnvAttr(EnvironmentHandle, Attribute, Value, StringLength); HND_UNLOCK(EnvironmentHandle); - TRACE5(_OUT, "dpdpd", ret, EnvironmentHandle, Attribute, Value, - StringLength); + TRACE5(_OUT, EnvironmentHandle, "dplpl", ret, EnvironmentHandle, Attribute, + Value, StringLength); return ret; } -SQLRETURN SQL_API SQLGetEnvAttr(SQLHENV EnvironmentHandle, +SQLRETURN SQL_API SQLGetEnvAttr( + SQLHENV EnvironmentHandle, SQLINTEGER Attribute, _Out_writes_(_Inexpressible_(BufferLength)) SQLPOINTER Value, - SQLINTEGER BufferLength, _Out_opt_ SQLINTEGER *StringLength) + SQLINTEGER BufferLength, + _Out_opt_ SQLINTEGER *StringLength) { SQLRETURN ret; - TRACE5(_IN, "pdpdp", EnvironmentHandle, Attribute, Value, BufferLength, - StringLength); + TRACE5(_IN, EnvironmentHandle, "plplp", EnvironmentHandle, + Attribute, Value, BufferLength, StringLength); HND_LOCK(EnvironmentHandle); /* see Note_sync above */ - ret = EsSQLGetEnvAttr(EnvironmentHandle, Attribute, Value, BufferLength, - StringLength); + ret = EsSQLGetEnvAttr(EnvironmentHandle, + Attribute, Value, BufferLength, StringLength); HND_UNLOCK(EnvironmentHandle); - TRACE6(_OUT, "dpdpdg", ret, EnvironmentHandle, Attribute, Value, - BufferLength, StringLength); + TRACE6(_OUT, EnvironmentHandle, "dplplg", ret, EnvironmentHandle, + Attribute, Value, BufferLength, StringLength); return ret; } @@ -335,13 +316,14 @@ SQLRETURN SQL_API SQLSetStmtAttrW( SQLINTEGER BufferLength) { SQLRETURN ret; - TRACE4(_IN, "pdpd", StatementHandle, Attribute, ValuePtr, BufferLength); + TRACE4(_IN, StatementHandle, "plpl", StatementHandle, Attribute, + ValuePtr, BufferLength); HND_LOCK(StatementHandle); - ret = EsSQLSetStmtAttrW(StatementHandle, Attribute, ValuePtr, - BufferLength); + ret = EsSQLSetStmtAttrW(StatementHandle, Attribute, + ValuePtr, BufferLength); HND_UNLOCK(StatementHandle); - TRACE5(_OUT, "dpdpd", ret, StatementHandle, Attribute, ValuePtr, - BufferLength); + TRACE5(_OUT, StatementHandle, "dplpl", ret, StatementHandle, Attribute, + ValuePtr, BufferLength); return ret; } @@ -353,14 +335,14 @@ SQLRETURN SQL_API SQLGetStmtAttrW( SQLINTEGER *StringLengthPtr) { SQLRETURN ret; - TRACE5(_IN, "pdpdp", StatementHandle, Attribute, ValuePtr, BufferLength, - StringLengthPtr); + TRACE5(_IN, StatementHandle, "plplp", StatementHandle, Attribute, + ValuePtr, BufferLength, StringLengthPtr); HND_LOCK(StatementHandle); - ret = EsSQLGetStmtAttrW(StatementHandle, Attribute, ValuePtr, BufferLength, - StringLengthPtr); + ret = EsSQLGetStmtAttrW(StatementHandle, Attribute, + ValuePtr, BufferLength, StringLengthPtr); HND_UNLOCK(StatementHandle); - TRACE6(_OUT, "dpdpdg", ret, StatementHandle, Attribute, ValuePtr, - BufferLength, StringLengthPtr); + TRACE6(_OUT, StatementHandle, "dplplg", ret, StatementHandle, Attribute, + ValuePtr, BufferLength, StringLengthPtr); return ret; } @@ -381,19 +363,19 @@ SQLRETURN SQL_API SQLGetDescFieldW( SQLINTEGER *StringLengthPtr) { SQLRETURN ret; - TRACE6(_IN, "pddpdp", DescriptorHandle, RecNumber, FieldIdentifier, - ValuePtr, BufferLength, StringLengthPtr); + TRACE6(_IN, DescriptorHandle, "phhplp", DescriptorHandle, RecNumber, + FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr); /* Note_stmt_sync: API descriptor access is serialized by statement's * mutex, not descriptor's, since statement functions working on * descriptors won't lock these, but will lock the statement instead * (which keeps the code simpler; besides, no "high-speed" concurrent * thread access on descriptors is necessary anyway). */ HND_LOCK(DSCH(DescriptorHandle)->hdr.stmt); - ret = EsSQLGetDescFieldW(DescriptorHandle, RecNumber, FieldIdentifier, - ValuePtr, BufferLength, StringLengthPtr); + ret = EsSQLGetDescFieldW(DescriptorHandle, RecNumber, + FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr); HND_UNLOCK(DSCH(DescriptorHandle)->hdr.stmt); - TRACE7(_OUT, "dpddpdg", ret, DescriptorHandle, RecNumber, FieldIdentifier, - ValuePtr, BufferLength, StringLengthPtr); + TRACE7(_OUT, DescriptorHandle, "dphhplg", ret, DescriptorHandle, RecNumber, + FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr); return ret; } @@ -407,18 +389,17 @@ SQLRETURN SQL_API SQLSetDescFieldW ) { SQLRETURN ret; - TRACE5(_IN, "pddpd", DescriptorHandle, RecNumber, FieldIdentifier, - Value, BufferLength); + TRACE5(_IN, DescriptorHandle, "phhpl", DescriptorHandle, RecNumber, + FieldIdentifier, Value, BufferLength); HND_LOCK(DSCH(DescriptorHandle)->hdr.stmt); /* see Note_stmt_sync */ - ret = EsSQLSetDescFieldW(DescriptorHandle, RecNumber, FieldIdentifier, - Value, BufferLength); + ret = EsSQLSetDescFieldW(DescriptorHandle, RecNumber, + FieldIdentifier, Value, BufferLength); HND_UNLOCK(DSCH(DescriptorHandle)->hdr.stmt); - TRACE6(_OUT, "dpddpd", ret, DescriptorHandle, RecNumber, FieldIdentifier, - Value, BufferLength); + TRACE6(_OUT, DescriptorHandle, "dphhpl", ret, DescriptorHandle, RecNumber, + FieldIdentifier, Value, BufferLength); return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLGetDescRecW( SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, @@ -441,10 +422,27 @@ SQLRETURN SQL_API SQLGetDescRecW( _Out_opt_ SQLSMALLINT *NullablePtr) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(DescriptorHandle); } +/* + * "When the application sets the SQL_DESC_TYPE field, the driver checks that + * other fields that specify the type are valid and consistent." AND: + * + * "A consistency check is performed by the driver automatically whenever an + * application sets the SQL_DESC_DATA_PTR field of the APD, ARD, or IPD. + * Whenever this field is set, the driver checks that the value of the + * SQL_DESC_TYPE field and the values applicable to the SQL_DESC_TYPE field in + * the same record are valid and consistent. + * + * The SQL_DESC_DATA_PTR field of an IPD is not normally set; however, an + * application can do so to force a consistency check of IPD fields. The value + * that the SQL_DESC_DATA_PTR field of the IPD is set to is not actually + * stored and cannot be retrieved by a call to SQLGetDescField or + * SQLGetDescRec; the setting is made only to force the consistency check. A + * consistency check cannot be performed on an IRD." + */ SQLRETURN SQL_API SQLSetDescRec( SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, @@ -457,7 +455,21 @@ SQLRETURN SQL_API SQLSetDescRec( _Inout_opt_ SQLLEN *StringLength, _Inout_opt_ SQLLEN *Indicator) { - RET_NOT_IMPLEMENTED; + /* + * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/column-wise-binding : + * "When using column-wise binding, an application binds one or two, or in + * some cases three, arrays to each column for which data is to be + * returned. The first array holds the data values, and the second array + * holds length/indicator buffers. Indicators and length values can be + * stored in separate buffers by setting the SQL_DESC_INDICATOR_PTR and + * SQL_DESC_OCTET_LENGTH_PTR descriptor fields to different values; if + * this is done, a third array is bound. Each array contains as many + * elements as there are rows in the rowset." + */ + + /* needs to trigger consistency_check */ + + RET_NOT_IMPLEMENTED(DescriptorHandle); } /* @@ -475,9 +487,8 @@ SQLRETURN SQL_API SQLSetDescRec( SQLRETURN SQL_API SQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(SourceDescHandle); } -#endif // WITH_EMPTY /* * "The prepared statement associated with the statement handle can be @@ -489,16 +500,17 @@ SQLRETURN SQL_API SQLCopyDesc(SQLHDESC SourceDescHandle, SQLRETURN SQL_API SQLPrepareW ( SQLHSTMT hstmt, - _In_reads_(cchSqlStr) SQLWCHAR *szSqlStr, + _In_reads_(cchSqlStr) + SQLWCHAR *szSqlStr, SQLINTEGER cchSqlStr ) { SQLRETURN ret; - TRACE3(_IN, "ppd", hstmt, szSqlStr, cchSqlStr); + TRACE3(_IN, hstmt, "pWl", hstmt, szSqlStr, cchSqlStr); HND_LOCK(hstmt); ret = EsSQLPrepareW(hstmt, szSqlStr, cchSqlStr); HND_UNLOCK(hstmt); - TRACE4(_OUT, "dppd", ret, hstmt, szSqlStr, cchSqlStr); + TRACE4(_OUT, hstmt, "dpWl", ret, hstmt, szSqlStr, cchSqlStr); return ret; } @@ -521,18 +533,17 @@ SQLRETURN SQL_API SQLBindParameter( SQLLEN *pcbValue) { SQLRETURN ret; - TRACE10(_IN, "pudddudpdp", hstmt, ipar, fParamType, fCType, + TRACE10(_IN, hstmt, "pHhhhZhpzp", hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); HND_LOCK(hstmt); ret = EsSQLBindParameter(hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); HND_UNLOCK(hstmt); - TRACE11(_OUT, "dpudddudpdn", ret, hstmt, ipar, fParamType, fCType, + TRACE11(_OUT, hstmt, "dpHhhhZhpzn", ret, hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLGetCursorNameW ( SQLHSTMT hstmt, @@ -542,7 +553,7 @@ SQLRETURN SQL_API SQLGetCursorNameW SQLSMALLINT *pcchCursor ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLSetCursorNameW @@ -552,7 +563,7 @@ SQLRETURN SQL_API SQLSetCursorNameW SQLSMALLINT cchCursor ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLSetScrollOptions( /* Use SQLSetStmtOptions */ @@ -561,9 +572,8 @@ SQLRETURN SQL_API SQLSetScrollOptions( /* Use SQLSetStmtOptions */ SQLLEN crowKeyset, SQLUSMALLINT crowRowset) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif // WITH_EMPTY /* * @@ -586,11 +596,11 @@ SQLRETURN SQL_API SQLSetScrollOptions( /* Use SQLSetStmtOptions */ SQLRETURN SQL_API SQLExecute(SQLHSTMT hstmt) { SQLRETURN ret; - TRACE1(_IN, "p", hstmt); + TRACE1(_IN, hstmt, "p", hstmt); HND_LOCK(hstmt); ret = EsSQLExecute(hstmt); HND_UNLOCK(hstmt); - TRACE2(_OUT, "dp", ret, hstmt); + TRACE2(_OUT, hstmt, "dp", ret, hstmt); return ret; } @@ -609,31 +619,40 @@ SQLRETURN SQL_API SQLExecute(SQLHSTMT hstmt) SQLRETURN SQL_API SQLExecDirectW ( SQLHSTMT hstmt, - _In_reads_opt_(TextLength) SQLWCHAR *szSqlStr, - SQLINTEGER cchSqlStr + _In_reads_opt_(TextLength) + SQLWCHAR *szSqlStr, + SQLINTEGER cchSqlStr ) { SQLRETURN ret; - TRACE3(_IN, "ppd", hstmt, szSqlStr, cchSqlStr); + TRACE3(_IN, hstmt, "pWl", hstmt, szSqlStr, cchSqlStr); HND_LOCK(hstmt); ret = EsSQLExecDirectW(hstmt, szSqlStr, cchSqlStr); HND_UNLOCK(hstmt); - TRACE4(_OUT, "dpWd", ret, hstmt, szSqlStr, cchSqlStr); + TRACE4(_OUT, hstmt, "dpWl", ret, hstmt, szSqlStr, cchSqlStr); return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLNativeSqlW ( SQLHDBC hdbc, - _In_reads_(cchSqlStrIn) SQLWCHAR *szSqlStrIn, + _In_reads_(cchSqlStrIn) SQLWCHAR *szSqlStrIn, SQLINTEGER cchSqlStrIn, _Out_writes_opt_(cchSqlStrMax) SQLWCHAR *szSqlStr, SQLINTEGER cchSqlStrMax, SQLINTEGER *pcchSqlStr ) { - RET_NOT_IMPLEMENTED; + SQLRETURN ret; + TRACE6(_IN, hdbc, "pplplp", hdbc, szSqlStrIn, cchSqlStrIn, szSqlStr, + cchSqlStrMax, pcchSqlStr); + HND_LOCK(hdbc); + ret = EsSQLNativeSqlW(hdbc, szSqlStrIn, cchSqlStrIn, szSqlStr, + cchSqlStrMax, pcchSqlStr); + HND_UNLOCK(hdbc); + TRACE7(_OUT, hdbc, "dpplplg", ret, hdbc, szSqlStrIn, cchSqlStrIn, szSqlStr, + cchSqlStrMax, pcchSqlStr); + return ret; } /* @@ -656,9 +675,8 @@ SQLRETURN SQL_API SQLDescribeParam( _Out_opt_ SQLSMALLINT *pfNullable) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif /* WITH_EMPTY */ SQLRETURN SQL_API SQLNumParams( SQLHSTMT hstmt, @@ -666,28 +684,26 @@ SQLRETURN SQL_API SQLNumParams( SQLSMALLINT *pcpar) { SQLRETURN ret; - TRACE2(_IN, "pp", hstmt, pcpar); + TRACE2(_IN, hstmt, "pp", hstmt, pcpar); HND_LOCK(hstmt); ret = EsSQLNumParams(hstmt, pcpar); HND_UNLOCK(hstmt); - TRACE3(_OUT, "dpt", ret, hstmt, pcpar); + TRACE3(_OUT, hstmt, "dpt", ret, hstmt, pcpar); return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLParamData(SQLHSTMT StatementHandle, _Out_opt_ SQLPOINTER *Value) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(StatementHandle); } SQLRETURN SQL_API SQLPutData(SQLHSTMT StatementHandle, _In_reads_(_Inexpressible_(StrLen_or_Ind)) SQLPOINTER Data, SQLLEN StrLen_or_Ind) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(StatementHandle); } -#endif /* WITH_EMPTY */ /* * @@ -699,11 +715,11 @@ SQLRETURN SQL_API SQLRowCount(_In_ SQLHSTMT StatementHandle, _Out_ SQLLEN *RowCount) { SQLRETURN ret; - TRACE2(_IN, "pp", StatementHandle, RowCount); + TRACE2(_IN, StatementHandle, "pp", StatementHandle, RowCount); HND_LOCK(StatementHandle); /* see Note_sync */ ret = EsSQLRowCount(StatementHandle, RowCount); HND_UNLOCK(StatementHandle); - TRACE3(_OUT, "dpn", ret, StatementHandle, RowCount); + TRACE3(_OUT, StatementHandle, "dpn", ret, StatementHandle, RowCount); return ret; } @@ -711,11 +727,11 @@ SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, _Out_ SQLSMALLINT *ColumnCount) { SQLRETURN ret; - TRACE2(_IN, "pp", StatementHandle, ColumnCount); + TRACE2(_IN, StatementHandle, "pp", StatementHandle, ColumnCount); HND_LOCK(StatementHandle); /* see Note_sync */ ret = EsSQLNumResultCols(StatementHandle, ColumnCount); HND_UNLOCK(StatementHandle); - TRACE3(_OUT, "dpt", ret, StatementHandle, ColumnCount); + TRACE3(_OUT, StatementHandle, "dpt", ret, StatementHandle, ColumnCount); return ret; } @@ -725,7 +741,7 @@ SQLRETURN SQL_API SQLDescribeColW SQLHSTMT hstmt, SQLUSMALLINT icol, _Out_writes_opt_(cchColNameMax) - SQLWCHAR *szColName, + SQLWCHAR *szColName, SQLSMALLINT cchColNameMax, _Out_opt_ SQLSMALLINT *pcchColName, @@ -740,14 +756,16 @@ SQLRETURN SQL_API SQLDescribeColW ) { SQLRETURN ret; - TRACE9(_IN, "pupdppppp", hstmt, icol, szColName, cchColNameMax, - pcchColName, pfSqlType, pcbColDef, pibScale, pfNullable); + TRACE9(_IN, hstmt, "pHphppppp", hstmt, icol, szColName, + cchColNameMax, pcchColName, pfSqlType, pcbColDef, pibScale, + pfNullable); HND_LOCK(hstmt); ret = EsSQLDescribeColW(hstmt, icol, szColName, cchColNameMax, pcchColName, pfSqlType, pcbColDef, pibScale, pfNullable); HND_UNLOCK(hstmt); - TRACE10(_OUT, "dpuWdttNtt", ret, hstmt, icol, szColName, cchColNameMax, - pcchColName, pfSqlType, pcbColDef, pibScale, pfNullable); + TRACE10(_OUT, hstmt, "dpHphttNtt", ret, hstmt, icol, szColName, + cchColNameMax, pcchColName, pfSqlType, pcbColDef, pibScale, + pfNullable); return ret; } @@ -770,38 +788,42 @@ SQLRETURN SQL_API SQLColAttributeW ) { SQLRETURN ret; - TRACE7(_IN, "pddpdtp", hstmt, iCol, iField, pCharAttr, cbDescMax, - pcbCharAttr, pNumAttr); + TRACE7(_IN, hstmt, "pHHphpp", hstmt, iCol, iField, pCharAttr, + cbDescMax, pcbCharAttr, pNumAttr); HND_LOCK(hstmt); - ret = EsSQLColAttributeW(hstmt, iCol, iField, pCharAttr, cbDescMax, - pcbCharAttr, pNumAttr); + ret = EsSQLColAttributeW(hstmt, iCol, iField, pCharAttr, + cbDescMax, pcbCharAttr, pNumAttr); HND_UNLOCK(hstmt); #ifdef _WIN64 - TRACE8(_OUT, "dpddpdtn", ret, hstmt, iCol, iField, pCharAttr, cbDescMax, - pcbCharAttr, pNumAttr); + TRACE8(_OUT, hstmt, "dpHHphtn", ret, hstmt, iCol, iField, pCharAttr, + cbDescMax, pcbCharAttr, pNumAttr); #else /* _WIN64 */ - TRACE8(_OUT, "dpddpdtp", ret, hstmt, iCol, iField, pCharAttr, cbDescMax, - pcbCharAttr, pNumAttr); + TRACE8(_OUT, hstmt, "dpddpdtg", ret, hstmt, iCol, iField, pCharAttr, + cbDescMax, pcbCharAttr, pNumAttr); #endif /* _WIN64 */ return ret; } -SQLRETURN SQL_API SQLBindCol(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, +SQLRETURN SQL_API SQLBindCol( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLSMALLINT TargetType, _Inout_updates_opt_(_Inexpressible_(BufferLength)) SQLPOINTER TargetValue, - SQLLEN BufferLength, _Inout_opt_ SQLLEN *StrLen_or_Ind) + SQLLEN BufferLength, + _Inout_opt_ + SQLLEN *StrLen_or_Ind) { SQLRETURN ret; - TRACE6(_IN, "pddpdp", StatementHandle, ColumnNumber, TargetType, - TargetValue, BufferLength, StrLen_or_Ind); + TRACE6(_IN, StatementHandle, "pHhpzp", StatementHandle, + ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); HND_LOCK(StatementHandle); - ret = EsSQLBindCol(StatementHandle, ColumnNumber, TargetType, - TargetValue, BufferLength, StrLen_or_Ind); + ret = EsSQLBindCol(StatementHandle, + ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); HND_UNLOCK(StatementHandle); - TRACE7(_OUT, "dpddpdn", ret, StatementHandle, ColumnNumber, TargetType, - TargetValue, BufferLength, StrLen_or_Ind); + TRACE7(_OUT, StatementHandle, "dpHhpzn", ret, StatementHandle, + ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); return ret; } @@ -809,15 +831,14 @@ SQLRETURN SQL_API SQLBindCol(SQLHSTMT StatementHandle, SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { SQLRETURN ret; - TRACE1(_IN, "p", StatementHandle); + TRACE1(_IN, StatementHandle, "p", StatementHandle); HND_LOCK(StatementHandle); ret = EsSQLFetch(StatementHandle); HND_UNLOCK(StatementHandle); - TRACE2(_OUT, "dp", ret, StatementHandle); + TRACE2(_OUT, StatementHandle, "dp", ret, StatementHandle); return ret; } -#if WITH_EMPTY /* * "SQLFetch and SQLFetchScroll use the rowset size at the time of the call to * determine how many rows to fetch. However, SQLFetchScroll with a @@ -849,40 +870,49 @@ SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) SQLRETURN SQL_API SQLFetchScroll(SQLHSTMT StatementHandle, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(StatementHandle); } -#endif /* WITH_EMPTY */ -SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, - SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, - _Out_writes_opt_(_Inexpressible_(BufferLength)) SQLPOINTER TargetValuePtr, +SQLRETURN SQL_API SQLGetData( + SQLHSTMT StatementHandle, + SQLUSMALLINT ColumnNumber, + SQLSMALLINT TargetType, + _Out_writes_opt_(_Inexpressible_(BufferLength)) + SQLPOINTER TargetValuePtr, SQLLEN BufferLength, - _Out_opt_ SQLLEN *StrLen_or_IndPtr) + _Out_opt_ + SQLLEN *StrLen_or_IndPtr) { SQLRETURN ret; - TRACE6(_IN, "pHhplp", StatementHandle, ColumnNumber, TargetType, - TargetValuePtr, BufferLength, StrLen_or_IndPtr); + TRACE6(_IN, StatementHandle, "pHhplp", StatementHandle, + ColumnNumber, TargetType, TargetValuePtr, BufferLength, + StrLen_or_IndPtr); HND_LOCK(StatementHandle); - ret = EsSQLGetData(StatementHandle, ColumnNumber, TargetType, - TargetValuePtr, BufferLength, StrLen_or_IndPtr); + ret = EsSQLGetData(StatementHandle, + ColumnNumber, TargetType, TargetValuePtr, BufferLength, + StrLen_or_IndPtr); HND_UNLOCK(StatementHandle); - TRACE7(_OUT, "dpHhpln", ret, StatementHandle, ColumnNumber, TargetType, - TargetValuePtr, BufferLength, StrLen_or_IndPtr); + TRACE7(_OUT, StatementHandle, "dpHhpln", ret, StatementHandle, + ColumnNumber, TargetType, TargetValuePtr, BufferLength, + StrLen_or_IndPtr); return ret; } SQLRETURN SQL_API SQLSetPos( SQLHSTMT StatementHandle, - SQLSETPOSIROW RowNumber, + SQLSETPOSIROW RowNumber, /* SQLULEN / SQLUSMALLINT */ SQLUSMALLINT Operation, SQLUSMALLINT LockType) { SQLRETURN ret; - TRACE4(_IN, "puuu", StatementHandle, RowNumber, Operation, LockType); + TRACE4(_IN, StatementHandle, "pZHH", StatementHandle, RowNumber, + Operation, LockType); HND_LOCK(StatementHandle); - ret = EsSQLSetPos(StatementHandle, RowNumber, Operation, LockType); + ret = EsSQLSetPos(StatementHandle, RowNumber, + Operation, LockType); HND_UNLOCK(StatementHandle); - TRACE5(_OUT, "dpuuu", ret,StatementHandle, RowNumber, Operation, LockType); + TRACE5(_OUT, StatementHandle, "dpZHH", ret,StatementHandle, RowNumber, + Operation, LockType); return ret; } @@ -891,44 +921,45 @@ SQLRETURN SQL_API SQLBulkOperations( SQLSMALLINT Operation) { SQLRETURN ret; - TRACE2(_IN, "pd", StatementHandle, Operation); + TRACE2(_IN, StatementHandle, "ph", StatementHandle, Operation); HND_LOCK(StatementHandle); ret = EsSQLBulkOperations(StatementHandle, Operation); HND_UNLOCK(StatementHandle); - TRACE3(_OUT, "dpd", ret, StatementHandle, Operation); + TRACE3(_OUT, StatementHandle, "dph", ret, StatementHandle, Operation); return ret; } SQLRETURN SQL_API SQLMoreResults(SQLHSTMT StatementHandle) { SQLRETURN ret; - TRACE1(_IN, "p", StatementHandle); + TRACE1(_IN, StatementHandle, "p", StatementHandle); /* no synchronization required */ ret = EsSQLMoreResults(StatementHandle); - TRACE2(_OUT, "dp", ret, StatementHandle); + TRACE2(_OUT, StatementHandle, "dp", ret, StatementHandle); return ret; } -/* TODO: see error.h: esodbc_errors definition note (2.x apps support) */ SQLRETURN SQL_API SQLGetDiagFieldW( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, - _Out_writes_opt_(_Inexpressible_(BufferLength)) SQLPOINTER DiagInfoPtr, + _Out_writes_opt_(_Inexpressible_(BufferLength)) + SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, - _Out_opt_ SQLSMALLINT *StringLengthPtr) + _Out_opt_ + SQLSMALLINT *StringLengthPtr) { SQLRETURN ret; - TRACE7(_IN, "dpddpdp", HandleType, Handle, RecNumber, DiagIdentifier, - DiagInfoPtr, BufferLength, StringLengthPtr); + TRACE7(_IN, Handle, "hphhphp", HandleType, Handle, RecNumber, + DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr); /* Note_diag: locking here only really makes sense for stmt and dbc, but * uniformly locking env and desc too is harmless and simple */ HND_LOCK(Handle); - ret = EsSQLGetDiagFieldW(HandleType, Handle, RecNumber, DiagIdentifier, - DiagInfoPtr, BufferLength, StringLengthPtr); + ret = EsSQLGetDiagFieldW(HandleType, Handle, RecNumber, + DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr); HND_UNLOCK(Handle); - TRACE8(_OUT, "ddpddpdt", ret, HandleType, Handle, RecNumber, + TRACE8(_OUT, Handle, "dhphhpht", ret, HandleType, Handle, RecNumber, DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr); return ret; } @@ -938,26 +969,28 @@ SQLRETURN SQL_API SQLGetDiagRecW SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, - _Out_writes_opt_(6) SQLWCHAR *Sqlstate, + _Out_writes_opt_(6) + SQLWCHAR *Sqlstate, SQLINTEGER *NativeError, - _Out_writes_opt_(BufferLength) SQLWCHAR *MessageText, + _Out_writes_opt_(BufferLength) + SQLWCHAR *MessageText, SQLSMALLINT BufferLength, - _Out_opt_ SQLSMALLINT *TextLength + _Out_opt_ + SQLSMALLINT *TextLength ) { SQLRETURN ret; - TRACE8(_IN, "dpdpppdp", HandleType, Handle, RecNumber, Sqlstate, - NativeError, MessageText, BufferLength, TextLength); + TRACE8(_IN, Handle, "hphppphp", HandleType, Handle, RecNumber, + Sqlstate, NativeError, MessageText, BufferLength, TextLength); HND_LOCK(Handle); /* see Note_diag */ - ret = EsSQLGetDiagRecW(HandleType, Handle, RecNumber, Sqlstate, - NativeError, MessageText, BufferLength, TextLength); + ret = EsSQLGetDiagRecW(HandleType, Handle, RecNumber, + Sqlstate, NativeError, MessageText, BufferLength, TextLength); HND_UNLOCK(Handle); - TRACE9(_OUT, "ddpdWgWdt", ret, HandleType, Handle, RecNumber, Sqlstate, - NativeError, MessageText, BufferLength, TextLength); + TRACE9(_OUT, Handle, "dhphwgwht", ret, HandleType, Handle, RecNumber, + Sqlstate, NativeError, MessageText, BufferLength, TextLength); return ret; } -#if WITH_EMPTY /* * * Obtaining information about the data source's system tables @@ -977,57 +1010,66 @@ SQLRETURN SQL_API SQLColumnPrivilegesW( SQLSMALLINT cchColumnName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif /* WITH_EMPTY */ SQLRETURN SQL_API SQLColumnsW ( - SQLHSTMT hstmt, - _In_reads_opt_(cchCatalogName) SQLWCHAR *szCatalogName, - SQLSMALLINT cchCatalogName, - _In_reads_opt_(cchSchemaName) SQLWCHAR *szSchemaName, - SQLSMALLINT cchSchemaName, - _In_reads_opt_(cchTableName) SQLWCHAR *szTableName, - SQLSMALLINT cchTableName, - _In_reads_opt_(cchColumnName) SQLWCHAR *szColumnName, - SQLSMALLINT cchColumnName + SQLHSTMT hstmt, + _In_reads_opt_(cchCatalogName) + SQLWCHAR *szCatalogName, + SQLSMALLINT cchCatalogName, + _In_reads_opt_(cchSchemaName) + SQLWCHAR *szSchemaName, + SQLSMALLINT cchSchemaName, + _In_reads_opt_(cchTableName) + SQLWCHAR *szTableName, + SQLSMALLINT cchTableName, + _In_reads_opt_(cchColumnName) + SQLWCHAR *szColumnName, + SQLSMALLINT cchColumnName ) { SQLRETURN ret; - TRACE9(_IN, "ppdpdpdpd", hstmt, szCatalogName, cchCatalogName, - szSchemaName, cchSchemaName, szTableName, cchTableName, - szColumnName, cchColumnName); + TRACE9(_IN, hstmt, "pWhWhWhWh", hstmt, szCatalogName, + cchCatalogName, szSchemaName, cchSchemaName, szTableName, + cchTableName, szColumnName, cchColumnName); HND_LOCK(hstmt); - ret = EsSQLColumnsW(hstmt, szCatalogName, cchCatalogName, - szSchemaName, cchSchemaName, szTableName, cchTableName, - szColumnName, cchColumnName); + ret = EsSQLColumnsW(hstmt, szCatalogName, + cchCatalogName, szSchemaName, cchSchemaName, szTableName, + cchTableName, szColumnName, cchColumnName); HND_UNLOCK(hstmt); - TRACE10(_OUT, "dpWdWdWdWd", ret, hstmt, szCatalogName, cchCatalogName, - szSchemaName, cchSchemaName, szTableName, cchTableName, - szColumnName, cchColumnName); + TRACE10(_OUT, hstmt, "dpWhWhWhWh", ret, hstmt, szCatalogName, + cchCatalogName, szSchemaName, cchSchemaName, szTableName, + cchTableName, szColumnName, cchColumnName); return ret; } SQLRETURN SQL_API SQLForeignKeysW ( SQLHSTMT hstmt, - _In_reads_opt_(cchPkCatalogName) SQLWCHAR *szPkCatalogName, + _In_reads_opt_(cchPkCatalogName) + SQLWCHAR *szPkCatalogName, SQLSMALLINT cchPkCatalogName, - _In_reads_opt_(cchPkSchemaName) SQLWCHAR *szPkSchemaName, + _In_reads_opt_(cchPkSchemaName) + SQLWCHAR *szPkSchemaName, SQLSMALLINT cchPkSchemaName, - _In_reads_opt_(cchPkTableName) SQLWCHAR *szPkTableName, + _In_reads_opt_(cchPkTableName) + SQLWCHAR *szPkTableName, SQLSMALLINT cchPkTableName, - _In_reads_opt_(cchFkCatalogName) SQLWCHAR *szFkCatalogName, + _In_reads_opt_(cchFkCatalogName) + SQLWCHAR *szFkCatalogName, SQLSMALLINT cchFkCatalogName, - _In_reads_opt_(cchFkSchemaName) SQLWCHAR *szFkSchemaName, + _In_reads_opt_(cchFkSchemaName) + SQLWCHAR *szFkSchemaName, SQLSMALLINT cchFkSchemaName, - _In_reads_opt_(cchFkTableName) SQLWCHAR *szFkTableName, + _In_reads_opt_(cchFkTableName) + SQLWCHAR *szFkTableName, SQLSMALLINT cchFkTableName ) { SQLRETURN ret; - TRACE13(_IN, "ppdpdpdpdpdpd", hstmt, + TRACE13(_IN, hstmt, "pWhWhWhWhWhWh", hstmt, szPkCatalogName, cchPkCatalogName, szPkSchemaName, cchPkSchemaName, szPkTableName, cchPkTableName, @@ -1043,7 +1085,7 @@ SQLRETURN SQL_API SQLForeignKeysW szFkSchemaName, cchFkSchemaName, szFkTableName, cchFkTableName); HND_UNLOCK(hstmt); - TRACE14(_OUT, "dpWdWdWdWdWdWd", ret, hstmt, + TRACE14(_OUT, hstmt, "dpWhWhWhWhWhWh", ret, hstmt, szPkCatalogName, cchPkCatalogName, szPkSchemaName, cchPkSchemaName, szPkTableName, cchPkTableName, @@ -1057,16 +1099,19 @@ SQLRETURN SQL_API SQLForeignKeysW SQLRETURN SQL_API SQLPrimaryKeysW ( SQLHSTMT hstmt, - _In_reads_opt_(cchCatalogName) SQLWCHAR *szCatalogName, + _In_reads_opt_(cchCatalogName) + SQLWCHAR *szCatalogName, SQLSMALLINT cchCatalogName, - _In_reads_opt_(cchSchemaName) SQLWCHAR *szSchemaName, + _In_reads_opt_(cchSchemaName) + SQLWCHAR *szSchemaName, SQLSMALLINT cchSchemaName, - _In_reads_opt_(cchTableName) SQLWCHAR *szTableName, + _In_reads_opt_(cchTableName) + SQLWCHAR *szTableName, SQLSMALLINT cchTableName ) { SQLRETURN ret; - TRACE7(_IN, "ppdpdpd", hstmt, + TRACE7(_IN, hstmt, "pWhWhWh", hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName); @@ -1076,14 +1121,13 @@ SQLRETURN SQL_API SQLPrimaryKeysW szSchemaName, cchSchemaName, szTableName, cchTableName); HND_UNLOCK(hstmt); - TRACE8(_OUT, "dpWdWdWd", ret, hstmt, + TRACE8(_OUT, hstmt, "dpWhWhWh", ret, hstmt, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName); return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLProcedureColumnsW ( SQLHSTMT hstmt, @@ -1097,7 +1141,7 @@ SQLRETURN SQL_API SQLProcedureColumnsW SQLSMALLINT cchColumnName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLProceduresW @@ -1111,26 +1155,28 @@ SQLRETURN SQL_API SQLProceduresW SQLSMALLINT cchProcName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif // WITH_EMPTY SQLRETURN SQL_API SQLSpecialColumnsW ( SQLHSTMT hstmt, SQLUSMALLINT fColType, - _In_reads_opt_(cchCatalogName) SQLWCHAR *szCatalogName, + _In_reads_opt_(cchCatalogName) + SQLWCHAR *szCatalogName, SQLSMALLINT cchCatalogName, - _In_reads_opt_(cchSchemaName) SQLWCHAR *szSchemaName, + _In_reads_opt_(cchSchemaName) + SQLWCHAR *szSchemaName, SQLSMALLINT cchSchemaName, - _In_reads_opt_(cchTableName) SQLWCHAR *szTableName, + _In_reads_opt_(cchTableName) + SQLWCHAR *szTableName, SQLSMALLINT cchTableName, SQLUSMALLINT fScope, SQLUSMALLINT fNullable ) { SQLRETURN ret; - TRACE10(_IN, "pupdpdpduu", hstmt, fColType, szCatalogName, + TRACE10(_IN, hstmt, "pHWhWhWhHH", hstmt, fColType, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, fScope, fNullable); HND_LOCK(hstmt); @@ -1138,13 +1184,12 @@ SQLRETURN SQL_API SQLSpecialColumnsW cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, fScope, fNullable); HND_UNLOCK(hstmt); - TRACE11(_OUT, "dpuWdWdWduu", ret, hstmt, fColType, szCatalogName, + TRACE11(_OUT, hstmt, "dpHWhWhWhHH", ret, hstmt, fColType, szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, szTableName, cchTableName, fScope, fNullable); return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLStatisticsW ( SQLHSTMT hstmt, @@ -1158,7 +1203,7 @@ SQLRETURN SQL_API SQLStatisticsW SQLUSMALLINT fAccuracy ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLTablePrivilegesW @@ -1172,36 +1217,39 @@ SQLRETURN SQL_API SQLTablePrivilegesW SQLSMALLINT cchTableName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif /* WITH_EMPTY */ SQLRETURN SQL_API SQLTablesW ( SQLHSTMT hstmt, - _In_reads_opt_(cchCatalogName) SQLWCHAR *szCatalogName, + _In_reads_opt_(cchCatalogName) + SQLWCHAR *szCatalogName, SQLSMALLINT cchCatalogName, - _In_reads_opt_(cchSchemaName) SQLWCHAR *szSchemaName, + _In_reads_opt_(cchSchemaName) + SQLWCHAR *szSchemaName, SQLSMALLINT cchSchemaName, - _In_reads_opt_(cchTableName) SQLWCHAR *szTableName, + _In_reads_opt_(cchTableName) + SQLWCHAR *szTableName, SQLSMALLINT cchTableName, - _In_reads_opt_(cchTableType) SQLWCHAR *szTableType, + _In_reads_opt_(cchTableType) + SQLWCHAR *szTableType, SQLSMALLINT cchTableType ) { SQLRETURN ret; - TRACE9(_IN, "ppdpdpdpd", hstmt, szCatalogName, cchCatalogName, - szSchemaName, cchSchemaName, szTableName, cchTableName, - szTableType, cchTableType); + TRACE9(_IN, hstmt, "pWhWhWhWh", hstmt, + szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, + szTableName, cchTableName, szTableType, cchTableType); HND_LOCK(hstmt); - ret = EsSQLTablesW(hstmt, szCatalogName, cchCatalogName, - szSchemaName, cchSchemaName, szTableName, cchTableName, - szTableType, cchTableType); + ret = EsSQLTablesW(hstmt, + szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, + szTableName, cchTableName, szTableType, cchTableType); HND_UNLOCK(hstmt); - TRACE10(_OUT, "dpWdWdWdWd", ret, hstmt, szCatalogName, cchCatalogName, - szSchemaName, cchSchemaName, szTableName, cchTableName, - szTableType, cchTableType); + TRACE10(_OUT, hstmt, "dpWhWhWhWh", ret, hstmt, + szCatalogName, cchCatalogName, szSchemaName, cchSchemaName, + szTableName, cchTableName, szTableType, cchTableType); return ret; } @@ -1215,33 +1263,33 @@ SQLRETURN SQL_API SQLTablesW SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option) { SQLRETURN ret; - TRACE2(_IN, "pd", StatementHandle, Option); + TRACE2(_IN, StatementHandle, "pH", StatementHandle, Option); HND_LOCK(StatementHandle); ret = EsSQLFreeStmt(StatementHandle, Option); HND_UNLOCK(StatementHandle); - TRACE3(_OUT, "dpd", ret, StatementHandle, Option); + TRACE3(_OUT, StatementHandle, "dpH", ret, StatementHandle, Option); return ret; } SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT StatementHandle) { SQLRETURN ret; - TRACE1(_IN, "p", StatementHandle); + TRACE1(_IN, StatementHandle, "p", StatementHandle); HND_LOCK(StatementHandle); ret = EsSQLCloseCursor(StatementHandle); HND_UNLOCK(StatementHandle); - TRACE2(_OUT, "dp", ret, StatementHandle); + TRACE2(_OUT, StatementHandle, "dp", ret, StatementHandle); return ret; } SQLRETURN SQL_API SQLCancel(SQLHSTMT StatementHandle) { SQLRETURN ret; - TRACE1(_IN, "p", StatementHandle); + TRACE1(_IN, StatementHandle, "p", StatementHandle); HND_LOCK(StatementHandle); ret = EsSQLCancel(StatementHandle); HND_UNLOCK(StatementHandle); - TRACE2(_OUT, "dp", ret, StatementHandle); + TRACE2(_OUT, StatementHandle, "dp", ret, StatementHandle); return ret; } @@ -1249,21 +1297,19 @@ SQLRETURN SQL_API SQLCancelHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle) { SQLRETURN ret; - TRACE2(_IN, "hp", HandleType, InputHandle); + TRACE2(_IN, InputHandle, "hp", HandleType, InputHandle); HND_LOCK(InputHandle); ret = EsSQLCancelHandle(HandleType, InputHandle); HND_UNLOCK(InputHandle); - TRACE3(_IN, "dhp", ret, HandleType, InputHandle); + TRACE3(_IN, InputHandle, "dhp", ret, HandleType, InputHandle); return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(Handle); } -#endif /* WITH_EMPTY */ /* @@ -1274,24 +1320,24 @@ SQLRETURN SQL_API SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) { SQLRETURN ret; - TRACE1(_IN, "p", ConnectionHandle); + TRACE1(_IN, ConnectionHandle, "p", ConnectionHandle); HND_LOCK(ConnectionHandle); ret = EsSQLDisconnect(ConnectionHandle); HND_UNLOCK(ConnectionHandle); - TRACE2(_OUT, "dp", ret, ConnectionHandle); + TRACE2(_OUT, ConnectionHandle, "dp", ret, ConnectionHandle); return ret; } SQLRETURN SQL_API SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { SQLRETURN ret; - TRACE2(_IN, "dp", HandleType, Handle); + TRACE2(_IN, Handle, "hp", HandleType, Handle); if (! HND_TRYLOCK(Handle)) { BUGH(Handle, "handle still locked while freeing attempt."); return SQL_ERROR; } ret = EsSQLFreeHandle(HandleType, Handle); - TRACE3(_OUT, "ddp", ret, HandleType, Handle); + TRACE3(_OUT, NULL/*Handle's freed*/, "dhp", ret, HandleType, Handle); return ret; } diff --git a/driver/queries.c b/driver/queries.c index d2fc84ee..eb36b5d1 100644 --- a/driver/queries.c +++ b/driver/queries.c @@ -20,6 +20,7 @@ #define JSON_ANSWER_ERROR "error" #define JSON_ANSWER_ERR_TYPE "type" #define JSON_ANSWER_ERR_REASON "reason" +#define JSON_ANSWER_ERR_RCAUSE "root_cause" #define JSON_ANSWER_COL_NAME "name" #define JSON_ANSWER_COL_TYPE "type" @@ -162,7 +163,8 @@ static SQLRETURN attach_columns(esodbc_stmt_st *stmt, UJObject columns) set_col_size(rec); - /* TODO: set remaining of settable fields (base table etc.) */ + /* setting the remaining of settable fields (base table etc.) requires + * server side changes => set them to "" */ /* "If a base column name does not exist (as in the case of columns * that are expressions), then this variable contains an empty @@ -314,21 +316,34 @@ SQLRETURN TEST_API attach_answer(esodbc_stmt_st *stmt, char *buff, size_t blen) static BOOL attach_sql_error(SQLHANDLE hnd, cstr_st *body) { BOOL ret; - UJObject obj, o_status, o_error, o_type, o_reason; - const wchar_t *wtype, *wreason; - size_t tlen, rlen, left; + UJObject obj, o_status, o_error, o_type, o_reason, o_rcause; + wstr_st type, reason; wchar_t wbuf[SQL_MAX_MESSAGE_LENGTH]; - size_t wbuflen; - int n; - void *state; + int cnt; + void *state, *iter; + /* following grouped JSON unpacking items must remain in sync */ + /* {"error": {..}, "status":200} */ const wchar_t *outer_keys[] = { MK_WPTR(JSON_ANSWER_ERROR), MK_WPTR(JSON_ANSWER_STATUS) }; + const char fmt_outer_keys[] = "ON"; + int cnt_outer_keys = sizeof(fmt_outer_keys) - /*\0*/1; + /* "error": {"root_cause":[?], "type":"..", "reason":".." ...} */ const wchar_t *err_keys[] = { + MK_WPTR(JSON_ANSWER_ERR_RCAUSE), MK_WPTR(JSON_ANSWER_ERR_TYPE), - MK_WPTR(JSON_ANSWER_ERR_REASON) + MK_WPTR(JSON_ANSWER_ERR_REASON), }; + const char fmt_err_keys[] = "aSS"; + int cnt_err_keys = sizeof(fmt_err_keys) - /*\0*/1; + /* "root_cause":[{"type":"..", "reason":".."} ..] */ + const wchar_t *r_err_keys[] = { + MK_WPTR(JSON_ANSWER_ERR_TYPE), + MK_WPTR(JSON_ANSWER_ERR_REASON), + }; + const char fmt_r_err_keys[] = "SS"; + int cnt_r_err_keys = sizeof(fmt_r_err_keys) - /*\0*/1; ret = FALSE; state = NULL; @@ -336,50 +351,76 @@ static BOOL attach_sql_error(SQLHANDLE hnd, cstr_st *body) /* parse the entire JSON answer */ obj = UJDecode(body->str, body->cnt, NULL, &state); if (! obj) { - INFOH(hnd, "answer not JSON (%s).", + ERRH(hnd, "answer not JSON (%s).", state ? UJGetError(state) : ""); goto end; } /* extract the status and error object */ - if (UJObjectUnpack(obj, 2, "ON", outer_keys, &o_error, &o_status) < 2) { - INFOH(hnd, "JSON answer not a SQL error (%s).", UJGetError(state)); + if (UJObjectUnpack(obj, cnt_outer_keys, fmt_outer_keys, outer_keys, + &o_error, &o_status) < cnt_outer_keys) { + ERRH(hnd, "JSON answer not a SQL error (%s).", UJGetError(state)); goto end; } /* unpack error object */ - if (UJObjectUnpack(o_error, 2, "SS", err_keys, &o_type, &o_reason) < 2) { - INFOH(hnd, "failed to unpack error object (%s).", UJGetError(state)); + if (UJObjectUnpack(o_error, cnt_err_keys, fmt_err_keys, err_keys, + &o_rcause, &o_type, &o_reason) < cnt_err_keys) { + ERRH(hnd, "failed to unpack error object (%s).", UJGetError(state)); goto end; } - wtype = UJReadString(o_type, &tlen); - wreason = UJReadString(o_reason, &rlen); - /* these return empty string in case of mismatch */ - assert(wtype && wreason); - DBGH(hnd, "server failures: type: [%zd] `" LWPDL "`, reason: [%zd] `" - LWPDL "`, status: %d.", tlen, tlen, wtype, rlen, rlen, wreason, - UJNumericInt(o_status)); - - /* swprintf will fail if formated string would overrun the buffer size (as - * opposed to write up to its limit) => find out the limit first.*/ - n = swprintf(NULL, 0, MK_WPTR("%.*s: %.*s"), (int)tlen, wtype, (int)rlen, - wreason); - if (0 < n) { - wbuflen = sizeof(wbuf)/sizeof(*wbuf); - wbuflen -= /* ": " */2 + /*\0*/1; - tlen = wbuflen < tlen ? wbuflen : tlen; - left = wbuflen - tlen; - rlen = left < rlen ? left : rlen; - wbuflen += /* ": " */2 + /*\0*/1; - /* swprintf will add the 0-term (or fail, if it can't) */ - n = swprintf(wbuf, wbuflen, MK_WPTR("%.*s: %.*s"), (int)tlen, wtype, - (int)rlen, wreason); - } - if (n <= 0) { - wbuf[0] = L'\0'; - } - - post_diagnostic(&HDRH(hnd)->diag, SQL_STATE_HY000, wbuf, - UJNumericInt(o_status)); + /* this is safe for NULL o_rcause: => -1 */ + cnt = UJLengthArray(o_rcause); + DBGH(hnd, "root cause(s) received: %d.", cnt); + if (0 < cnt) { + /* print the root_cause, if available */ + iter = UJBeginArray(o_rcause); + /* save, UJIterArray() checks against NULL */ + assert(iter); + while (UJIterArray(&iter, &o_rcause)) { /* reuse o_rcause obj */ + /* unpack root error object */ + if (UJObjectUnpack(o_rcause, cnt_r_err_keys, fmt_r_err_keys, + r_err_keys, &o_type, &o_reason) < cnt_r_err_keys) { + ERRH(hnd, "failed to unpack root error object (%s).", + UJGetError(state)); + goto end; /* TODO: continue on error? */ + } else { + /* stop at first element. TODO: is ever [array] > 1? */ + break; + } + } + } + /* else: root_cause not available, print "generic" reason */ + type.str = (SQLWCHAR *)UJReadString(o_type, &type.cnt); + reason.str = (SQLWCHAR *)UJReadString(o_reason, &reason.cnt); + + /* should be empty string in case of mismatch */ + assert(type.str && reason.str); + DBGH(hnd, "reported failure: type: [%zd] `" LWPDL "`, reason: [%zd] `" + LWPDL "`, status: %d.", type.cnt, LWSTR(&type), + reason.cnt, LWSTR(&reason), UJNumericInt(o_status)); + + /* swprintf will always append the 0-term ("A null character is appended + * after the last character written."), but fail if formated string would + * overrun the buffer size (in an equivocal way: overrun encoding + * error). */ + errno = 0; + cnt = swprintf(wbuf, sizeof(wbuf)/sizeof(*wbuf), + WPFWP_LDESC L": " WPFWP_LDESC, LWSTR(&type), LWSTR(&reason)); + assert(cnt); + if (cnt < 0) { + if (errno) { + ERRH(hnd, "printing the received error message failed."); + goto end; + } + /* partial error message printed */ + WARNH(hnd, "current error buffer to small (%zu) for full error " + "detail.", sizeof(wbuf)/sizeof(*wbuf)); + cnt = sizeof(wbuf)/sizeof(*wbuf) - 1; + } + assert(wbuf[cnt] == L'\0'); + ERRH(hnd, "request failure reason: [%d] `" LWPD "`.", cnt, wbuf); + + post_diagnostic(hnd, SQL_STATE_HY000, wbuf, UJNumericInt(o_status)); ret = TRUE; end: @@ -409,13 +450,13 @@ SQLRETURN TEST_API attach_error(SQLHANDLE hnd, cstr_st *body, int code) memcpy(buff, body->str, to_copy); buff[to_copy] = '\0'; - post_c_diagnostic(&HDRH(hnd)->diag, SQL_STATE_08S01, buff, code); + post_c_diagnostic(hnd, SQL_STATE_08S01, buff, code); } RET_STATE(HDRH(hnd)->diag.state); } - return post_diagnostic(&HDRH(hnd)->diag, SQL_STATE_08S01, NULL, code); + return post_diagnostic(hnd, SQL_STATE_08S01, NULL, code); } /* @@ -624,7 +665,7 @@ SQLRETURN copy_one_row(esodbc_stmt_st *stmt, SQLULEN pos) do { \ if (ard->array_status_ptr) \ ard->array_status_ptr[pos] = SQL_ROW_ERROR; \ - return post_row_diagnostic(&stmt->hdr.diag, _state, MK_WPTR(_message),\ + return post_row_diagnostic(stmt, _state, MK_WPTR(_message), \ 0, rowno, _colno); \ } while (0) #define SET_ROW_DIAG(_rowno, _colno) \ @@ -1035,7 +1076,7 @@ SQLRETURN EsSQLGetData( } } else { if (0 <= stmt->gd_col) { - INFOH(stmt, "previous source column #%hu (pos @ %lld), SQL C %hd " + DBGH(stmt, "previous source column #%hu (pos @ %lld), SQL C %hd " "abandoned for new #%hu, SQL C %hd.", stmt->gd_col, stmt->gd_offt, stmt->gd_ctype, ColumnNumber, TargetType); /* reset fields now, should the call eventually fail */ @@ -1053,7 +1094,7 @@ SQLRETURN EsSQLGetData( } /* data is available */ - /* save stmt current ARD before overwriting it */ + /* save stmt's current ARD before overwriting it */ ard = getdata_set_ard(stmt, &gd_ard, ColumnNumber, arecs, sizeof(arecs)/sizeof(arecs[0])); if (! ard) { @@ -1164,7 +1205,7 @@ SQLRETURN EsSQLBulkOperations( SQLRETURN EsSQLMoreResults(SQLHSTMT hstmt) { - INFOH(hstmt, "multiple result sets not supported."); // TODO? + INFOH(hstmt, "multiple result sets not supported."); return SQL_NO_DATA; } @@ -1175,6 +1216,7 @@ SQLRETURN EsSQLCloseCursor(SQLHSTMT StatementHandle) ERRH(stmt, "no open cursor for statement"); RET_HDIAGS(stmt, SQL_STATE_24000); } + /* TODO: POST /_xpack/sql/close {"cursor":""} if cursor */ return EsSQLFreeStmt(StatementHandle, SQL_CLOSE); } @@ -1976,6 +2018,47 @@ SQLRETURN EsSQLExecDirectW return ret; } +/* simple loop back, the driver does no actual translation */ +SQLRETURN EsSQLNativeSqlW( + SQLHDBC hdbc, + _In_reads_(cchSqlStrIn) SQLWCHAR *szSqlStrIn, + SQLINTEGER cchSqlStrIn, + _Out_writes_opt_(cchSqlStrMax) SQLWCHAR *szSqlStr, + SQLINTEGER cchSqlStrMax, + SQLINTEGER *pcchSqlStr) +{ + SQLINTEGER to_copy; + int state = SQL_STATE_00000; + + if (! szSqlStrIn) { + RET_HDIAGS(DBCH(hdbc), SQL_STATE_HY009); + } + + if (cchSqlStrIn == SQL_NTSL) { + cchSqlStrIn = (SQLINTEGER)wcslen(szSqlStrIn); + } + if (pcchSqlStr) { + *pcchSqlStr = cchSqlStrIn; + } + + if (szSqlStr) { + if (cchSqlStrMax <= cchSqlStrIn) { + to_copy = cchSqlStrMax - /* \0 */1; + state = SQL_STATE_01004; + } else { + to_copy = cchSqlStrIn; + } + memcpy(szSqlStr, szSqlStrIn, to_copy * sizeof(*szSqlStr)); + szSqlStr[to_copy] = L'\0'; + } + + if (state == SQL_STATE_01004) { + RET_HDIAGS(DBCH(hdbc), SQL_STATE_01004); + } else { + return SQL_SUCCESS; + } +} + static inline SQLULEN get_col_size(esodbc_rec_st *rec) { assert(DESC_TYPE_IS_IMPLEMENTATION(rec->desc->type)); @@ -2269,9 +2352,9 @@ SQLRETURN EsSQLColAttributeW( /* function implementation is correct, but it can't really be used as * intended, since the driver's "preparation" doesn't really involve sending - * it to ES or even parameter marker counting. - * TODO: marker counting? (SQLDescribeParam would need ES/SQL support, tho); - * or execute with fetch_size 0 or 1. */ + * it to ES or even parameter marker counting; the later would be doable now, + * but SQLDescribeParam would either need ES/SQL support, or execute with + * fetch_size 0 or 1. */ SQLRETURN EsSQLNumParams( SQLHSTMT StatementHandle, _Out_opt_ diff --git a/driver/queries.h b/driver/queries.h index 3953dc4d..b92ea78c 100644 --- a/driver/queries.h +++ b/driver/queries.h @@ -70,6 +70,13 @@ SQLRETURN EsSQLExecDirectW( SQLHSTMT hstmt, _In_reads_opt_(TextLength) SQLWCHAR *szSqlStr, SQLINTEGER cchSqlStr); +SQLRETURN EsSQLNativeSqlW( + SQLHDBC hdbc, + _In_reads_(cchSqlStrIn) SQLWCHAR *szSqlStrIn, + SQLINTEGER cchSqlStrIn, + _Out_writes_opt_(cchSqlStrMax) SQLWCHAR *szSqlStr, + SQLINTEGER cchSqlStrMax, + SQLINTEGER *pcchSqlStr); SQLRETURN EsSQLDescribeColW( SQLHSTMT hstmt, diff --git a/driver/setup.c b/driver/setup.c index ef6623f9..e65d3b18 100644 --- a/driver/setup.c +++ b/driver/setup.c @@ -11,6 +11,7 @@ #include "log.h" #include "info.h" #include "dsn.h" +#include "connect.h" #define VAL_NAME_APILEVEL "APILevel" #define VAL_NAME_CONNECTFN "ConnectFunctions" @@ -29,7 +30,6 @@ static BOOL add_subkey_values(LPCWSTR lpszDriver) SQLSMALLINT supported; assert(ESODBC_ODBC_INTERFACE_CONFORMANCE <= 9); - memset(buff, 0, sizeof(buff)); buff[0] = MK_WPTR('0') + ESODBC_ODBC_INTERFACE_CONFORMANCE; if (! SQLWritePrivateProfileStringW(lpszDriver, MK_WPTR(VAL_NAME_APILEVEL), @@ -39,8 +39,7 @@ static BOOL add_subkey_values(LPCWSTR lpszDriver) } memset(buff, 0, sizeof(buff)); - memset(&dbc, 0, sizeof(dbc)); - dbc.hdr.type = SQL_HANDLE_DBC; + init_dbc(&dbc, NULL); ret |= EsSQLGetFunctions(&dbc, SQL_API_SQLCONNECT, &supported); buff[0] = supported ? MK_WPTR('Y') : MK_WPTR('N'); ret |= EsSQLGetFunctions(&dbc, SQL_API_SQLDRIVERCONNECT, &supported); @@ -90,8 +89,8 @@ BOOL SQL_API ConfigDriverW( { BOOL ret = FALSE; - TRACE7(_IN, "phWWpht", hwndParent, fRequest, lpszDriver, lpszArgs, - lpszMsg, cbMsgMax, pcbMsgOut); + TRACE7(_IN, NULL, "phwwphp", hwndParent, fRequest, lpszDriver, + lpszArgs, lpszMsg, cbMsgMax, pcbMsgOut); switch (fRequest) { case ODBC_INSTALL_DRIVER: @@ -112,12 +111,111 @@ BOOL SQL_API ConfigDriverW( goto end; } end: - TRACE8(_OUT, "dphWWpht", ret, hwndParent, fRequest, lpszDriver, lpszArgs, - lpszMsg, cbMsgMax, pcbMsgOut); + TRACE8(_OUT, NULL, "dphwwpht", ret, hwndParent, fRequest, lpszDriver, + lpszArgs, lpszMsg, cbMsgMax, pcbMsgOut); return ret; # undef _DSN_END_MARKER } +static int save_dsn_cb(void *arg, const wchar_t *dsn_str, + wchar_t *err_out, size_t eo_max, unsigned int flags) +{ + size_t cnt; + int res; + esodbc_dsn_attrs_st attrs; + BOOL remove_old; + esodbc_dsn_attrs_st *old_attrs = (esodbc_dsn_attrs_st *)arg; + wstr_st old_dsn = old_attrs->dsn; + + init_dsn_attrs(&attrs); + res = validate_dsn(&attrs, dsn_str, err_out, eo_max, /*connect?*/FALSE); + if (res < 0) { + return res; + } + + /* There are the following cases possible: + * - new DSN, name not yet used; + * - new DSN, name already used; + * - old DSN renamed to a name not yet used; + * - old DSN renamed to a name already used */ + + /* is it a brand new DSN name or has the DSN name changed? */ + DBG("old DSN name: `" LWPDL "`, new DSN name: `" LWPDL "`.", + LWSTR(&old_dsn), LWSTR(&attrs.dsn)); + if (! EQ_CASE_WSTR(&old_dsn, &attrs.dsn)) { /* new DSN or name changed */ + /* check if DSN name already exists */ + res = system_dsn_exists(&attrs.dsn); + if (res < 0) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to check if DSN `" LWPDL "` already exists: " + LWPDL ".", LWSTR(&attrs.dsn), cnt, err_out); + return ESODBC_DSN_GENERIC_ERROR; + } else if (res) { /* name already in use */ + DBG("overwrite confirmed? %s!", flags & ESODBC_DSN_OVERWRITE_FLAG + ? "yes" : "no"); + if (! (flags & ESODBC_DSN_OVERWRITE_FLAG)) { + return ESODBC_DSN_EXISTS_ERROR; + } else { + /* need to delete old entry now to make sure no attribute set + * in old one persists in new one */ + if (! SQLRemoveDSNFromIniW(attrs.dsn.str)) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to remove old DSN with same name` " LWPDL "`:" + " " LWPDL ".", LWSTR(&old_dsn), cnt, err_out); + } else { + DBG("removed DSN to be overwritten."); + } + } + } else { /* name not yet used */ + /* new DSN to be added: check name validity */ + if (! SQLValidDSNW(attrs.dsn.str)) { + SQLPostInstallerError(ODBC_ERROR_INVALID_DSN, NULL); + ERR("invalid DSN value `" LWPDL "`.", LWSTR(&attrs.dsn)); + return ESODBC_DSN_NAME_INVALID_ERROR; + } else { + INFO("creating new DSN `" LWPDL "` for driver ` " LWPDL " `.", + LWSTR(&attrs.dsn), LWSTR(&attrs.driver)); + } + } + /* create new entry for the new DSN */ + if (! SQLWriteDSNToIniW(attrs.dsn.str, attrs.driver.str)) { + ERR("failed to add DSN `" LWPDL "` for driver ` " LWPDL " ` to " + ".INI.", LWSTR(&attrs.dsn), LWSTR(&attrs.driver)); + cnt = copy_installer_errors(err_out, eo_max); + return ESODBC_DSN_GENERIC_ERROR; + } + + /* if an old DSN exists, it'll need to be deleted */ + remove_old = !!old_dsn.cnt; + /* a new entry is created, force writing all new values */ + old_attrs = NULL; + } else { + remove_old = FALSE; + } + + /* update/create the DSN with user values */ + if (! write_system_dsn(&attrs, old_attrs)) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to add DSN to the system: " LWPDL ".", cnt, err_out); + return ESODBC_DSN_GENERIC_ERROR; + } + + /* only remove old if new is succesfully created */ + if (remove_old) { + assert(old_dsn.cnt); + if (! SQLRemoveDSNFromIniW(old_dsn.str)) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to remove old DSN ` " LWPDL "`: " LWPDL ".", + LWSTR(&old_dsn), cnt, err_out); + } else { + DBG("removed now renamed DSN `" LWPDL "`.", LWSTR(&old_dsn)); + } + } + + return 0; +} + + BOOL SQL_API ConfigDSNW( HWND hwndParent, WORD fRequest, @@ -126,23 +224,21 @@ BOOL SQL_API ConfigDSNW( { esodbc_dsn_attrs_st attrs; wstr_st driver; - SQLWCHAR buff[ESODBC_DSN_MAX_ATTR_LEN] = {0}; - wstr_st old_dsn = {buff, 0}; - BOOL create_new; int res; - BOOL ret = FALSE; + DWORD ierror = 0; - TRACE4(_IN, "phWW", hwndParent, fRequest, lpszDriver, lpszAttributes); +#ifndef NDEBUG /* don't print the PWD */ + const char *fmt_in = "phww"; + const char *fmt_out = "dphww"; +#else /* NDEBUG */ + const char *fmt_in = "phwp"; + const char *fmt_out = "dphwp"; +#endif /* NDEBUG */ + TRACE4(_IN, NULL, fmt_in, hwndParent, fRequest, lpszDriver, + lpszAttributes); init_dsn_attrs(&attrs); - /* If there's a DSN in reveived attributes, load the config from the - * registry. Otherwise, populate a new config with defaults. */ - if (! load_system_dsn(&attrs, (SQLWCHAR *)lpszAttributes)) { - ERR("failed to load system DSN for driver ` " LWPD " ` and " - "attributes `" LWPD "`.", lpszDriver, lpszAttributes); - return FALSE;; - } /* assign the Driver name; this is not the value of the Driver key in the * registry (i.e. the path to the DLL), which is actually skipped when * loading the config. */ @@ -150,100 +246,51 @@ BOOL SQL_API ConfigDSNW( (SQLWCHAR *)lpszDriver, wcslen(lpszDriver) }; + + /* If there's a DSN in reveived attributes, load the config from the + * registry. */ + if (! load_system_dsn(&attrs, (SQLWCHAR *)lpszAttributes)) { + ERR("failed to load system DSN for driver ` " LWPD " ` and " + "attributes `" LWPDL "`.", LWSTR(&driver), lpszAttributes); + return FALSE; + } res = assign_dsn_attr(&attrs, &MK_WSTR(ESODBC_DSN_DRIVER), &driver, /*overwrite?*/FALSE); assert(0 < res); switch (fRequest) { case ODBC_CONFIG_DSN: - /* save the DSN naming, since this might be changed by the user */ - if (attrs.dsn.cnt) { - /* attrs.dsn.cnt < ESODBC_DSN_MAX_ATTR_LEN due to - * load_sys_dsn() */ - wcscpy(old_dsn.str, attrs.dsn.str); - old_dsn.cnt = attrs.dsn.cnt; - } case ODBC_ADD_DSN: - /* user-interraction loop */ - while (TRUE) { - res = prompt_user_config(hwndParent, &attrs, FALSE); - if (res < 0) { - ERR("failed getting user values."); - goto err; - } else if (! res) { - INFO("user canceled the dialog."); - goto end; - } - /* is it a brand new DSN or has the DSN name changed? */ - DBG("old DSN: `" LWPDL "`, new DSN: `" LWPDL "`.", - LWSTR(&old_dsn), LWSTR(&attrs.dsn)); - if ((! old_dsn.cnt) || - (! EQ_CASE_WSTR(&old_dsn, &attrs.dsn))) { - /* check if target DSN (new or old) already exists */ - res = system_dsn_exists(&attrs.dsn); - if (res < 0) { - ERR("failed to check if DSN `" LWPDL "` already " - "exists.", LWSTR(&attrs.dsn)); - goto err; - } else if (res) { - res = prompt_user_overwrite(hwndParent, &attrs.dsn); - if (res < 0) { - ERR("failed to get user input."); - goto err; - } else if (! res) { - /* let user chose a different DSN name */ - continue; - } - } - /* if an old DSN exists, delete it */ - if (old_dsn.cnt) { - if (! SQLRemoveDSNFromIniW(old_dsn.str)) { - ERR("failed to remove old DSN ` " LWPDL " `.", - LWSTR(&old_dsn)); - goto err; - } - DBG("removed now renamed DSN `" LWPDL "`.", - LWSTR(&old_dsn)); - } - create_new = TRUE; - } else { - create_new = FALSE; - } - break; - } - /* create or update the DSN */ - if (! write_system_dsn(&attrs, create_new)) { - ERR("failed to add DSN to the system."); - } else { - ret = TRUE; + res = prompt_user_config(hwndParent, FALSE, &attrs, save_dsn_cb); + if (res < 0) { + ierror = ODBC_ERROR_REQUEST_FAILED; + ERR("failed getting user values."); } break; case ODBC_REMOVE_DSN: if (! SQLRemoveDSNFromIniW(attrs.dsn.str)) { + ierror = ODBC_ERROR_REQUEST_FAILED; ERR("failed to remove driver ` " LWPD " ` with " - "attributes `" LWPD "`.", lpszDriver, lpszAttributes); + "attributes `" LWPDL "`.", LWSTR(&driver), lpszAttributes); } else { INFO("removed DSN `" LWPDL "` from the system.", LWSTR(&attrs.dsn)); - ret = TRUE; } break; default: ERR("unexpected configuration request type: %hd.", fRequest); - SQLPostInstallerError(ODBC_ERROR_INVALID_REQUEST_TYPE, NULL); - goto end; + ierror = ODBC_ERROR_INVALID_REQUEST_TYPE; } -err: - if (! ret) { + if (ierror) { SQLPostInstallerError(ODBC_ERROR_REQUEST_FAILED, NULL); } -end: - TRACE5(_OUT, "dphWW", ret, hwndParent, fRequest, lpszDriver, + + TRACE5(_OUT, NULL, fmt_out, ierror, hwndParent, fRequest, lpszDriver, lpszAttributes); - return ret; + return ierror == 0; } /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ diff --git a/driver/tracing.h b/driver/tracing.h index 0e3e2dac..76d3c49b 100644 --- a/driver/tracing.h +++ b/driver/tracing.h @@ -12,12 +12,35 @@ #define TRACE_LOG_LEVEL LOG_LEVEL_DBG -#define _AVAIL sizeof(_bf) - _ps +/* name of the buffer into which theh trace messages get printed */ +#define _BUFF _trace_buff +/* size of the trace buffer */ +#define TBUFF_SIZE (ESODBC_LOG_BUF_SIZE - /*final `.`*/1) +/* room left in the buffer */ +#define _AVAIL(_ps) ((int)(sizeof(_BUFF)/sizeof(_BUFF[0])) - _ps) + +/* check if buffer limit has been reached */ +#define _CHECK_WRITE(/*written*/_n, /*position*/_ps) \ + if (_n < 0) { /* printing failed */ \ + assert(_ps < _AVAIL(0)); \ + _BUFF[_ps] = '\0'; /* if previously usable, make sure it's NTS'd */\ + break; \ + } else if (_AVAIL(_ps) <= _ps + _n) { /* buffer end reached */ \ + _ps = _AVAIL(0); \ + break; \ + } else { \ + _ps += _n; \ + } /*INDENT-OFF*/ /* TODO: the SQL[U]LEN for _WIN32 */ -#define _PRINT_PARAM_VAL(type, val) \ +#define _PRINT_PARAM_VAL(type, val, prec) \ do { \ + int _prec; \ + wchar_t *_w; \ + if (_AVAIL(_ps) <= 0) { \ + break; \ + } \ switch(type) { \ /* * numeric pointers @@ -25,95 +48,113 @@ /* SQLNUMERIC/SQLDATE/SQLCHAR/etc. = unsigned char */ \ /* SQLSCHAR = char */ \ case 'c': /* char signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hhd", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hhd", \ val ? *(char *)(uintptr_t)val : 0); \ break; \ case 'C': /* char unsigned */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hhu", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hhu", \ val ? *(unsigned char *)(uintptr_t)val : 0); \ break; \ /* SQL[U]SMALLINT = [unsigned] short */ \ case 't': /* short signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hd", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hd", \ val ? *(short *)(uintptr_t)val : 0); \ break; \ case 'T': /* short unsigned */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hu", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hu", \ val ? *(unsigned short *)(uintptr_t)val : 0); \ break; \ /* SQL[U]INTEGER = [unsigned] long */ \ case 'g': /* long signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%ld", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%ld", \ val ? *(long *)(uintptr_t)val : 0); \ break; \ case 'G': /* long unsigned */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%lu", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lu", \ val ? *(unsigned long *)(uintptr_t)val : 0); \ break; \ /* SQL[U]LEN = [unsigned] long OR [u]int64_t (64b _WIN32) */ \ case 'n': /* long/int64_t signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%lld", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lld", \ val ? *(int64_t *)(uintptr_t)val : 0); \ break; \ case 'N': /* long/int64_t unsigned */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%llu", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%llu", \ val ? *(uint64_t *)(uintptr_t)val : 0); \ break; \ /* * non-numeric pointers */ \ case 'p': /* void* */ \ - _n = snprintf(_bf + _ps, _AVAIL, "@0x%p", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "@0x%p", \ (void *)(uintptr_t)val); \ break; \ - case 'W': /* wchar_t* */ \ - /* TODO: scan for unsafe usages with uninit'ed buffers */ \ - /* Note: problematic for untouched buffs: 'p' for unsure. */ \ - _n = snprintf(_bf + _ps, _AVAIL, "`" LWPD "`[%zd]", \ - val ? (wchar_t *)(uintptr_t)val : TWS_NULL, \ - val ? wcslen((wchar_t *)(uintptr_t)val) : 0); \ + /* TODO: support for (buffer*, max, written*) pattern */ \ + case 'W': /* wchar_t* with precision (always following param) */ \ + if (val) { \ + _w = (wchar_t *)(uintptr_t)val; \ + _prec = (int)(intptr_t)prec; \ + _prec = (_prec == SQL_NTS) ? (int)wcslen(_w) : _prec; \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), \ + "[%d] `" LWPDL "`", _prec, _prec, _w); \ + } else { \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), TS_NULL); \ + } \ break; \ - case 's': /* char* */ \ - /* Note: problematic for untouched buffs: 'p' for unsure. */ \ - _n = snprintf(_bf + _ps, _AVAIL, "`" LCPD "`[%zd]", \ - val ? (char *)(uintptr_t)val : TS_NULL, \ - val ? strlen((char *)(uintptr_t)val) : 0); \ + case 'w': /* NTS wchar_t* */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "[%zu] `" LWPD "`", \ + val ? wcslen((wchar_t *)(uintptr_t)val) : 0, \ + val ? (wchar_t *)(uintptr_t)val : TWS_NULL); \ + break; \ + case 's': /* NTS char* */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "[%zu] `" LCPD "`", \ + val ? strlen((char *)(uintptr_t)val) : 0, \ + val ? (char *)(uintptr_t)val : TS_NULL); \ break; \ /* * imediat values */ \ + /* long longs */ \ + case 'z': /* long long signed (SQLLEN) */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lld", \ + (long long)(intptr_t)val); \ + break; \ + case 'Z': /* long long unsigned (SQLULEN) */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%llu", \ + (unsigned long long)(uintptr_t)val); \ + break; \ /* longs */ \ - case 'l': /* long signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%ld", \ + case 'l': /* long signed (SQLINTEGER) */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%ld", \ (long)(intptr_t)val); \ - break;\ - case 'L': /* long unsigned */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%lu", \ + break; \ + case 'L': /* long unsigned (SQLUINTEGER) */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lu", \ (unsigned long)(uintptr_t)val); \ - break;\ + break; \ /* ints */ \ case 'd': /* int signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%d", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%d", \ (int)(intptr_t)val); \ - break;\ + break; \ case 'u': /* int unsigned */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%u", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%u", \ (unsigned)(uintptr_t)val); \ - break;\ - case 'h': /* short signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hd", \ + break; \ + case 'h': /* short signed (SQLSMALLINT) */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hd", \ (short)(intptr_t)val); \ - break;\ - case 'H': /* short unsigned */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hu", \ + break; \ + case 'H': /* short unsigned (SQLUSMALLINT) */ \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hu", \ (unsigned short)(uintptr_t)val); \ - break;\ + break; \ default: \ - _n = snprintf(_bf+_ps, _AVAIL, "BUG! unknown type: %d",type); \ + _n = snprintf(_BUFF+_ps, _AVAIL(_ps), "BUG! unknown type: %d",\ + type); \ break; \ } \ - if (0 < _n) \ - _ps += _n; \ + _CHECK_WRITE(_n, _ps); \ } while (0) /*INDENT-ON*/ @@ -133,15 +174,15 @@ } \ } while (0) -#define _PRINT_PARAM(type, param, add_comma) \ +#define _PRINT_PARAM(type, param, prec, add_comma) \ do { \ BOOL _is_ptr; \ _IS_PTR(type, _is_ptr); \ - int _n = snprintf(_bf + _ps, _AVAIL, "%s%s%s=", add_comma ? ", " : "",\ - _is_ptr ? "*" : "", # param); \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%s%s%s=", \ + add_comma ? ", " : "", _is_ptr ? "*" : "", # param); \ if (0 < _n) /* no proper err check */ \ _ps += _n; \ - _PRINT_PARAM_VAL(type, param); \ + _PRINT_PARAM_VAL(type, param, prec); \ } while (0) @@ -150,205 +191,214 @@ #define _OUT 1 #define _TRACE_OUT "EXIT: " -#define _TRACE_DECLARATION(end) \ - char _bf[1024]; /*"ought to be enough for anybody"*/\ - int _ps = 0; \ - _ps += snprintf(_bf + _ps, _AVAIL, end ? _TRACE_OUT : _TRACE_IN) +#define _TRACE_HEADER(inout, hnd) \ + char _BUFF[TBUFF_SIZE]; \ + int _ps = 0, _n; \ + esodbc_filelog_st *_log; \ + _log = (hnd && HDRH(hnd)->log) ? HDRH(hnd)->log : _gf_log; \ + if ((! _log) || (_log->level < TRACE_LOG_LEVEL)) { \ + /* skip all the printing as early as possible */ \ + break; \ + } \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), inout ? _TRACE_OUT : _TRACE_IN); \ + _CHECK_WRITE(_n, _ps); -#define _TRACE_ENDING \ - _ps += snprintf(_bf + _ps, _AVAIL, "."); \ - LOG(TRACE_LOG_LEVEL, "%s", _bf) +#define _TRACE_FOOTER \ + _esodbc_log(_log, TRACE_LOG_LEVEL, /*werr*/0, \ + __func__, __FILE__, __LINE__, "%s.", _BUFF); -#define TRACE1(out, fmt, p0) \ - do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _TRACE_ENDING; \ - } while(0) -#define TRACE2(out, fmt, p0, p1) \ +#define TRACE1(inout, hnd, fmt, p0) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, SQL_NTS, 0); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE3(out, fmt, p0, p1, p2) \ +#define TRACE2(inout, hnd, fmt, p0, p1) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE4(out, fmt, p0, p1, p2, p3) \ +#define TRACE3(inout, hnd, fmt, p0, p1, p2) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE5(out, fmt, p0, p1, p2, p3, p4) \ +#define TRACE4(inout, hnd, fmt, p0, p1, p2, p3) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE6(out, fmt, p0, p1, p2, p3, p4, p5) \ +#define TRACE5(inout, hnd, fmt, p0, p1, p2, p3, p4) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE7(out, fmt, p0, p1, p2, p3, p4, p5, p6) \ +#define TRACE6(inout, hnd, fmt, p0, p1, p2, p3, p4, p5) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE8(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7) \ +#define TRACE7(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _PRINT_PARAM(fmt[7], p7, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE9(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8) \ +#define TRACE8(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _PRINT_PARAM(fmt[7], p7, 1); \ - _PRINT_PARAM(fmt[8], p8, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, p7, 1); \ + _PRINT_PARAM(fmt[7], p7, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE10(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) \ +#define TRACE9(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _PRINT_PARAM(fmt[7], p7, 1); \ - _PRINT_PARAM(fmt[8], p8, 1); \ - _PRINT_PARAM(fmt[9], p9, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, p7, 1); \ + _PRINT_PARAM(fmt[7], p7, p8, 1); \ + _PRINT_PARAM(fmt[8], p8, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE11(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) \ +#define TRACE10(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _PRINT_PARAM(fmt[7], p7, 1); \ - _PRINT_PARAM(fmt[8], p8, 1); \ - _PRINT_PARAM(fmt[9], p9, 1); \ - _PRINT_PARAM(fmt[10], p10, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, p7, 1); \ + _PRINT_PARAM(fmt[7], p7, p8, 1); \ + _PRINT_PARAM(fmt[8], p8, p9, 1); \ + _PRINT_PARAM(fmt[9], p9, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE12(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) \ +#define TRACE11(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _PRINT_PARAM(fmt[7], p7, 1); \ - _PRINT_PARAM(fmt[8], p8, 1); \ - _PRINT_PARAM(fmt[9], p9, 1); \ - _PRINT_PARAM(fmt[10], p10, 1); \ - _PRINT_PARAM(fmt[11], p11, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, p7, 1); \ + _PRINT_PARAM(fmt[7], p7, p8, 1); \ + _PRINT_PARAM(fmt[8], p8, p9, 1); \ + _PRINT_PARAM(fmt[9], p9, p10, 1); \ + _PRINT_PARAM(fmt[10], p10, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) +#define TRACE12(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, \ + p11) \ +do { \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, p7, 1); \ + _PRINT_PARAM(fmt[7], p7, p8, 1); \ + _PRINT_PARAM(fmt[8], p8, p9, 1); \ + _PRINT_PARAM(fmt[9], p9, p10, 1); \ + _PRINT_PARAM(fmt[10], p10, p11, 1); \ + _PRINT_PARAM(fmt[11], p11, SQL_NTS, 1); \ + _TRACE_FOOTER; \ +} while(0) + /*INDENT-OFF*/ //astyle trips on these following two defs -#define TRACE13(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, \ - p12) \ +#define TRACE13(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, \ + p11, p12) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _PRINT_PARAM(fmt[7], p7, 1); \ - _PRINT_PARAM(fmt[8], p8, 1); \ - _PRINT_PARAM(fmt[9], p9, 1); \ - _PRINT_PARAM(fmt[10], p10, 1); \ - _PRINT_PARAM(fmt[11], p11, 1); \ - _PRINT_PARAM(fmt[12], p12, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, p7, 1); \ + _PRINT_PARAM(fmt[7], p7, p8, 1); \ + _PRINT_PARAM(fmt[8], p8, p9, 1); \ + _PRINT_PARAM(fmt[9], p9, p10, 1); \ + _PRINT_PARAM(fmt[10], p10, p11, 1); \ + _PRINT_PARAM(fmt[11], p11, p12, 1); \ + _PRINT_PARAM(fmt[12], p12, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE14(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, \ - p12, p13) \ +#define TRACE14(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, \ + p11, p12, p13) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _PRINT_PARAM(fmt[1], p1, 1); \ - _PRINT_PARAM(fmt[2], p2, 1); \ - _PRINT_PARAM(fmt[3], p3, 1); \ - _PRINT_PARAM(fmt[4], p4, 1); \ - _PRINT_PARAM(fmt[5], p5, 1); \ - _PRINT_PARAM(fmt[6], p6, 1); \ - _PRINT_PARAM(fmt[7], p7, 1); \ - _PRINT_PARAM(fmt[8], p8, 1); \ - _PRINT_PARAM(fmt[9], p9, 1); \ - _PRINT_PARAM(fmt[10], p10, 1); \ - _PRINT_PARAM(fmt[11], p11, 1); \ - _PRINT_PARAM(fmt[12], p12, 1); \ - _PRINT_PARAM(fmt[13], p13, 1); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, p2, 1); \ + _PRINT_PARAM(fmt[2], p2, p3, 1); \ + _PRINT_PARAM(fmt[3], p3, p4, 1); \ + _PRINT_PARAM(fmt[4], p4, p5, 1); \ + _PRINT_PARAM(fmt[5], p5, p6, 1); \ + _PRINT_PARAM(fmt[6], p6, p7, 1); \ + _PRINT_PARAM(fmt[7], p7, p8, 1); \ + _PRINT_PARAM(fmt[8], p8, p9, 1); \ + _PRINT_PARAM(fmt[9], p9, p10, 1); \ + _PRINT_PARAM(fmt[10], p10, p11, 1); \ + _PRINT_PARAM(fmt[11], p11, p12, 1); \ + _PRINT_PARAM(fmt[12], p12, p13, 1); \ + _PRINT_PARAM(fmt[13], p13, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) /*INDENT-ON*/ diff --git a/driver/util.c b/driver/util.c index 2e6b094a..96cc387e 100644 --- a/driver/util.c +++ b/driver/util.c @@ -9,7 +9,7 @@ #include #include "util.h" -#include "log.h" +#include "handles.h" #include "error.h" @@ -262,7 +262,7 @@ void trim_ws(cstr_st *cstr) * Returns negative if conversion fails, OR number of converted wchars, * including/plus the 0-term. */ -int TEST_API ascii_w2c(SQLWCHAR *src, SQLCHAR *dst, size_t chars) +int TEST_API ascii_w2c(const SQLWCHAR *src, SQLCHAR *dst, size_t chars) { size_t i = 0; @@ -288,7 +288,7 @@ int TEST_API ascii_w2c(SQLWCHAR *src, SQLCHAR *dst, size_t chars) /* * This is the inverse of ascii_w2c(). */ -int TEST_API ascii_c2w(SQLCHAR *src, SQLWCHAR *dst, size_t chars) +int TEST_API ascii_c2w(const SQLCHAR *src, SQLWCHAR *dst, size_t chars) { size_t i = 0; @@ -503,7 +503,7 @@ SQLRETURN write_wstr(SQLHANDLE hnd, SQLWCHAR *dest, wstr_st *src, /* needs to be multiple of SQLWCHAR units (2 on Win) */ if (avail % sizeof(SQLWCHAR)) { ERRH(hnd, "invalid buffer length provided: %d.", avail); - RET_DIAG(&HDRH(hnd)->diag, SQL_STATE_HY090, NULL, 0); + RET_HDIAGS(hnd, SQL_STATE_HY090); } else { wide_avail = avail/sizeof(SQLWCHAR); } @@ -516,7 +516,7 @@ SQLRETURN write_wstr(SQLHANDLE hnd, SQLWCHAR *dest, wstr_st *src, INFOH(hnd, "not enough buffer size to write required string (plus " "terminator): `" LWPD "` [%zu]; available: %zu.", LWSTR(src), src->cnt, wide_avail); - RET_DIAG(&HDRH(hnd)->diag, SQL_STATE_01004, NULL, 0); + RET_HDIAGS(hnd, SQL_STATE_01004); } else { wcsncpy(dest, src->str, src->cnt + /* 0-term */1); } diff --git a/driver/util.h b/driver/util.h index e08f2efb..ee70a840 100644 --- a/driver/util.h +++ b/driver/util.h @@ -79,8 +79,8 @@ typedef struct cstr { * Returns negative if conversion fails, OR number of converted wchars, * including/plus the 0-term. */ -int TEST_API ascii_w2c(SQLWCHAR *src, SQLCHAR *dst, size_t chars); -int TEST_API ascii_c2w(SQLCHAR *src, SQLWCHAR *dst, size_t chars); +int TEST_API ascii_w2c(const SQLWCHAR *src, SQLCHAR *dst, size_t chars); +int TEST_API ascii_c2w(const SQLCHAR *src, SQLWCHAR *dst, size_t chars); /* * Compare two SQLWCHAR object, case INsensitive. */ @@ -114,10 +114,9 @@ typedef struct wstr { #ifndef __cplusplus /* no MSVC support for compound literals with /TP */ # define MK_WSTR(_s) ((wstr_st){.str = MK_WPTR(_s), .cnt = sizeof(_s) - 1}) # define MK_CSTR(_s) ((cstr_st){.str = _s, .cnt = sizeof(_s) - 1}) -#else /* !__cplusplus */ -# define WSTR_INIT(_s) {MK_WPTR(_s), sizeof(_s) - 1} -# define CSTR_INIT(_s) {(SQLCHAR *)_s, sizeof(_s) - 1} #endif /* !__cplusplus */ +#define WSTR_INIT(_s) {MK_WPTR(_s), sizeof(_s) - 1} +#define CSTR_INIT(_s) {(SQLCHAR *)_s, sizeof(_s) - 1} /* * Test equality of two wstr_st objects. */ @@ -194,7 +193,7 @@ typedef SRWLOCK esodbc_mutex_lt; #else /* _WIN32 */ -#error "unsupported platform" /* TODO */ +#error "unsupported platform" /* "[R]eturns the number of bytes written into the multibyte output * string, excluding the terminating NULL (if any)". Copies until \0 is * met in wstr or buffer runs out. If \0 is met, it's copied, but not @@ -254,7 +253,6 @@ SQLRETURN write_wstr(SQLHANDLE hnd, SQLWCHAR *dest, wstr_st *src, * (returned pointer or dst->str). * Returns NULL on error. */ -//cstr_st* TEST_API wstr_to_utf8(wstr_st *src, cstr_st *dst); cstr_st TEST_API *wstr_to_utf8(wstr_st *src, cstr_st *dst); /* @@ -266,7 +264,6 @@ cstr_st TEST_API *wstr_to_utf8(wstr_st *src, cstr_st *dst); * "WPrintF Wide/Char Pointer _ DESCriptor" */ #ifdef _WIN32 -/* funny M$ 'inverted' logic */ /* wprintf wide_t pointer descriptor */ # define WPFWP_DESC L"%s" # define WPFWP_LDESC L"%.*s" @@ -295,30 +292,6 @@ cstr_st TEST_API *wstr_to_utf8(wstr_st *src, cstr_st *dst); #endif /* _WIN32 */ -/* SNTPRINTF: function to printf into a string */ -#ifdef UNICODE -#define SNTPRINTF _snwprintf -#define TSTRCHR wcschr -#else /* UNICODE */ -#define SNTPRINTF snprintf -#define TSTRCHR strchr -#endif /* UNICODE */ - - -/* - * Descriptors to be used with STPRINTF for TCHAR pointer type. - * "SNTPRINTF Tchar Pointer Descriptor [with Length]" - */ -#ifdef UNICODE -#define STPD WPFWP_DESC -#define STPDL WPFWP_LDESC -#else /* UNICODE */ -#define STPD PFCP_DESC -#define STPDL PFCP_LDESC -#endif /* UNICODE */ - - - #endif /* __UTIL_H__ */ /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ diff --git a/dsneditor/EsOdbcDsn.sln b/dsneditor/EsOdbcDsn.sln new file mode 100644 index 00000000..66f2340e --- /dev/null +++ b/dsneditor/EsOdbcDsn.sln @@ -0,0 +1,63 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2009 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EsOdbcDsnEditor", "EsOdbcDsnEditor\EsOdbcDsnEditor.csproj", "{FAC0512C-E595-4BF4-ACB7-617611DF5715}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EsOdbcDsnBinding", "EsOdbcDsnBinding\EsOdbcDsnBinding.vcxproj", "{47824E02-6B1A-4B43-9D20-3CB2F5479F6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EsOdbcDsnEditorLauncher", "EsOdbcDsnEditorLauncher\EsOdbcDsnEditorLauncher.csproj", "{71BEBFF7-652E-4B26-9EC3-CAEF947D368C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Debug|x64.ActiveCfg = Debug|x64 + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Debug|x64.Build.0 = Debug|x64 + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Debug|x86.ActiveCfg = Debug|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Debug|x86.Build.0 = Debug|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Release|Any CPU.Build.0 = Release|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Release|x64.ActiveCfg = Release|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Release|x64.Build.0 = Release|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Release|x86.ActiveCfg = Release|Any CPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715}.Release|x86.Build.0 = Release|Any CPU + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Debug|x64.ActiveCfg = Debug|x64 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Debug|x64.Build.0 = Debug|x64 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Debug|x86.ActiveCfg = Debug|Win32 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Debug|x86.Build.0 = Debug|Win32 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Release|Any CPU.ActiveCfg = Release|Win32 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Release|x64.ActiveCfg = Release|x64 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Release|x64.Build.0 = Release|x64 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Release|x86.ActiveCfg = Release|Win32 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B}.Release|x86.Build.0 = Release|Win32 + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Debug|x64.ActiveCfg = Debug|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Debug|x64.Build.0 = Debug|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Debug|x86.ActiveCfg = Debug|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Debug|x86.Build.0 = Debug|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Release|Any CPU.Build.0 = Release|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Release|x64.ActiveCfg = Release|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Release|x64.Build.0 = Release|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Release|x86.ActiveCfg = Release|Any CPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {208F0E95-4408-4AEB-BE40-8230AF03964B} + EndGlobalSection +EndGlobal diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cc b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cc new file mode 100644 index 00000000..908c96da --- /dev/null +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cc @@ -0,0 +1,228 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +#include +#include +#include +#include + +#include "EsOdbcDsnBinding.h" + +/* ODBC's unprefixed define is 512 */ +#define _SQL_MAX_MESSAGE_LENGTH 1024 + +using namespace System; +using namespace System::Reflection; +using namespace System::Text; +using namespace System::Runtime::InteropServices; +using namespace System::Threading; + +using namespace EsOdbcDsnEditor; + + +namespace EsOdbcDsnBinding { + + /* + * Proxy/Bridge class for the C# actual implementation. + */ + public ref class EsOdbcDsnBinding + { + bool onConnect; + String ^dsnInStr; + int dsnOutLen; + + driver_callback_ft cbConnectionTest; + void *argConnectionTest; + + driver_callback_ft cbSaveDsn; + void *argSaveDsn; + + public:static HWND hwnd; + + + private:int cbImplementation(driver_callback_ft cbFunction, void *cbArgument, + String ^connString, String ^%errorMessage, unsigned int flags) + { + assert(cbFunction); + if (connString->Length <= 0) { + // this should not happen, but if it does, shortcut it here. + return 0; + } + + // the callback understands wchar_t* only, so need to convert the input String + pin_ptr wch = PtrToStringChars(connString); + //buffer to hold the (possible) error message + wchar_t errorMessageW[_SQL_MAX_MESSAGE_LENGTH] = { 0 }; + + int ret = cbFunction(cbArgument, (const wchar_t *)wch, + errorMessageW, sizeof(errorMessageW) / sizeof(*errorMessageW), flags); + + // if there was an error/warning, let the caller know of it + if (0 < wcslen(errorMessageW)) { + // The driver should only return errors that are actionable by the user. + // Error message usage is dictated by error code: might be discarded for some "expected" errors. + errorMessage = gcnew String(errorMessageW); + } + + return ret; + } + /* + * proxy method for the connection test call back + */ + private:int proxyConnectionTest(String ^connString, String ^%errorMessage, unsigned int flags) + { + return cbImplementation(cbConnectionTest, argConnectionTest, connString, errorMessage, flags); + } + + /* + * proxy method for the connection test call back + */ + private:int proxySaveDsn(String ^connString, String ^%errorMessage, unsigned int flags) + { + return cbImplementation(cbSaveDsn, argSaveDsn, connString, errorMessage, flags); + } + + /* + * The constructor merely saves the input parameters and registers an assembly resolve handler + */ + public: EsOdbcDsnBinding(HWND hwnd, bool onConnect, wchar_t *dsnInW, + driver_callback_ft cbConnectionTest, void *argConnectionTest, + driver_callback_ft cbSaveDsn, void *argSaveDsn) + { + /* there should(?) be one window handler in use for one application through the driver */ + assert(this->hwnd == NULL); + this->hwnd = hwnd; + + this->onConnect = onConnect; + dsnInStr = dsnInW != NULL ? gcnew String(dsnInW) : ""; + + this->cbConnectionTest = cbConnectionTest; + this->argConnectionTest = argConnectionTest; + + this->cbSaveDsn = cbSaveDsn; + this->argSaveDsn = argSaveDsn; + + // register an event handler for failed assembly loading + AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler(resolveEventHandler); + } + + public: ~EsOdbcDsnBinding() + { + hwnd = NULL; + } + + /* + * Handler called if loading an assembly fails. + */ + static Assembly^ resolveEventHandler(Object^ sender, ResolveEventArgs^ args) + { + Assembly^ assembly; + try { + // TODO: check if this is safe "enough" ('args' always there, with proper format) + String^ loadingAssembleyName = args->Name->Split(',')[0]; + + if (loadingAssembleyName->EndsWith("resources")) { + // no resources available to load + assembly = nullptr; + } else { + // Get the bridging assembley, its location and based on that build the loading assembly path. + // Note: this assumes that the two libraries are always going to be collocated. + assembly = Assembly::GetExecutingAssembly(); + int lastBackSlash = assembly->Location->LastIndexOf('\\'); + String ^loadingAssemblyPath = assembly->Location->Substring(0, lastBackSlash + 1) + + loadingAssembleyName + ".dll"; + + if (!System::IO::File::Exists(loadingAssemblyPath)) { + throw gcnew Exception("Failed to load assembly '" + loadingAssemblyPath + "'."); + } + + // then load the assembley + assembly = Assembly::LoadFrom(loadingAssemblyPath); + } + + return assembly; + } catch (Exception ^e) { + if (hwnd) { + pin_ptr wch = PtrToStringChars(e->Message); + MessageBox(hwnd, wch, L"Loading Exception", MB_OK | MB_ICONERROR); + } + assembly = nullptr; + } + + return assembly; + } + + public:int EsOdbcDsnEditor() + { + Thread ^ t; + /* (Re)set the threading model; "Multiple calls to CoInitializeEx by the same + thread are allowed as long as they pass the same concurrency flag". + For neutral/MTAs create a new thread, for STAs just run the worker. */ + switch (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY)) { + case S_OK: + case S_FALSE: + DsnEditorWorker(); + break; + case RPC_E_CHANGED_MODE: + t = gcnew Thread(gcnew ThreadStart(this, &EsOdbcDsnBinding::DsnEditorWorker)); + t->ApartmentState = ApartmentState::STA; + t->Start(); + t->Join(); + break; + default: + throw gcnew Exception("setting threading model failed."); + } + + return dsnOutLen; + } + + private:void DsnEditorWorker() + { + EsOdbcDsnEditor::DriverCallbackDelegate ^delegConnectionTest; + EsOdbcDsnEditor::DriverCallbackDelegate ^delegSaveDsn; + + delegConnectionTest = gcnew DriverCallbackDelegate(this, &EsOdbcDsnBinding::proxyConnectionTest); + delegSaveDsn = gcnew DriverCallbackDelegate(this, &EsOdbcDsnBinding::proxySaveDsn); + + dsnOutLen = DsnEditorFactory::DsnEditor(onConnect, dsnInStr, delegConnectionTest, delegSaveDsn); + } + + + }; +} + +#ifdef __cplusplus +extern "C" +#endif /* __cpluplus */ +#ifndef DRIVER_BUILD +__declspec(dllexport) +#else /* DRIVER_BUILD */ +__declspec(dllimport) +#endif /* DRIVER_BUILD */ +int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, + driver_callback_ft cbConnectionTest, void *argConnectionTest, + driver_callback_ft cbSaveDsn, void *argSaveDsn) +{ + try { + EsOdbcDsnBinding::EsOdbcDsnBinding binding(hwnd, onConnect, dsnInW, + cbConnectionTest, argConnectionTest, + cbSaveDsn, argSaveDsn); + return binding.EsOdbcDsnEditor(); + } catch (Exception^ e) { + if (hwnd) { + pin_ptr wch = PtrToStringChars(e->Message); + if (! MessageBox(hwnd, wch, L"Loading Exception", MB_OK | MB_ICONERROR)) { + // failed to display failure error + return -3; + } + // failure error presented to user + return -1; + } + // no window hander available + return -2; + } +} + diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h new file mode 100644 index 00000000..5741400b --- /dev/null +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +#pragma once + +/* needed for HWND definition (just a void*) */ +#include + +/* flag passed to callback to indicate that an overwrite of existing DSN + * is desired by user. */ +#define ESODBC_DSN_OVERWRITE_FLAG (1<<0) + +#define ESODBC_DSN_NO_ERROR 0 +/* Code returned by callback to signal that given DSN name already exists. */ +#define ESODBC_DSN_EXISTS_ERROR -1 +/* The receivd DSN string (connection/00-list) is NULL. */ +#define ESODBC_DSN_ISNULL_ERROR -2 +/* The receivd DSN string couldn't be parsed. */ +#define ESODBC_DSN_INVALID_ERROR -3 +/* The receivd DSN name is invalid. */ +#define ESODBC_DSN_NAME_INVALID_ERROR -4 +/* Non charachteristic (system?) error. */ +#define ESODBC_DSN_GENERIC_ERROR -127 + +/* + * Callback into the driver definition. + * + * Arguments: + * - arg: opaque value provided back to the function (from and back into the + * driver); + * - connectionString: serialized form of the DSN set. + * - connStrLength: the lenght of the connectionString (excluding null + * terminator); + * - errorMessage: out parameter, conveying the failure message (on failure); + * - messageMaxLength: size of the errorMessage buffer (i.e. how much can the + * driver write into 'errorMessage'); + */ +typedef int(*driver_callback_ft)(void *arg, const wchar_t *connectionString, + wchar_t *errorMessage, size_t messageMaxLength, unsigned flags); + +/* + * Main entry point into the GUI. + * + * Arguments: + * - hwnd: window handler passed through the driver; currently only used when + * there's an exception loading or in the assembley; + * - onConnect: if true, the GUI must disable all controls + * non-connection-related (like DSN name, details, log file etc); + * - dsnInW: connection string input parameter; could be empty/NULL. + * - cbConnectionTest: callback from the GUI into the driver when the user + * wants to test the input parameters. + * - argConnectionTest: argument to be passed back to cbConnectionTest; + * - cbSaveDsn: callback from the GUI itno the driver when the user wants to + * save a connection string (here called also 'DSN'); + * - argSaveDsn: argument to be passed back to cbSaveDsn. + * + * Return: + * - success: lenght of output connection string; + * - non-failure: 0 (user canceled); + * - failure: negative. + */ + +#ifdef __cplusplus +extern "C" +#endif /* __cpluplus */ +#ifndef DRIVER_BUILD +__declspec(dllexport) +#else /* DRIVER_BUILD */ +__declspec(dllimport) +#endif /* DRIVER_BUILD */ +int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, + driver_callback_ft cbConnectionTest, void *argConnectionTest, + driver_callback_ft cbSaveDsn, void *argSaveDsn); diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj new file mode 100644 index 00000000..45458646 --- /dev/null +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj @@ -0,0 +1,195 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {47824E02-6B1A-4B43-9D20-3CB2F5479F6B} + Win32Proj + EsOdbcDsnBinding + 10.0.17134.0 + + + + DynamicLibrary + true + v141 + Unicode + true + + + DynamicLibrary + false + v141 + true + Unicode + true + + + DynamicLibrary + true + v141 + Unicode + true + + + DynamicLibrary + false + v141 + true + Unicode + true + + + + + + + + + + + + + + + + + + + + + true + esdsnbnd + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + + + true + esdsnbnd + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + + + false + esdsnbnd + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + + + false + esdsnbnd + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;ESODBCDSNBINDING_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + /Zc:twoPhase- %(AdditionalOptions) + + + Windows + true + + + $(Configuration)\$(Platform)\$(MSBuildProjectName).log + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;ESODBCDSNBINDING_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + /Zc:twoPhase- %(AdditionalOptions) + + + Windows + true + + + $(Configuration)\$(Platform)\$(MSBuildProjectName).log + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;ESODBCDSNBINDING_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + /Zc:twoPhase- %(AdditionalOptions) + + + Windows + true + true + true + + + $(Configuration)\$(Platform)\$(MSBuildProjectName).log + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;ESODBCDSNBINDING_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + /Zc:twoPhase- %(AdditionalOptions) + + + Windows + true + true + true + + + $(Configuration)\$(Platform)\$(MSBuildProjectName).log + + + + + + + + {fac0512c-e595-4bf4-acb7-617611df5715} + + + + + + + + + diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters new file mode 100644 index 00000000..c03e342e --- /dev/null +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + diff --git a/dsneditor/EsOdbcDsnEditor/App.config b/dsneditor/EsOdbcDsnEditor/App.config new file mode 100644 index 00000000..160269e9 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/App.config @@ -0,0 +1,4 @@ + + + + diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs new file mode 100644 index 00000000..ed568caf --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -0,0 +1,557 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +namespace EsOdbcDsnEditor +{ + partial class DsnEditorForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DsnEditorForm)); + this.saveButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.testButton = new System.Windows.Forms.Button(); + this.header = new System.Windows.Forms.PictureBox(); + this.certificatePathButton = new System.Windows.Forms.Button(); + this.textCertificatePath = new System.Windows.Forms.TextBox(); + this.labelCertificatePath = new System.Windows.Forms.Label(); + this.groupSSL = new System.Windows.Forms.GroupBox(); + this.radioEnabledFull = new System.Windows.Forms.RadioButton(); + this.radioEnabledHostname = new System.Windows.Forms.RadioButton(); + this.radioEnabledNoHostname = new System.Windows.Forms.RadioButton(); + this.radioEnabledNoValidation = new System.Windows.Forms.RadioButton(); + this.radioButtonDisabled = new System.Windows.Forms.RadioButton(); + this.numericUpDownPort = new System.Windows.Forms.NumericUpDown(); + this.textPassword = new System.Windows.Forms.TextBox(); + this.labelPassword = new System.Windows.Forms.Label(); + this.textUsername = new System.Windows.Forms.TextBox(); + this.labelUsername = new System.Windows.Forms.Label(); + this.textHostname = new System.Windows.Forms.TextBox(); + this.labelPort = new System.Windows.Forms.Label(); + this.labelHostname = new System.Windows.Forms.Label(); + this.certificateFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.tabConfiguration = new System.Windows.Forms.TabControl(); + this.pageBasic = new System.Windows.Forms.TabPage(); + this.textDescription = new System.Windows.Forms.TextBox(); + this.labelDescription = new System.Windows.Forms.Label(); + this.textName = new System.Windows.Forms.TextBox(); + this.labelName = new System.Windows.Forms.Label(); + this.pageSecurity = new System.Windows.Forms.TabPage(); + this.pageLogging = new System.Windows.Forms.TabPage(); + this.checkLoggingEnabled = new System.Windows.Forms.CheckBox(); + this.comboLogLevel = new System.Windows.Forms.ComboBox(); + this.labelLogLevel = new System.Windows.Forms.Label(); + this.logDirectoryPathButton = new System.Windows.Forms.Button(); + this.textLogDirectoryPath = new System.Windows.Forms.TextBox(); + this.labelLogDirectory = new System.Windows.Forms.Label(); + this.folderLogDirectoryDialog = new System.Windows.Forms.FolderBrowserDialog(); + ((System.ComponentModel.ISupportInitialize)(this.header)).BeginInit(); + this.groupSSL.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).BeginInit(); + this.tabConfiguration.SuspendLayout(); + this.pageBasic.SuspendLayout(); + this.pageSecurity.SuspendLayout(); + this.pageLogging.SuspendLayout(); + this.SuspendLayout(); + // + // saveButton + // + this.saveButton.Location = new System.Drawing.Point(514, 676); + this.saveButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.saveButton.Name = "saveButton"; + this.saveButton.Size = new System.Drawing.Size(112, 35); + this.saveButton.TabIndex = 17; + this.saveButton.Text = "Save"; + this.saveButton.UseVisualStyleBackColor = true; + this.saveButton.Click += new System.EventHandler(this.SaveButton_Click); + // + // cancelButton + // + this.cancelButton.Location = new System.Drawing.Point(630, 676); + this.cancelButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(112, 35); + this.cancelButton.TabIndex = 18; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.CancelButton_Click); + // + // testButton + // + this.testButton.Location = new System.Drawing.Point(19, 676); + this.testButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.testButton.Name = "testButton"; + this.testButton.Size = new System.Drawing.Size(176, 35); + this.testButton.TabIndex = 16; + this.testButton.Text = "Test Connection"; + this.testButton.UseVisualStyleBackColor = true; + this.testButton.Click += new System.EventHandler(this.TestConnectionButton_Click); + // + // header + // + this.header.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("header.BackgroundImage"))); + this.header.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.header.InitialImage = null; + this.header.Location = new System.Drawing.Point(0, 0); + this.header.Margin = new System.Windows.Forms.Padding(0); + this.header.Name = "header"; + this.header.Size = new System.Drawing.Size(765, 72); + this.header.TabIndex = 5; + this.header.TabStop = false; + // + // certificatePathButton + // + this.certificatePathButton.Location = new System.Drawing.Point(590, 258); + this.certificatePathButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.certificatePathButton.Name = "certificatePathButton"; + this.certificatePathButton.Size = new System.Drawing.Size(112, 35); + this.certificatePathButton.TabIndex = 15; + this.certificatePathButton.Text = "Browse..."; + this.certificatePathButton.UseVisualStyleBackColor = true; + this.certificatePathButton.Click += new System.EventHandler(this.CertificatePathButton_Click); + // + // textCertificatePath + // + this.textCertificatePath.Location = new System.Drawing.Point(140, 261); + this.textCertificatePath.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.textCertificatePath.Name = "textCertificatePath"; + this.textCertificatePath.Size = new System.Drawing.Size(440, 26); + this.textCertificatePath.TabIndex = 14; + // + // labelCertificatePath + // + this.labelCertificatePath.AutoSize = true; + this.labelCertificatePath.Location = new System.Drawing.Point(18, 265); + this.labelCertificatePath.Name = "labelCertificatePath"; + this.labelCertificatePath.Size = new System.Drawing.Size(114, 20); + this.labelCertificatePath.TabIndex = 11; + this.labelCertificatePath.Text = "Certificate File:"; + // + // groupSSL + // + this.groupSSL.Controls.Add(this.radioEnabledFull); + this.groupSSL.Controls.Add(this.radioEnabledHostname); + this.groupSSL.Controls.Add(this.radioEnabledNoHostname); + this.groupSSL.Controls.Add(this.radioEnabledNoValidation); + this.groupSSL.Controls.Add(this.radioButtonDisabled); + this.groupSSL.Location = new System.Drawing.Point(18, 20); + this.groupSSL.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.groupSSL.Name = "groupSSL"; + this.groupSSL.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.groupSSL.Size = new System.Drawing.Size(684, 220); + this.groupSSL.TabIndex = 10; + this.groupSSL.TabStop = false; + this.groupSSL.Text = "Secure Sockets Layer (SSL):"; + // + // radioEnabledFull + // + this.radioEnabledFull.AutoSize = true; + this.radioEnabledFull.Location = new System.Drawing.Point(18, 174); + this.radioEnabledFull.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.radioEnabledFull.Name = "radioEnabledFull"; + this.radioEnabledFull.Size = new System.Drawing.Size(340, 24); + this.radioEnabledFull.TabIndex = 13; + this.radioEnabledFull.TabStop = true; + this.radioEnabledFull.Text = "Enabled. Certificate identity chain validated."; + this.radioEnabledFull.UseVisualStyleBackColor = true; + // + // radioEnabledHostname + // + this.radioEnabledHostname.AutoSize = true; + this.radioEnabledHostname.Location = new System.Drawing.Point(18, 140); + this.radioEnabledHostname.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.radioEnabledHostname.Name = "radioEnabledHostname"; + this.radioEnabledHostname.Size = new System.Drawing.Size(405, 24); + this.radioEnabledHostname.TabIndex = 12; + this.radioEnabledHostname.TabStop = true; + this.radioEnabledHostname.Text = "Enabled. Certificate is validated; hostname validated."; + this.radioEnabledHostname.UseVisualStyleBackColor = true; + // + // radioEnabledNoHostname + // + this.radioEnabledNoHostname.AutoSize = true; + this.radioEnabledNoHostname.Location = new System.Drawing.Point(18, 106); + this.radioEnabledNoHostname.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.radioEnabledNoHostname.Name = "radioEnabledNoHostname"; + this.radioEnabledNoHostname.Size = new System.Drawing.Size(432, 24); + this.radioEnabledNoHostname.TabIndex = 11; + this.radioEnabledNoHostname.TabStop = true; + this.radioEnabledNoHostname.Text = "Enabled. Certificate is validated; hostname not validated."; + this.radioEnabledNoHostname.UseVisualStyleBackColor = true; + // + // radioEnabledNoValidation + // + this.radioEnabledNoValidation.AutoSize = true; + this.radioEnabledNoValidation.Location = new System.Drawing.Point(18, 72); + this.radioEnabledNoValidation.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.radioEnabledNoValidation.Name = "radioEnabledNoValidation"; + this.radioEnabledNoValidation.Size = new System.Drawing.Size(271, 24); + this.radioEnabledNoValidation.TabIndex = 10; + this.radioEnabledNoValidation.TabStop = true; + this.radioEnabledNoValidation.Text = "Enabled. Certificate not validated."; + this.radioEnabledNoValidation.UseVisualStyleBackColor = true; + // + // radioButtonDisabled + // + this.radioButtonDisabled.AutoSize = true; + this.radioButtonDisabled.Location = new System.Drawing.Point(18, 39); + this.radioButtonDisabled.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.radioButtonDisabled.Name = "radioButtonDisabled"; + this.radioButtonDisabled.Size = new System.Drawing.Size(336, 24); + this.radioButtonDisabled.TabIndex = 9; + this.radioButtonDisabled.TabStop = true; + this.radioButtonDisabled.Text = "Disabled. All communications unencrypted."; + this.radioButtonDisabled.UseVisualStyleBackColor = true; + // + // numericUpDownPort + // + this.numericUpDownPort.Location = new System.Drawing.Point(126, 308); + this.numericUpDownPort.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.numericUpDownPort.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this.numericUpDownPort.Name = "numericUpDownPort"; + this.numericUpDownPort.Size = new System.Drawing.Size(117, 26); + this.numericUpDownPort.TabIndex = 5; + this.numericUpDownPort.Value = new decimal(new int[] { + 9200, + 0, + 0, + 0}); + this.numericUpDownPort.ValueChanged += new System.EventHandler(this.NumericUpDownPort_ValueChanged); + // + // textPassword + // + this.textPassword.Location = new System.Drawing.Point(126, 405); + this.textPassword.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.textPassword.Name = "textPassword"; + this.textPassword.PasswordChar = '*'; + this.textPassword.Size = new System.Drawing.Size(257, 26); + this.textPassword.TabIndex = 7; + // + // labelPassword + // + this.labelPassword.AutoSize = true; + this.labelPassword.Location = new System.Drawing.Point(18, 409); + this.labelPassword.Name = "labelPassword"; + this.labelPassword.Size = new System.Drawing.Size(82, 20); + this.labelPassword.TabIndex = 6; + this.labelPassword.Text = "Password:"; + // + // textUsername + // + this.textUsername.Location = new System.Drawing.Point(126, 355); + this.textUsername.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.textUsername.Name = "textUsername"; + this.textUsername.Size = new System.Drawing.Size(257, 26); + this.textUsername.TabIndex = 6; + // + // labelUsername + // + this.labelUsername.AutoSize = true; + this.labelUsername.Location = new System.Drawing.Point(18, 359); + this.labelUsername.Name = "labelUsername"; + this.labelUsername.Size = new System.Drawing.Size(87, 20); + this.labelUsername.TabIndex = 4; + this.labelUsername.Text = "Username:"; + // + // textHostname + // + this.textHostname.Location = new System.Drawing.Point(126, 260); + this.textHostname.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.textHostname.Name = "textHostname"; + this.textHostname.Size = new System.Drawing.Size(569, 26); + this.textHostname.TabIndex = 4; + this.textHostname.TextChanged += new System.EventHandler(this.TextHostname_TextChanged); + // + // labelPort + // + this.labelPort.AutoSize = true; + this.labelPort.Location = new System.Drawing.Point(18, 310); + this.labelPort.Name = "labelPort"; + this.labelPort.Size = new System.Drawing.Size(42, 20); + this.labelPort.TabIndex = 2; + this.labelPort.Text = "Port:"; + // + // labelHostname + // + this.labelHostname.AutoSize = true; + this.labelHostname.Location = new System.Drawing.Point(18, 264); + this.labelHostname.Name = "labelHostname"; + this.labelHostname.Size = new System.Drawing.Size(87, 20); + this.labelHostname.TabIndex = 0; + this.labelHostname.Text = "Hostname:"; + // + // certificateFileDialog + // + this.certificateFileDialog.Filter = "X509 Certificate|*.pem;*.der|All Files|*.*"; + // + // tabConfiguration + // + this.tabConfiguration.Controls.Add(this.pageBasic); + this.tabConfiguration.Controls.Add(this.pageSecurity); + this.tabConfiguration.Controls.Add(this.pageLogging); + this.tabConfiguration.Location = new System.Drawing.Point(18, 92); + this.tabConfiguration.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.tabConfiguration.Name = "tabConfiguration"; + this.tabConfiguration.SelectedIndex = 0; + this.tabConfiguration.Size = new System.Drawing.Size(729, 575); + this.tabConfiguration.TabIndex = 8; + // + // pageBasic + // + this.pageBasic.Controls.Add(this.textDescription); + this.pageBasic.Controls.Add(this.labelDescription); + this.pageBasic.Controls.Add(this.textName); + this.pageBasic.Controls.Add(this.labelName); + this.pageBasic.Controls.Add(this.textHostname); + this.pageBasic.Controls.Add(this.labelHostname); + this.pageBasic.Controls.Add(this.labelPort); + this.pageBasic.Controls.Add(this.labelUsername); + this.pageBasic.Controls.Add(this.textUsername); + this.pageBasic.Controls.Add(this.numericUpDownPort); + this.pageBasic.Controls.Add(this.labelPassword); + this.pageBasic.Controls.Add(this.textPassword); + this.pageBasic.Location = new System.Drawing.Point(4, 29); + this.pageBasic.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.pageBasic.Name = "pageBasic"; + this.pageBasic.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.pageBasic.Size = new System.Drawing.Size(721, 542); + this.pageBasic.TabIndex = 0; + this.pageBasic.Text = "Basic"; + this.pageBasic.UseVisualStyleBackColor = true; + // + // textDescription + // + this.textDescription.Location = new System.Drawing.Point(126, 70); + this.textDescription.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.textDescription.Multiline = true; + this.textDescription.Name = "textDescription"; + this.textDescription.Size = new System.Drawing.Size(569, 164); + this.textDescription.TabIndex = 3; + // + // labelDescription + // + this.labelDescription.AutoSize = true; + this.labelDescription.Location = new System.Drawing.Point(18, 74); + this.labelDescription.Name = "labelDescription"; + this.labelDescription.Size = new System.Drawing.Size(93, 20); + this.labelDescription.TabIndex = 10; + this.labelDescription.Text = "Description:"; + // + // textName + // + this.textName.Location = new System.Drawing.Point(126, 20); + this.textName.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.textName.Name = "textName"; + this.textName.Size = new System.Drawing.Size(569, 26); + this.textName.TabIndex = 2; + this.textName.TextChanged += new System.EventHandler(this.TextName_TextChanged); + // + // labelName + // + this.labelName.AutoSize = true; + this.labelName.Location = new System.Drawing.Point(18, 26); + this.labelName.Name = "labelName"; + this.labelName.Size = new System.Drawing.Size(55, 20); + this.labelName.TabIndex = 8; + this.labelName.Text = "Name:"; + // + // pageSecurity + // + this.pageSecurity.Controls.Add(this.certificatePathButton); + this.pageSecurity.Controls.Add(this.groupSSL); + this.pageSecurity.Controls.Add(this.textCertificatePath); + this.pageSecurity.Controls.Add(this.labelCertificatePath); + this.pageSecurity.Location = new System.Drawing.Point(4, 29); + this.pageSecurity.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.pageSecurity.Name = "pageSecurity"; + this.pageSecurity.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.pageSecurity.Size = new System.Drawing.Size(721, 542); + this.pageSecurity.TabIndex = 1; + this.pageSecurity.Text = "Security"; + this.pageSecurity.UseVisualStyleBackColor = true; + // + // pageLogging + // + this.pageLogging.Controls.Add(this.checkLoggingEnabled); + this.pageLogging.Controls.Add(this.comboLogLevel); + this.pageLogging.Controls.Add(this.labelLogLevel); + this.pageLogging.Controls.Add(this.logDirectoryPathButton); + this.pageLogging.Controls.Add(this.textLogDirectoryPath); + this.pageLogging.Controls.Add(this.labelLogDirectory); + this.pageLogging.Location = new System.Drawing.Point(4, 29); + this.pageLogging.Name = "pageLogging"; + this.pageLogging.Padding = new System.Windows.Forms.Padding(3); + this.pageLogging.Size = new System.Drawing.Size(721, 542); + this.pageLogging.TabIndex = 2; + this.pageLogging.Text = "Logging"; + this.pageLogging.UseVisualStyleBackColor = true; + // + // checkLoggingEnabled + // + this.checkLoggingEnabled.AutoSize = true; + this.checkLoggingEnabled.Checked = true; + this.checkLoggingEnabled.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkLoggingEnabled.Location = new System.Drawing.Point(18, 26); + this.checkLoggingEnabled.Name = "checkLoggingEnabled"; + this.checkLoggingEnabled.Size = new System.Drawing.Size(155, 24); + this.checkLoggingEnabled.TabIndex = 22; + this.checkLoggingEnabled.Text = "Enable Logging?"; + this.checkLoggingEnabled.UseVisualStyleBackColor = true; + this.checkLoggingEnabled.CheckedChanged += new System.EventHandler(this.CheckLoggingEnabled_CheckedChanged); + // + // comboLogLevel + // + this.comboLogLevel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboLogLevel.FormattingEnabled = true; + this.comboLogLevel.Items.AddRange(new object[] { + "DEBUG", + "INFO", + "WARN", + "ERROR"}); + this.comboLogLevel.Location = new System.Drawing.Point(139, 123); + this.comboLogLevel.Name = "comboLogLevel"; + this.comboLogLevel.Size = new System.Drawing.Size(121, 28); + this.comboLogLevel.TabIndex = 21; + // + // labelLogLevel + // + this.labelLogLevel.AutoSize = true; + this.labelLogLevel.Location = new System.Drawing.Point(18, 126); + this.labelLogLevel.Name = "labelLogLevel"; + this.labelLogLevel.Size = new System.Drawing.Size(81, 20); + this.labelLogLevel.TabIndex = 20; + this.labelLogLevel.Text = "Log Level:"; + // + // logDirectoryPathButton + // + this.logDirectoryPathButton.Location = new System.Drawing.Point(592, 69); + this.logDirectoryPathButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.logDirectoryPathButton.Name = "logDirectoryPathButton"; + this.logDirectoryPathButton.Size = new System.Drawing.Size(112, 35); + this.logDirectoryPathButton.TabIndex = 18; + this.logDirectoryPathButton.Text = "Browse..."; + this.logDirectoryPathButton.UseVisualStyleBackColor = true; + this.logDirectoryPathButton.Click += new System.EventHandler(this.LogDirectoryPathButton_Click); + // + // textLogDirectoryPath + // + this.textLogDirectoryPath.Location = new System.Drawing.Point(139, 71); + this.textLogDirectoryPath.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.textLogDirectoryPath.Name = "textLogDirectoryPath"; + this.textLogDirectoryPath.Size = new System.Drawing.Size(440, 26); + this.textLogDirectoryPath.TabIndex = 17; + // + // labelLogDirectory + // + this.labelLogDirectory.AutoSize = true; + this.labelLogDirectory.Location = new System.Drawing.Point(18, 77); + this.labelLogDirectory.Name = "labelLogDirectory"; + this.labelLogDirectory.Size = new System.Drawing.Size(107, 20); + this.labelLogDirectory.TabIndex = 16; + this.labelLogDirectory.Text = "Log Directory:"; + // + // DsnEditorForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(767, 735); + this.Controls.Add(this.tabConfiguration); + this.Controls.Add(this.header); + this.Controls.Add(this.testButton); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.saveButton); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DsnEditorForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "Elasticsearch ODBC DSN Configuration"; + ((System.ComponentModel.ISupportInitialize)(this.header)).EndInit(); + this.groupSSL.ResumeLayout(false); + this.groupSSL.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).EndInit(); + this.tabConfiguration.ResumeLayout(false); + this.pageBasic.ResumeLayout(false); + this.pageBasic.PerformLayout(); + this.pageSecurity.ResumeLayout(false); + this.pageSecurity.PerformLayout(); + this.pageLogging.ResumeLayout(false); + this.pageLogging.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.Button saveButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button testButton; + private System.Windows.Forms.PictureBox header; + private System.Windows.Forms.TextBox textHostname; + private System.Windows.Forms.Label labelPort; + private System.Windows.Forms.Label labelHostname; + private System.Windows.Forms.TextBox textPassword; + private System.Windows.Forms.Label labelPassword; + private System.Windows.Forms.TextBox textUsername; + private System.Windows.Forms.Label labelUsername; + private System.Windows.Forms.NumericUpDown numericUpDownPort; + private System.Windows.Forms.RadioButton radioButtonDisabled; + private System.Windows.Forms.GroupBox groupSSL; + private System.Windows.Forms.RadioButton radioEnabledFull; + private System.Windows.Forms.RadioButton radioEnabledHostname; + private System.Windows.Forms.RadioButton radioEnabledNoHostname; + private System.Windows.Forms.RadioButton radioEnabledNoValidation; + private System.Windows.Forms.Button certificatePathButton; + private System.Windows.Forms.TextBox textCertificatePath; + private System.Windows.Forms.Label labelCertificatePath; + private System.Windows.Forms.OpenFileDialog certificateFileDialog; + private System.Windows.Forms.TabControl tabConfiguration; + private System.Windows.Forms.TabPage pageBasic; + private System.Windows.Forms.TabPage pageSecurity; + private System.Windows.Forms.TextBox textDescription; + private System.Windows.Forms.Label labelDescription; + private System.Windows.Forms.TextBox textName; + private System.Windows.Forms.Label labelName; + private System.Windows.Forms.TabPage pageLogging; + private System.Windows.Forms.Label labelLogLevel; + private System.Windows.Forms.Button logDirectoryPathButton; + private System.Windows.Forms.TextBox textLogDirectoryPath; + private System.Windows.Forms.Label labelLogDirectory; + private System.Windows.Forms.FolderBrowserDialog folderLogDirectoryDialog; + private System.Windows.Forms.ComboBox comboLogLevel; + private System.Windows.Forms.CheckBox checkLoggingEnabled; + } +} + diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs new file mode 100644 index 00000000..2033e262 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -0,0 +1,353 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +using System; +using System.Data.Odbc; +using System.Windows.Forms; +using System.IO; + +// uncomment to have the assembly loading to ask for (various) resources; various solutions: +// https://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl +// [assembly: NeutralResourcesLanguageAttribute("en-GB", UltimateResourceFallbackLocation.MainAssembly)] + +namespace EsOdbcDsnEditor +{ + /// + /// Delegate for the driver callbacks. + /// + public delegate int DriverCallbackDelegate(string connectionString, ref string errorMessage, uint flags); + + public partial class DsnEditorForm : Form + { + private const int ESODBC_DSN_EXISTS_ERROR = -1; + + private DriverCallbackDelegate testConnection; + private DriverCallbackDelegate saveDsn; + + private readonly bool isConnecting; + + public OdbcConnectionStringBuilder Builder { get; set; } = new OdbcConnectionStringBuilder(); + + public DsnEditorForm( + bool onConnect, + string dsn, + DriverCallbackDelegate connectionTest, + DriverCallbackDelegate dsnSave) + { + InitializeComponent(); + + // Wire up default button behaviours + AcceptButton = saveButton; + CancelButton = cancelButton; + + isConnecting = onConnect; + testConnection = connectionTest; + saveDsn = dsnSave; + + // If connecting then disable some user inputs + if (isConnecting) + { + textName.ReadOnly = textDescription.ReadOnly = true; + textName.Enabled = textDescription.Enabled = false; + } + + // If this is a call serving a connect request, name the button "Connect", otherwise it's a DSN editing, so it's going to be a "Save". + saveButton.Text = onConnect ? "Connect" : "Save"; + + // Parse DSN into the builder + Builder.ConnectionString = dsn; + + // Basic Panel + textName.Text = Builder.ContainsKey("dsn") ? Builder["dsn"].ToString().StripBraces() : string.Empty; + textDescription.Text = Builder.ContainsKey("description") ? Builder["description"].ToString().StripBraces() : string.Empty; + textUsername.Text = Builder.ContainsKey("uid") ? Builder["uid"].ToString().StripBraces() : string.Empty; + textPassword.Text = Builder.ContainsKey("pwd") ? Builder["pwd"].ToString().StripBraces() : string.Empty; + textHostname.Text = Builder.ContainsKey("server") ? Builder["server"].ToString().StripBraces() : string.Empty; + numericUpDownPort.Text = Builder.ContainsKey("port") ? Builder["port"].ToString().StripBraces() : string.Empty; + + // Security Panel + textCertificatePath.Text = Builder.ContainsKey("capath") ? Builder["capath"].ToString().StripBraces() : string.Empty; + radioEnabledNoValidation.Checked = true; // Default setting + if (Builder.ContainsKey("secure")) + { + var result = int.TryParse(Builder["secure"].ToString(), out int val); + if (result) + { + switch(val) + { + case 0: radioButtonDisabled.Checked = true; break; + case 1: radioEnabledNoValidation.Checked = true; break; + case 2: radioEnabledNoHostname.Checked = true; break; + case 3: radioEnabledHostname.Checked = true; break; + case 4: radioEnabledFull.Checked = true; break; + } + } + } + + // Logging Panel + textLogDirectoryPath.Text = Builder.ContainsKey("tracefile") ? Builder["tracefile"].ToString().StripBraces() : string.Empty; + comboLogLevel.Text = "DEBUG"; // Default setting + checkLoggingEnabled.Checked = true; // Default setting + if (Builder.ContainsKey("tracelevel")) + { + switch (Builder["tracelevel"].ToString().ToUpperInvariant()) + { + case "DEBUG": comboLogLevel.Text = "DEBUG"; break; + case "INFO": comboLogLevel.Text = "INFO"; break; + case "WARN": comboLogLevel.Text = "WARN"; break; + case "ERROR": comboLogLevel.Text = "ERROR"; break; + } + } + if (Builder.ContainsKey("traceenabled")) + { + var result = int.TryParse(Builder["traceenabled"].ToString(), out int val); + if (result) + { + switch (val) + { + case 0: checkLoggingEnabled.Checked = false; break; + default: checkLoggingEnabled.Checked = true; break; + } + } + } + else + { + checkLoggingEnabled.Checked = false; + } + + // Set initial state of action buttons. + EnableDisableActionButtons(); + } + + /// + /// On save, call the driver's callback. If operation succeeds, close the window. + /// On failure, display the error received from the driver and keep editing. + /// + private void SaveButton_Click(object sender, EventArgs e) + { + SaveDsn(false); + } + + private void SaveDsn(bool forceOverwrite) + { + var errorMessage = string.Empty; + + var dsnResult = RebuildAndValidateDsn(); + if (!dsnResult) return; + + var dsn = Builder.ToString(); + var flag = forceOverwrite ? 1u : 0; + + int result = saveDsn(dsn, ref errorMessage, flag); + if (result >= 0 || (forceOverwrite + && result == ESODBC_DSN_EXISTS_ERROR)) + { + Close(); + return; + } + + // Specific handling for prompting the user if result is an overwrite action + if (forceOverwrite == false + && result == ESODBC_DSN_EXISTS_ERROR) + { + var dialogResult = MessageBox.Show("The DSN already exists, are you sure you wish to overwrite it?", "Overwrite", MessageBoxButtons.YesNo); + if (dialogResult == DialogResult.Yes) + { + SaveDsn(true); + } + + return; + } + + if (errorMessage.Length <= 0) + { + errorMessage = "Saving the DSN failed"; + } + + MessageBox.Show(errorMessage, "Operation failure", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + /// + /// With the Test button the user checks if the input data leads to a connection. + /// The function calls the driver callback and displays the result of the operation. + /// + private void TestConnectionButton_Click(object sender, EventArgs e) + { + var errorMessage = string.Empty; + + var dsnResult = RebuildAndValidateDsn(); + if (!dsnResult) return; + + var dsn = Builder.ToString(); + + // Wrap slow operation in a wait cursor + Cursor = Cursors.WaitCursor; + int result = testConnection(dsn, ref errorMessage, 0); + Cursor = Cursors.Arrow; + + if (result >= 0) + { + MessageBox.Show("Connection Success", "Connection Test", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + var message = "Connection Failed"; + if (0 < errorMessage.Length) + { + message += ": " + errorMessage; + } + MessageBox.Show(message, "Connection Test", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private bool RebuildAndValidateDsn() + { + // Basic Panel + Builder["dsn"] = textName.Text; + Builder["description"] = textDescription.Text; + Builder["uid"] = textUsername.Text; + Builder["pwd"] = textPassword.Text; + Builder["server"] = textHostname.Text; + Builder["port"] = numericUpDownPort.Text; + + // Security Panel + Builder["capath"] = textCertificatePath.Text; + if (radioButtonDisabled.Checked) Builder["secure"] = 0; + if (radioEnabledNoValidation.Checked) Builder["secure"] = 1; + if (radioEnabledNoHostname.Checked) Builder["secure"] = 2; + if (radioEnabledHostname.Checked) Builder["secure"] = 3; + if (radioEnabledFull.Checked) Builder["secure"] = 4; + + // Logging Panel + Builder["tracefile"] = textLogDirectoryPath.Text; + Builder["tracelevel"] = comboLogLevel.Text; + Builder["traceenabled"] = checkLoggingEnabled.Checked ? "1" : "0"; + + // Validations + var certificateFileOK = true; + var logDirectoryOK = true; + + if (!string.IsNullOrEmpty(textCertificatePath.Text)) + { + certificateFileOK = ValidateCertificateFile(textCertificatePath.Text); + } + + if (!string.IsNullOrEmpty(textLogDirectoryPath.Text)) + { + logDirectoryOK = ValidateLogFolderPath(textLogDirectoryPath.Text); + } + + return certificateFileOK && logDirectoryOK; + } + + private void LogDirectoryPathButton_Click(object sender, EventArgs e) + { + var result = folderLogDirectoryDialog.ShowDialog(); + if (result == DialogResult.OK) + { + string path = folderLogDirectoryDialog.SelectedPath; + if (ValidateLogFolderPath(path)) + { + textLogDirectoryPath.Text = path; + } + } + } + + private bool ValidateLogFolderPath(string path) + { + if (string.IsNullOrEmpty(path) || Directory.Exists(path)) + { + return true; + } + + MessageBox.Show("Log directory invalid, path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; + } + + private void CertificatePathButton_Click(object sender, EventArgs e) + { + var result = certificateFileDialog.ShowDialog(); + if (result == DialogResult.OK) + { + string file = certificateFileDialog.FileName; + if (ValidateCertificateFile(file)) + { + textCertificatePath.Text = file; + } + } + } + + private bool ValidateCertificateFile(string file) + { + if (string.IsNullOrEmpty(file)) + { + return true; + } + + var info = new FileInfo(file); + if (info.Exists && info.Length > 0) + { + return true; + } + + MessageBox.Show("Certificate file invalid", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; + } + + private void TextName_TextChanged(object sender, EventArgs e) + { + EnableDisableActionButtons(); + } + + private void TextHostname_TextChanged(object sender, EventArgs e) + { + EnableDisableActionButtons(); + } + + private void NumericUpDownPort_ValueChanged(object sender, EventArgs e) + { + EnableDisableActionButtons(); + } + + private void EnableDisableActionButtons() + { + saveButton.Enabled = string.IsNullOrEmpty(textName.Text) == false + && string.IsNullOrEmpty(textHostname.Text) == false; + testButton.Enabled = string.IsNullOrEmpty(textHostname.Text) == false; + } + + private void CheckLoggingEnabled_CheckedChanged(object sender, EventArgs e) + { + EnableDisableLoggingControls(); + } + + private void EnableDisableLoggingControls() + { + textLogDirectoryPath.Enabled = checkLoggingEnabled.Checked; + comboLogLevel.Enabled = checkLoggingEnabled.Checked; + logDirectoryPathButton.Enabled = checkLoggingEnabled.Checked; + } + + private void CancelButton_Click(object sender, EventArgs e) + { + // Clear the builder so that the resulting int returned to the caller is 0. + Builder.Clear(); + Close(); + } + + // Remove the [X] close button in top right to force user to use the cancel button. + protected override CreateParams CreateParams + { + get + { + const int WS_SYSMENU = 0x80000; + CreateParams cp = base.CreateParams; + cp.Style &= ~WS_SYSMENU; + return cp; + } + } + } +} diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx new file mode 100644 index 00000000..2a42dcab --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx @@ -0,0 +1,3120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + Qk12aAIAAAAAADYAAAAoAAAAqAIAADoAAAABACAAAAAAAAAAAAASCwAAEgsAAAAAAAAAAAAAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABIPjsAjIaEAMrH + xgDy8fEA////AP///wDp5+cAvbm4AHpzcQBCODUAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAgHl3AO3s7AD9/vsA6u3EANXaiwDM03IAzNJwANbbjADq7cQA/v79ANrZ2ABmXVsAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AKSfngD///8A4+exALjBOgCvuSAAr7kgAK+5IACvuSAAr7kgAK+5 + IAC7w0AA5+q9APn5+QB7dHIAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NACcl5UA/v78AM3TdACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAA1tuMAPv7+wB0bGoAlI6MAODe3gD+/f0A9/b2AMnG + xQBtZWMAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAaWFfAP79 + /QDR1n4Ar7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAA2t+YAP7+ + /gDq+fMAluDEAHbXsgCI3LwAzvHjAP79/QCLhYMAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NADQzs0A6+7IAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IACwuiMA/f35AG7VrQA8xpEAPMaRADzGkQA8xpEAn+PJAPz8/ABlXVoAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAYFhVAP///wDByVMAr7kgAK+5IACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IADv8dMAkN/AADzGkQA8xpEAPMaRADzG + kQA8xpEA0/LmALm1tABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NACalZQA9vfmAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAODk + qQC06dUAPMaRADzGkQA8xpEAPMaRADzGkQCN3r8A6OfmAEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0ALy4twDi5a4Ar7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IACvuSAA0teAANn06QA8xpEAPMaRADzGkQA8xpEAPMaRAHjYswDy8fEAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAzsvKANnelgCvuSAAr7kgAK+5IACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IADCylYA+v38AD7HkgA8xpEAPMaRADzG + kQA8xpEAj97AANjW1gBBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NADHxMMA2d6UAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgALO9 + LAD///8AXtCkAFHMnQB/2bcAr+jSAN317AD+//8A9PPzAJ6ZmABGPDoAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AMC8uwDi5a0Ar7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAPX25ADw+/cA////APj17QDi1rIAy7V1ALSVOADFrWYA9vPoANbU + 0wBPRkMAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AGBYVQDY1dUA/vz4AP7++wDO1HcAr7kgAK+5IACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IAC6wz8A/v78AMy2eACwjy4AoHkEAKB4 + AwCgeAMAoHgDAKB4AwCqhh4A8OrYAM3KygBCODUAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABnX10A9fT0APXjtgDhrikA6sVmAP79 + +ADT2YUAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAOPn + sQDk2bgAoHgDAKB4AwCgeAMAoHgDAKB4AwCgeAMAoHgDAKB4AwCwjy0A/v37AIJ7eQBBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQASD88AOnn + 5wD04a8A3qcVAN6mEwDephMA6MFZAP789gDZ3pQAr7kgAK+5IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5 + IACvuSAAr7kgAK+5IAC8xEQA/v38ALSVOQCgeAMAoHgDAKB4AwCgeAMAoHgDAKB4AwCgeAMAoHgDAKB4 + AwDdz6YAz83MAEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NACWkI8A/fnuAOCsIgDephMA3qYTAN6mEwDephMA5rxNAP368QDe4qMAsLoiAK+5 + IACvuSAAr7kgAK+5IACvuSAAr7kgAK+5IACxuiQAzdR1APT14AD08OIAo30MAKB4AwCgeAMAoHgDAKB4 + AwCgeAMAoHgDAKB4AwCgeAMAoHgDAMGnWwD7+voARDo3AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0ANfV1ADx2ZsA3qYTAN6mEwDephMA3qYTAN6m + EwDephMA5bhCAPz47ADj5rAAsbokAK+5IACvuSAAr7kgAK+5IACyvCoA0tiCAPb35gDy/f8Aj+r/ANz5 + /wDq4ccAp4IWAKB4AwCgeAMAoHgDAKB4AwCgeAMAoHgDAKB4AwCgeAMAt5lBAP///wBPRkMAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQA+/r6AOrF + ZgDephMA3qYTAN6mEwDephMA3qYTAN6mEwDephMA47Q3APv15QDn6r0AsrsoAK+5IAC1vjEA19yQAPn6 + 7gDp+/8AfOf/ABTU/wAF0f8AGtX/AM72/wDw6tcAq4cgAKB4AwCgeAMAoHgDAKB4AwCgeAMAoHgDAKB4 + AwC7nkoA////AEg+OwBBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NAD///8A6MJcAN6mEwDephMA3qYTAN6mEwDephMA3qYTAN6mEwDephMA4rAuAPry + 3gDs7skA3OGeAPv89QDg+f8AbeT/AA3S/wAF0f8ABdH/AAXR/wAF0f8AEtP/ALzz/wD18OMAsI8uAKB4 + AwCgeAMAoHgDAKB4AwCgeAMAoHgDANG9hADj4uIAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0APDv7wDsy3YA3qYTAN6mEwDephMA3qYTAN6m + EwDephMA3qYTAN6mEwDephMA4a0mAP789wDX+P8AXuH/AAjS/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR + /wAF0f8ACtL/AKrv/wD59+8Atpg+AKB4AwCgeAMAoHgDAKB4AwCjfQwA9fHkAKKdmwBBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAw8C/APbm + uwDephMA3qYTAN6mEwDephMA3qYTAN6mEwDephMA3qYTAN6mEwDnvlIA+f7/AB/W/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AJLr/wD8+vYAvqJSAKB4AwCgeAMAoHgDANvM + oADu7e0ATkRCAEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NAB0bWsA///+AOa6SQDephMA3qYTAN6mEwDephMA3qYTAN6mEwDephMA3qYTAPbn + vgCk7v8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AHrm + /wD9/fwAxq1nAKeCFgDe0KgA+Pf3AG5nZQBBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NADBvr0A/PbmAOOyMwDephMA3qYTAN6m + EwDephMA3qYUAOS1OwDtzn0A/v/+ADHZ/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wAF0f8ABdH/AGPi/wD7/v8A/Pr2ANrZ2ABjW1kAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEpA + PgDNysoA/fnwAOzMeADluUUA7c59APXjtQD9+e4A////APXv/QD3/v8AB9H/AAXR/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AJfs/wDPzcwAQjg1AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEQ6NwCXkpAA8O/vAP/+/wDu4/wA1Lf3ALqM8wCgYu4An2DuAP// + /wAr2P8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR + /wAF0f8Ae+f/ANjV1QBBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NADMyskAyaX1AJVP + 7ACVT+wAlU/sAJVP7ACVT+wA9vD9AFrh/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wB/5/8A3NraAEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AOXk4wC8kfMAlU/sAJVP7ACVT+wAlU/sAJVP7ADiz/oAien/AAXR/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AJrs/wDOy8oAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQA2tnYAMml9QCVT+wAlU/sAJVP7ACVT+wAlU/sAM6u + 9gC48v8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR + /wAF0f8A2vj/AKmlowBBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NACsqKcA7uL8AJZQ + 7ACVT+wAlU/sAJVP7ACVT+wAuo3zAOb6/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wAF0f8ABdH/ADXa/wD///8AcWlnAEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AFpSTwD5+fkA0rT3AJVP7ACVT+wAlU/sAJVP7ACpb/AA////ABzV/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8AuvL/AODe3gBCODUAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AIB6eAD7+/sA6dr7AMCW9AC1hfIAxJ30APDl + /AD///8Ap+//AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAF0f8ABdH/AGrk + /wD///8Adm9tAEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AGZd + WwDBvr0A9fT0AP7+/gDj4eEAm5aUAG5mZAD4+PgAm+3/AAjS/wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR + /wAF0f8ABdH/AAXR/wBj4v8A/P7/AKqmpABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AHJraQD39vYAyvX/ADvb + /wAF0f8ABdH/AAXR/wAF0f8ABdH/AAXR/wAk1/8Aqu//AP///wCyrq0AQzk2AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AGBXVQDV0tIA////AND2/wCM6v8Aa+T/AGrk/wCF6f8Aw/T/AP3//wDx8PAAiYOBAEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NAB0bWsAura1AObl5QD+/v4A////APPy + 8gDNysoAkYyKAEpAPgBBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3NABBNzQAQTc0AEE3 + NABBNzQAQTc0AEE3NAA= + + + + 17, 17 + + + 239, 17 + + + + AAABAAIAICAAAAEAIACoEAAAJgAAAEBAAAABACAAKEIAAM4QAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQ + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs74AALO+ + AACzvgAJs74ANbO+AHmzvgCxs74A2rO+APSzvgD/s74A/bO+AO6zvgDPs74AobO+AGOzvgAjs74ABLO+ + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+ + AACzvgAIs74ARbO+AKKzvgDjs74A+7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A9rO+ + ANSzvgCFs74AKrO+AAGzvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+ + AACzvgAAs74AJbO+AJazvgDts74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP6zvgDZs74AbrO+AA+zvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACzvgAAs74AAbO+AEKzvgDJs74A/rO+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD3s74AobO+AB+zvgAAs74AAAAAAAAAAAAAAAAAAAAA + AAAAAAAAs74AALO+AAGzvgBOs74A3bO+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD+s74AtrO+ACWzvgAAs74AAAAA + AAAAAAAAAAAAALO+AACzvgAAs74AQLO+AN2zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74ApLO+ + AAqzvgAAAAAAAAAAAAAAAAAAs74AALO+ACOzvgDGs74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AO+zvgBqs74AA7O+AAAAAAAAAAAAALO+AACzvgAGs74AkrO+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgDns74AZ7O+AAazvgAAAAAAAAAAAAAAAAAAs74AALO+AC+zvgDIs74A37O+AN2zvgDds74A3bO+ + AN2zvgDds74A3bO+AN2zvgDds74A3bO+AN2zvgDds74A3bO+AN2zvgDds74A3bO+AN2zvgDds74A3bO+ + AN6zvgDbs74As7O+AEmzvgADs74AAAAAAAAAAAAAAAAAAAAAAACzvgAAs74ACrO+ABqzvgAas74AGrO+ + ABqzvgAas74AGrO+ABqzvgAas74AGrO+ABqzvgAas74AGrO+ABqzvgAas74AGrO+ABqzvgAas74AGrO+ + ABqzvgAas74AGrO+ABmzvgANs74AALO+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE3NABBNzQAQTc0PUE3 + NIFBNzSAQTc0f0E3NH9BNzR/QTc0f0E3NH9BNzR/QTc0f0E3NH9BNzR/QTc0f0E3NH9BNzR/QTc0f0E3 + NH9BNzSAQTc0gEE3NHpBNzRYQTc0HEE3NAFBNzQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQTc0AEE3 + NANBNzSZQTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NPhBNzTHQTc0Q0E3NABBNzQAAAAAAAAAAAAAAAAAAAAAAAAA + AABBNzQAQTc0EEE3NL5BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzTSQTc0KkE3NAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEE3NABBNzQYQTc010E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzR8QTc0AEE3 + NAAAAAAAAAAAAAAAAAAAAAAAQTc0AEE3NBtBNzTiQTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NKlBNzQJQTc0AAAAAAAAAAAAAAAAAAAAAABBNzQAQTc0G0E3NOJBNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0qUE3NAlBNzQAAAAAAAAAAAAAAAAAAAAAAEE3NABBNzQYQTc010E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzR8QTc0AEE3NAAAAAAAAAAAAAAAAAAAAAAAQTc0AEE3NBBBNzS+QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc00kE3NCpBNzQAAAAAAAAAAAAAAAAAAAAAAAAAAABBNzQAQTc0A0E3 + NJlBNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0+EE3NMhBNzRDQTc0AEE3NAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE3 + NABBNzQAQTc0PUE3NIFBNzSAQTc0f0E3NH9BNzR/QTc0f0E3NH9BNzR/QTc0f0E3NH9BNzR/QTc0f0E3 + NH9BNzR/QTc0f0E3NH9BNzSAQTc0gEE3NHpBNzRZQTc0HEE3NAFBNzQAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnR/QAJ0f0KCdH9GgnR/RoJ0f0aCdH9GgnR/RoJ0f0aCdH9GgnR + /RoJ0f0aCdH9GgnR/RoJ0f0aCdH9GgnR/RoJ0f0aCdH9GgnR/RoJ0f0aCdH9GgnR/RoJ0f0aCdH9GQnR + /Q0J0f0ACdH9AAAAAAAAAAAAAAAAAAAAAAAAAAAACdH9AAnR/S8J0f3ICdH93wnR/d0J0f3dCdH93QnR + /d0J0f3dCdH93QnR/d0J0f3dCdH93QnR/d0J0f3dCdH93QnR/d0J0f3dCdH93QnR/d0J0f3dCdH93QnR + /d4J0f3bCdH9swnR/UkJ0f0DCdH9AAAAAAAAAAAAAAAAAAAAAAAJ0f0ACdH9BgnR/ZIJ0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH95wnR/WcJ0f0GCdH9AAAAAAAAAAAAAAAAAAAAAAAJ0f0ACdH9IwnR + /cYJ0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH97wnR/WoJ0f0DCdH9AAAAAAAAAAAAAAAAAAnR + /QAJ0f0ACdH9QAnR/d0J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9pAnR/QoJ0f0AAAAAAAAA + AAAAAAAAAAAAAAnR/QAJ0f0BCdH9TgnR/d0J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/gnR/bYJ0f0lCdH9AAnR + /QAAAAAAAAAAAAAAAAAAAAAAAAAAAAnR/QAJ0f0BCdH9QgnR/ckJ0f3+CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/fcJ0f2hCdH9HwnR + /QAJ0f0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnR/QAJ0f0ACdH9JQnR/ZYJ0f3tCdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f4J0f3ZCdH9bgnR + /Q8J0f0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0f0ACdH9CAnR + /UUJ0f2iCdH94wnR/fsJ0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/fYJ0f3UCdH9hQnR + /SoJ0f0BCdH9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAJ0f0ACdH9AAnR/QkJ0f01CdH9eQnR/bEJ0f3aCdH99AnR/f8J0f39CdH97gnR/c8J0f2hCdH9YwnR + /SMJ0f0ECdH9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAH//gAAf/wAAD/wAAAf4AAAD+AAAAfAA + AAHgAAAD4AAAB+AAAB//////wAAAf4AAAH+AAAA/gAAAP4AAAB+AAAAfgAAAP4AAAD+AAAB/wAAAf/// + ///gAAAf4AAAB+AAAAPwAAAB+AAAAfgAAAP8AAAH/wAAD/+AAB//4AB/KAAAAEAAAACAAAAAAQAgAAAA + AAAAQAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAALO+AAOzvgA0s74AarO+AJmzvgC/s74A27O+APGzvgD/s74A/7O+AP+zvgD6s74A5rO+ + AMyzvgCos74Ae7O+AEizvgAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACzvgAJs74AVrO+AKyzvgDus74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/LO+AMWzvgB1s74AHwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+ACizvgCSs74A8rO+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP6zvgC6s74ATLO+AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs74AKLO+AKizvgD+s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgDVs74AULO+AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACzvgANs74Ai7O+APuzvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgDBs74AKwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACzvgA/s74A4LO+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+APqzvgB/s74AAwAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+ + AAGzvgCAs74A/rO+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AMGzvgAZAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAALO+AAqzvgCys74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A5LO+ + ADIAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+ABOzvgDIs74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgDys74AQgAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+AA+zvgDSs74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+APazvgBCAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+AAqzvgDJs74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A9rO+ + ADb///8A////AP///wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALO+ + AAGzvgCps74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgDW////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACzvgB5s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD3s74APP///wD///8A////AP///wD///8A////AP// + /wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAACzvgA2s74A+7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD6s74ATgAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAACzvgAHs74A2LO+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD2s74ASgAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAs74AfbO+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgDos74ANwAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAA + AAAAAAAAs74AGbO+APezvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+AP+zvgD/s74A/7O+ + AP+zvgD/s74A/7O+AP+zvgDGs74AGwAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AAAAAAAAAAAAAAAAALO+AIqzvgDws74A8LO+APCzvgDws74A8LO+APCzvgDws74A8LO+ + APCzvgDws74A8LO+APCzvgDws74A8LO+APCzvgDws74A8LO+APCzvgDws74A8LO+APCzvgDws74A8LO+ + APCzvgDws74A8LO+APCzvgDws74A8LO+APCzvgDws74A8LO+APCzvgDws74A8LO+APCzvgDws74A8LO+ + APCzvgDws74A8LO+APCzvgDws74A57O+ALizvgBds74AAgAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AAAAAABBNzTkQTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/kE3NOhBNzTGQTc0e0E3NB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wBBNzQkQTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT6QTc0h0E3 + NAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8AQTc0XkE3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzS9QTc0CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AEE3NI1BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NK0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wBBNzS2QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0WAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8AQTc01EE3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NNMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AEE3NOxBNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wBBNzT9QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NGMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AQTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AEE3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wBBNzT9QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NGMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AQTc07EE3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzQnAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AEE3 + NNRBNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzTTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wBBNzS2QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8AQTc0jUE3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0rQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AEE3NF5BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0vUE3NAoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wBBNzQkQTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT6QTc0iEE3 + NAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8AAAAAAEE3NORBNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3 + NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT/QTc0/0E3NP9BNzT+QTc06EE3 + NMZBNzR8QTc0HgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAA + AAAAAAAACdH9iQnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR + /fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR + /fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR/fAJ0f3wCdH98AnR + /fAJ0f3nCdH9uAnR/V0J0f0CAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AAAAAAAAAAAAAAAAAAnR/RkJ0f33CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9xgnR/RsAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAACdH9fQnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3oCdH9NwAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAnR + /QcJ0f3YCdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/fYJ0f1KAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AAAA + AAAAAAAAAAAAAAAAAAAAAAAACdH9NgnR/fsJ0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9+gnR/U4AAAAA////AP///wD///8A////AP// + /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0f14CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f33CdH9PP// + /wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdH9AQnR + /akJ0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/db///8A////AP///wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAJ0f0KCdH9yQnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/fYJ0f02////AP///wD///8A////AP///wD///8A////AP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnR/Q8J0f3RCdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/fYJ0f1CAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdH9EwnR + /cgJ0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/fIJ0f1CAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAJ0f0KCdH9sgnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /eQJ0f0yAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnR/QEJ0f2ACdH9/gnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/cEJ0f0ZAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnR + /T8J0f3gCdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9+gnR/X8J0f0DAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACdH9DQnR/YsJ0f37CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9wQnR/SsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdH9KAnR/agJ0f3+CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3VCdH9UAnR/QEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAACdH9KAnR/ZIJ0f3yCdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/gnR/boJ0f1MCdH9AQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdH9CQnR/VYJ0f2sCdH97gnR/f8J0f3/CdH9/wnR + /f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/f8J0f3/CdH9/wnR/fwJ0f3FCdH9dQnR + /R8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnR + /QMJ0f00CdH9agnR/ZkJ0f2/CdH92wnR/fEJ0f3/CdH9/wnR/f8J0f36CdH95gnR/cwJ0f2oCdH9ewnR + /UgJ0f0PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD////gAA///////wAAAf/////8AAAAP/////AAAAAP//// + wAAAAAf///+AAAAAAf///gAAAAAA///8AAAAAAB///gAAAAAAD//8AAAAAAAH//gAAAAAAAP/8AAAAAA + AA//wAAAAAAAD/+AAAAAAAAf/wAAAAAAAD//AAAAAAAAf/4AAAAAAAD//gAAAAAAAf////////////// + ////////////////////////////////////////+AAAAAAB///wAAAAAAB///AAAAAAAD//8AAAAAAA + P//wAAAAAAAf//AAAAAAAB//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AA + AAAAAA//8AAAAAAAH//wAAAAAAAf//AAAAAAAD//8AAAAAAAP//wAAAAAAB///gAAAAAAf////////// + //////////////////////////////////////////////4AAAAAAAH//gAAAAAAAP//AAAAAAAAf/8A + AAAAAAA//4AAAAAAAB//wAAAAAAAD//AAAAAAAAP/+AAAAAAAA//8AAAAAAAH//4AAAAAAA///wAAAAA + AH///gAAAAAA////gAAAAAH////AAAAAB/////AAAAAP/////AAAAD//////AAAB///////gAA///w== + + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs new file mode 100644 index 00000000..90eb44d5 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +using System.Windows.Forms; +using System; + +// uncomment to have the assembly loading to ask for (various) resources; various solutions: +// https://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl +// [assembly: NeutralResourcesLanguageAttribute("en-GB", UltimateResourceFallbackLocation.MainAssembly)] + +namespace EsOdbcDsnEditor +{ + public static class DsnEditorFactory + { + public static int DsnEditor( + bool onConnect, + string dsnIn, + DriverCallbackDelegate delegConnectionTest, + DriverCallbackDelegate delegSaveDsn) + { + Application.EnableVisualStyles(); + DsnEditorForm form = new DsnEditorForm(onConnect, dsnIn, delegConnectionTest, delegSaveDsn); + // instead of Application.Run(form): prevent STA apps from launching a new editor within the same thread + form.ShowDialog(); + var dsn = form.Builder.ToString(); + return dsn.Length; + } + } +} diff --git a/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj new file mode 100644 index 00000000..32fd350f --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {FAC0512C-E595-4BF4-ACB7-617611DF5715} + Library + EsOdbcDsnEditor + esdsnedt + v4.0 + 512 + true + + + + AnyCPU + true + full + false + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + TRACE + prompt + 4 + + + + + + true + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + false + + + + + + + + + + + Form + + + DSNEditorForm.cs + + + + + True + True + Resources.resx + + + DSNEditorForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs b/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs new file mode 100644 index 00000000..ce4c1190 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// uncomment to have the assembly loading to ask for (various) resources; various solutions: +// https://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl +// [assembly: NeutralResourcesLanguageAttribute("en-GB", UltimateResourceFallbackLocation.MainAssembly)] + +namespace EsOdbcDsnEditor +{ + public static class ExtensionMethods + { + public static string StripBraces(this string input) + { + if (input.StartsWith("{") && input.EndsWith("}")) + { + return input.Substring(1, input.Length - 2); + } + + return input; + } + } +} diff --git a/dsneditor/EsOdbcDsnEditor/ODBC.ico b/dsneditor/EsOdbcDsnEditor/ODBC.ico new file mode 100644 index 00000000..a3c48809 Binary files /dev/null and b/dsneditor/EsOdbcDsnEditor/ODBC.ico differ diff --git a/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs b/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5bd7a64f --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Elasticsearch DSN Editor")] +[assembly: AssemblyDescription("Elasticsearch DSN Editor for managing ODBC connection strings.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Elasticsearch B.V.")] +[assembly: AssemblyProduct("Elasticsearch ODBC Driver")] +[assembly: AssemblyCopyright("Copyright Elasticsearch B.V. ©")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fac0512c-e595-4bf4-acb7-617611df5715")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.2")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dsneditor/EsOdbcDsnEditor/Properties/Resources.Designer.cs b/dsneditor/EsOdbcDsnEditor/Properties/Resources.Designer.cs new file mode 100644 index 00000000..3c3f83dc --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace EsOdbcDsnEditor.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EsOdbcDsnEditor.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/dsneditor/EsOdbcDsnEditor/Properties/Resources.resx b/dsneditor/EsOdbcDsnEditor/Properties/Resources.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/Properties/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/header.bmp b/dsneditor/EsOdbcDsnEditor/header.bmp new file mode 100644 index 00000000..e4e15c28 Binary files /dev/null and b/dsneditor/EsOdbcDsnEditor/header.bmp differ diff --git a/dsneditor/EsOdbcDsnEditorLauncher/EsOdbcDsnEditorLauncher.csproj b/dsneditor/EsOdbcDsnEditorLauncher/EsOdbcDsnEditorLauncher.csproj new file mode 100644 index 00000000..ea18a2d3 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/EsOdbcDsnEditorLauncher.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {71BEBFF7-652E-4B26-9EC3-CAEF947D368C} + WinExe + EsOdbcDsnEditorLauncher + EsOdbcDsnEditorLauncher + v4.0 + 512 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + Form + + + Launcher.cs + + + + + Launcher.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {fac0512c-e595-4bf4-acb7-617611df5715} + EsOdbcDsnEditor + + + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs new file mode 100644 index 00000000..f359a7db --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +namespace EsOdbcDsnEditorLauncher +{ + partial class Launcher + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.LaunchButton = new System.Windows.Forms.Button(); + this.textLog = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // LaunchButton + // + this.LaunchButton.Location = new System.Drawing.Point(12, 12); + this.LaunchButton.Name = "LaunchButton"; + this.LaunchButton.Size = new System.Drawing.Size(619, 52); + this.LaunchButton.TabIndex = 0; + this.LaunchButton.Text = "Launch"; + this.LaunchButton.UseVisualStyleBackColor = true; + this.LaunchButton.Click += new System.EventHandler(this.LaunchButton_Click); + // + // textLog + // + this.textLog.Location = new System.Drawing.Point(12, 70); + this.textLog.Multiline = true; + this.textLog.Name = "textLog"; + this.textLog.Size = new System.Drawing.Size(619, 409); + this.textLog.TabIndex = 1; + // + // Launcher + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(643, 491); + this.Controls.Add(this.textLog); + this.Controls.Add(this.LaunchButton); + this.Name = "Launcher"; + this.Text = "DSN Launcher"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button LaunchButton; + private System.Windows.Forms.TextBox textLog; + } +} + diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs new file mode 100644 index 00000000..4bacfb02 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +using System; +using System.Threading; +using System.Windows.Forms; + +namespace EsOdbcDsnEditorLauncher +{ + public partial class Launcher : Form + { + public Launcher() + { + InitializeComponent(); + } + + private void LaunchButton_Click(object sender, EventArgs e) + { + bool onConnect = false; + string dsn = "driver={Elasticsearch Driver};Database=localhost;hostname=localhost;uid=elastic;pwd=pass!word1;secure=4"; + var form = new EsOdbcDsnEditor.DsnEditorForm(onConnect, dsn, ConnectTest, SaveDsn); + form.Show(); + } + + private int ConnectTest(string connectionString, ref string errorMessage, uint flags) + { + Thread.Sleep(5000); // Simulate a slow connection test + textLog.Text += "CONNECT. Connection String:" + connectionString + Environment.NewLine; + return 0; + } + + private int SaveDsn(string connectionString, ref string errorMessage, uint flags) + { + textLog.Text += "SAVE. Connection String:" + connectionString + Environment.NewLine; + //errorMessage = "ESODBC_DSN_EXISTS_ERROR"; + //return -1; + return 0; + } + } +} diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.resx b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.resx new file mode 100644 index 00000000..7080a7d1 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Program.cs b/dsneditor/EsOdbcDsnEditorLauncher/Program.cs new file mode 100644 index 00000000..3f9b6f76 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Program.cs @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +using System; +using System.Windows.Forms; + +namespace EsOdbcDsnEditorLauncher +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Launcher()); + } + } +} diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs b/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..62de8362 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Elasticsearch DSN Editor Launcher")] +[assembly: AssemblyDescription("Elasticsearch DSN Editor Launcher.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Elasticsearch B.V.")] +[assembly: AssemblyProduct("Elasticsearch ODBC Driver")] +[assembly: AssemblyCopyright("Copyright Elasticsearch B.V. ©")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("71bebff7-652e-4b26-9ec3-caef947d368c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.Designer.cs b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.Designer.cs new file mode 100644 index 00000000..e2ff9e97 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace EsOdbcDsnEditorLauncher.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EsOdbcDsnEditorLauncher.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.resx b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.Designer.cs b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.Designer.cs new file mode 100644 index 00000000..bf4ecf9d --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace EsOdbcDsnEditorLauncher.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.settings b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.settings new file mode 100644 index 00000000..39645652 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dsneditor/EsOdbcDsnEditorLauncher/app.config b/dsneditor/EsOdbcDsnEditorLauncher/app.config new file mode 100644 index 00000000..fcd0c937 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/app.config @@ -0,0 +1,3 @@ + + + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f0c0a3a8..ee4268fc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -54,7 +54,7 @@ include_directories(${CMAKE_SOURCE_DIR}/test ${GTEST_INC_PATH}) # find all test case source files file(GLOB TEST_CASES LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test_*.cc) -message("Test cases: ${TEST_CASES}") +message("Driver test cases: ${TEST_CASES}") set(EXTRA_SRC connected_dbc.cc) @@ -67,6 +67,9 @@ if (${LIBCURL_LINK_MODE} MATCHES dll) ${LIBCURL_LD_PATH}/libcurl${CMAKE_SHARED_LIBRARY_SUFFIX} SRC_PATH_CURL_DLL) endif (${LIBCURL_LINK_MODE} MATCHES dll) +file(TO_NATIVE_PATH + ${DSNBND_LIB_BIN_DIR_BASE}-$/*${CMAKE_SHARED_LIBRARY_SUFFIX} + SRC_PATH_DSNBND_DLL) file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR}/test/${CMAKE_CFG_INTDIR}/ DST_PATH_DLL) @@ -86,15 +89,21 @@ file(TO_NATIVE_PATH ${GTEST_LD_PATH}/gtest_maind${CMAKE_STATIC_LIBRARY_SUFFIX} add_custom_target(install_shared # dir's otherwise created only later on test target execution COMMAND if not exist ${DST_PATH_DLL} mkdir ${DST_PATH_DLL} - COMMAND xcopy /E /Y ${SRC_PATH_ESODBC_DLL} ${DST_PATH_DLL} - COMMAND if exist ${SRC_PATH_CURL_DLL} xcopy /E /Y ${SRC_PATH_CURL_DLL} ${DST_PATH_DLL} + COMMAND xcopy /E/Y/F ${SRC_PATH_ESODBC_DLL} ${DST_PATH_DLL} + COMMAND if exist ${SRC_PATH_CURL_DLL} + xcopy /E/Y/F ${SRC_PATH_CURL_DLL} ${DST_PATH_DLL} + COMMAND xcopy /E/Y/F ${SRC_PATH_DSNBND_DLL} ${DST_PATH_DLL} # gtest->gtestd hack # if googletest just built (i.e. not OS'es) AND the -d version exists COMMAND if exist ${GTEST_NATIVE_PREFIX} if exist ${GTEST_LIBD} - cd ${GTEST_LD_PATH} && copy gtestd${CMAKE_STATIC_LIBRARY_SUFFIX} gtest${CMAKE_STATIC_LIBRARY_SUFFIX} + cd ${GTEST_LD_PATH} && + copy /Y gtestd${CMAKE_STATIC_LIBRARY_SUFFIX} + gtest${CMAKE_STATIC_LIBRARY_SUFFIX} # (same checks as above) COMMAND if exist ${GTEST_NATIVE_PREFIX} if exist ${GTEST_MAIND} - cd ${GTEST_LD_PATH} && copy gtest_maind${CMAKE_STATIC_LIBRARY_SUFFIX} gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX} + cd ${GTEST_LD_PATH} && + copy /Y gtest_maind${CMAKE_STATIC_LIBRARY_SUFFIX} + gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX} ) foreach (TSRC ${TEST_CASES}) diff --git a/test/connected_dbc.cc b/test/connected_dbc.cc index d2c1113e..29677597 100644 --- a/test/connected_dbc.cc +++ b/test/connected_dbc.cc @@ -88,93 +88,113 @@ static const SQLWCHAR connect_string[] = L"Driver=ElasticODBC"; /* * Class will provide a "connected" DBC: the ES types are loaded. */ -ConnectedDBC::ConnectedDBC() { - SQLRETURN ret; - cstr_st types; +ConnectedDBC::ConnectedDBC() +{ + SQLRETURN ret; + cstr_st types; - ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); - assert(SQL_SUCCEEDED(ret)); + ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); + assert(SQL_SUCCEEDED(ret)); - ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, - (SQLPOINTER)SQL_OV_ODBC3_80, NULL); - assert(SQL_SUCCEEDED(ret)); + ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, + (SQLPOINTER)SQL_OV_ODBC3_80, NULL); + assert(SQL_SUCCEEDED(ret)); - ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); - assert(SQL_SUCCEEDED(ret)); + ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); + assert(SQL_SUCCEEDED(ret)); - types.cnt = sizeof(systypes_answer) - 1; - types.str = (SQLCHAR *)malloc(types.cnt); - assert(types.str != NULL); - memcpy(types.str, systypes_answer, types.cnt); + types.cnt = sizeof(systypes_answer) - 1; + types.str = (SQLCHAR *)malloc(types.cnt); + assert(types.str != NULL); + memcpy(types.str, systypes_answer, types.cnt); - ret = SQLDriverConnect(dbc, (SQLHWND)&types, (SQLWCHAR *)connect_string, - sizeof(connect_string) / sizeof(connect_string[0]) - 1, NULL, 0, NULL, - ESODBC_SQL_DRIVER_TEST); - assert(SQL_SUCCEEDED(ret)); + ret = SQLDriverConnect(dbc, (SQLHWND)&types, (SQLWCHAR *)connect_string, + sizeof(connect_string) / sizeof(connect_string[0]) - 1, NULL, 0, NULL, + ESODBC_SQL_DRIVER_TEST); + assert(SQL_SUCCEEDED(ret)); - ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt); - assert(SQL_SUCCEEDED(ret)); - assert(stmt != NULL); + ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt); + assert(SQL_SUCCEEDED(ret)); + assert(stmt != NULL); } -ConnectedDBC::~ConnectedDBC() { - SQLRETURN ret; +ConnectedDBC::~ConnectedDBC() +{ + SQLRETURN ret; - ret = SQLFreeHandle(SQL_HANDLE_STMT, stmt); - assert(SQL_SUCCEEDED(ret)); + ret = SQLFreeHandle(SQL_HANDLE_STMT, stmt); + assert(SQL_SUCCEEDED(ret)); - ret = SQLFreeHandle(SQL_HANDLE_DBC, dbc); - assert(SQL_SUCCEEDED(ret)); + ret = SQLFreeHandle(SQL_HANDLE_DBC, dbc); + assert(SQL_SUCCEEDED(ret)); - ret = SQLFreeHandle(SQL_HANDLE_ENV, env); - assert(SQL_SUCCEEDED(ret)); + ret = SQLFreeHandle(SQL_HANDLE_ENV, env); + assert(SQL_SUCCEEDED(ret)); } -void ConnectedDBC::assertState(const SQLWCHAR *state) { - SQLRETURN ret; - SQLWCHAR buff[SQL_SQLSTATE_SIZE+1] = {L'\0'}; - SQLSMALLINT len; - - ret = SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_SQLSTATE, buff, - (SQL_SQLSTATE_SIZE + 1) * sizeof(buff[0]), &len); - if (state) { - ASSERT_TRUE(SQL_SUCCEEDED(ret)); - ASSERT_EQ(len, SQL_SQLSTATE_SIZE * sizeof(buff[0])); - ASSERT_STREQ(buff, state); - } else { - ASSERT_EQ(ret, SQL_NO_DATA); - } +void ConnectedDBC::assertState(SQLSMALLINT htype, const SQLWCHAR *state) +{ + SQLHANDLE hnd; + SQLWCHAR buff[SQL_SQLSTATE_SIZE+1] = {L'\0'}; + SQLSMALLINT len; + + switch (htype) { + case SQL_HANDLE_STMT: hnd = stmt; break; + case SQL_HANDLE_DBC: hnd = dbc; break; + case SQL_HANDLE_ENV: hnd = env; break; + default: ASSERT_TRUE(FALSE); + } + ret = SQLGetDiagField(htype, hnd, 1, SQL_DIAG_SQLSTATE, buff, + (SQL_SQLSTATE_SIZE + 1) * sizeof(buff[0]), &len); + if (state) { + if (! wcscmp(state, MK_WPTR("00000"))) { + ASSERT_EQ(ret, SQL_NO_DATA); + } else { + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + ASSERT_EQ(len, SQL_SQLSTATE_SIZE * sizeof(buff[0])); + ASSERT_STREQ(buff, state); + } + } else { + ASSERT_EQ(ret, SQL_NO_DATA); + } +} +void ConnectedDBC::assertState(const SQLWCHAR *state) +{ + assertState(SQL_HANDLE_STMT, state); } -void ConnectedDBC::prepareStatement() { - const char *testName = - ::testing::UnitTest::GetInstance()->current_test_info()->name(); - size_t nameLen = strlen(testName); - std::wstring wstr(nameLen, L' '); - ASSERT_TRUE(mbstowcs(&wstr[0], testName, nameLen + 1) != (size_t)-1); +void ConnectedDBC::prepareStatement() +{ + const char *testName = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + size_t nameLen = strlen(testName); + std::wstring wstr(nameLen, L' '); + ASSERT_TRUE(mbstowcs(&wstr[0], testName, nameLen + 1) != (size_t)-1); - ret = ATTACH_SQL(stmt, &wstr[0], nameLen); - ASSERT_TRUE(SQL_SUCCEEDED(ret)); + ret = ATTACH_SQL(stmt, &wstr[0], nameLen); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); } void ConnectedDBC::prepareStatement(const SQLWCHAR *sql, - const char *jsonAnswer) { - ret = ATTACH_SQL(stmt, sql, wcslen(sql)); - ASSERT_TRUE(SQL_SUCCEEDED(ret)); - - char *answer = STRDUP(jsonAnswer); - ASSERT_TRUE(answer != NULL); - ret = ATTACH_ANSWER(stmt, answer, strlen(answer)); - ASSERT_TRUE(SQL_SUCCEEDED(ret)); + const char *jsonAnswer) +{ + ret = ATTACH_SQL(stmt, sql, wcslen(sql)); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + + char *answer = STRDUP(jsonAnswer); + ASSERT_TRUE(answer != NULL); + ret = ATTACH_ANSWER(stmt, answer, strlen(answer)); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); } -void ConnectedDBC::prepareStatement(const char *jsonAnswer) { - prepareStatement(); +void ConnectedDBC::prepareStatement(const char *jsonAnswer) +{ + prepareStatement(); - char *answer = STRDUP(jsonAnswer); - ASSERT_TRUE(answer != NULL); - ret = ATTACH_ANSWER(stmt, answer, strlen(answer)); - ASSERT_TRUE(SQL_SUCCEEDED(ret)); + char *answer = STRDUP(jsonAnswer); + ASSERT_TRUE(answer != NULL); + ret = ATTACH_ANSWER(stmt, answer, strlen(answer)); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); } diff --git a/test/connected_dbc.h b/test/connected_dbc.h index ea792c2b..f73fe329 100644 --- a/test/connected_dbc.h +++ b/test/connected_dbc.h @@ -37,23 +37,24 @@ extern "C" { class ConnectedDBC { - protected: - SQLHANDLE env, dbc, stmt; - SQLRETURN ret; - SQLLEN ind_len = SQL_NULL_DATA; + protected: + SQLHANDLE env, dbc, stmt; + SQLRETURN ret; + SQLLEN ind_len = SQL_NULL_DATA; - ConnectedDBC(); - virtual ~ConnectedDBC(); + ConnectedDBC(); + virtual ~ConnectedDBC(); - void assertState(const SQLWCHAR *state); + void assertState(const SQLWCHAR *state); + void assertState(SQLSMALLINT htype, const SQLWCHAR *state); - // use the test name as SQL (for faster logs lookup) - void prepareStatement(); - // use an actual SQL statement (if it might be processed) - void prepareStatement(const SQLWCHAR *sql, const char *jsonAnswer); - // use test name as SQL and attach given answer - void prepareStatement(const char *jsonAnswer); + // use the test name as SQL (for faster logs lookup) + void prepareStatement(); + // use an actual SQL statement (if it might be processed) + void prepareStatement(const SQLWCHAR *sql, const char *jsonAnswer); + // use test name as SQL and attach given answer + void prepareStatement(const char *jsonAnswer); }; #endif /* __CONNECTED_DBC_H__ */ diff --git a/test/test_dsn.cc b/test/test_dsn.cc new file mode 100644 index 00000000..f9903b0a --- /dev/null +++ b/test/test_dsn.cc @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +extern "C" { +#include "dsn.h" +} // extern C + +#include + + +namespace test { + +class Dsn : public ::testing::Test { +}; + +TEST_F(Dsn, parse_write_00_list) { +#undef SRC_STR +#define SRC_STR \ + "Driver={Elasticsearch Driver}\0" \ + "Description={Some description}\0" \ + "DSN=Data_Source_Name\0" \ + "PWD=password\0" \ + "UID=user_id\0" \ + "SAVEFILE=C:\\Temp\\Data_Source_Name.dsn\0" \ + "FILEDSN=C:\\Temp\\Data_Source_Name.dsn\0" \ + "Server=::1\0" \ + "Port=9200\0" \ + "Secure=4\0" \ + "CAPath=C:\\Temp\\Data_Source_Name.pem\0" \ + "Timeout=1000\0" \ + "Follow=1\0" \ + "Catalog=\0" \ + "Packing=JSON\0" \ + "MaxFetchSize=10000\0" \ + "MaxBodySizeMB=100\0" \ + "TraceFile=C:\\Temp\\Data_Source_Name.log\0" \ + "TraceLevel=DEBUG\0" \ + "\0" + + + esodbc_dsn_attrs_st attrs; + SQLWCHAR *src = MK_WPTR(SRC_STR); + SQLWCHAR dst[2 * sizeof(attrs.buff)/sizeof(*attrs.buff)]; + long written; + + init_dsn_attrs(&attrs); + ASSERT_TRUE(parse_00_list(&attrs, src)); + written = write_00_list(&attrs, dst, sizeof(dst)/sizeof(*dst)); + ASSERT_TRUE(0 < written); + + ASSERT_TRUE(memcmp(src, dst, written) == 0); +} + + +TEST_F(Dsn, parse_write_connection_string) { +#undef SRC_STR +#define SRC_STR \ + "Driver={Elasticsearch Driver};" \ + "Description={Some description};" \ + "DSN=Data_Source_Name;" \ + "PWD=password;" \ + "UID=user_id;" \ + "SAVEFILE=C:\\Temp\\Data_Source_Name.dsn;" \ + "FILEDSN=C:\\Temp\\Data_Source_Name.dsn;" \ + "Server=::1;" \ + "Port=9200;" \ + "Secure=4;" \ + "CAPath=C:\\Temp\\Data_Source_Name.pem;" \ + "Timeout=;" \ + "Follow=;" \ + "Catalog=;" \ + "Packing=JSON;" \ + "MaxFetchSize=10000;" \ + "MaxBodySizeMB=100;" \ + "TraceFile=C:\\Temp\\Data_Source_Name.log;" \ + "TraceLevel=DEBUG;" + + + esodbc_dsn_attrs_st attrs; + wstr_st src = WSTR_INIT(SRC_STR); + SQLWCHAR dst[2 * sizeof(attrs.buff)/sizeof(*attrs.buff)]; + long written; + + init_dsn_attrs(&attrs); + ASSERT_TRUE(parse_connection_string(&attrs, src.str, + (SQLSMALLINT)src.cnt)); + written = write_connection_string(&attrs, dst, + (SQLSMALLINT)sizeof(dst)/sizeof(*dst)); + ASSERT_TRUE(0 < written); + + ASSERT_TRUE(memcmp(src.str, dst, written) == 0); +} + +TEST_F(Dsn, write_connection_string_null_str_out) { +#undef SRC_STR +#define SRC_STR \ + "Driver={Elasticsearch Driver};" \ + "Description={Some description};" \ + "DSN=Data_Source_Name;" \ + "PWD=password;" \ + "UID=user_id;" \ + "SAVEFILE=C:\\Temp\\Data_Source_Name.dsn;" \ + "FILEDSN=C:\\Temp\\Data_Source_Name.dsn;" \ + "Server=::1;" \ + "Port=9200;" \ + "Secure=4;" \ + "CAPath=C:\\Temp\\Data_Source_Name.pem;" \ + "Timeout=;" \ + "Follow=;" \ + "Catalog=;" \ + "Packing=JSON;" \ + "MaxFetchSize=10000;" \ + "MaxBodySizeMB=100;" \ + "TraceFile=C:\\Temp\\Data_Source_Name.log;" \ + "TraceLevel=DEBUG;" + + + esodbc_dsn_attrs_st attrs; + wstr_st src = WSTR_INIT(SRC_STR); + SQLWCHAR dst[2 * sizeof(attrs.buff)/sizeof(*attrs.buff)]; + long written, counted; + + init_dsn_attrs(&attrs); + ASSERT_TRUE(parse_connection_string(&attrs, src.str, + (SQLSMALLINT)src.cnt)); + written = write_connection_string(&attrs, dst, + (SQLSMALLINT)sizeof(dst)/sizeof(*dst)); + ASSERT_TRUE(0 < written); + counted = write_connection_string(&attrs, NULL, 0); + ASSERT_EQ(written, counted); +} + + +} // test namespace + +/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ diff --git a/test/test_queries.cc b/test/test_queries.cc index 919646a3..a4c297cc 100644 --- a/test/test_queries.cc +++ b/test/test_queries.cc @@ -39,7 +39,6 @@ TEST_F(Queries, attach_error_sql) { \"status\": " STR(SRC_AID3) "\ }" - SQLRETURN ret; cstr_st body = CSTR_INIT(SRC_STR); SQLWCHAR *pos, *prev; @@ -74,7 +73,6 @@ TEST_F(Queries, attach_error_non_sql) { \"status\": " STR(SRC_AID3) "\ }" - SQLRETURN ret; cstr_st body = CSTR_INIT(SRC_STR); SQLWCHAR *pos, *prev; @@ -91,6 +89,33 @@ TEST_F(Queries, attach_error_non_sql) { ASSERT_EQ(HDRH(stmt)->diag.native_code, SRC_AID3); } +TEST_F(Queries, SQLNativeSql) { +#undef SRC_STR +#define SRC_STR "SELECT 1" + SQLWCHAR *src = MK_WPTR(SRC_STR); + SQLWCHAR buff[sizeof(SRC_STR)]; + SQLINTEGER written; + + ret = SQLNativeSql(dbc, src, SQL_NTSL, buff, sizeof(buff)/sizeof(*buff), + &written); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + assertState(SQL_HANDLE_DBC, NULL); + ASSERT_STREQ(buff, src); +} + +TEST_F(Queries, SQLNativeSql_truncate) { +#undef SRC_STR +#define SRC_STR "SELECT 1" + SQLWCHAR *src = MK_WPTR(SRC_STR); + SQLWCHAR buff[sizeof(SRC_STR) - /*0-term*/1]; + SQLINTEGER written; + + ret = SQLNativeSql(dbc, src, SQL_NTSL, buff, sizeof(buff)/sizeof(*buff), + &written); + ASSERT_TRUE(SQL_SUCCEEDED(ret)); + assertState(SQL_HANDLE_DBC, MK_WPTR("01004")); + ASSERT_TRUE(wcsncmp(buff, src, sizeof(buff)/sizeof(*buff) - 1) == 0); +} } // test namespace