From 91bf4b36d32318614963e3d0c16907e200e66a5d Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 28 Sep 2018 00:09:19 +0200 Subject: [PATCH 01/35] add integration with the .net installer - implement prompt_user_config() based on EsOdbcDsnEdit() entry point.; - implement the callbacks on the GUI actions; - implement DSN bundle serializing to 00-list (which is going to be the way to send/receive the DSN at interfacing point). - start a unit test on the 'dsn' module. --- CMakeLists.txt | 9 +- driver/connect.c | 48 +++++++-- driver/connect.h | 3 + driver/dsn.c | 275 ++++++++++++++++++++++++++++++++++------------- driver/dsn.h | 20 ++-- driver/error.h | 2 +- driver/handles.c | 195 ++++++++++++++++++--------------- driver/handles.h | 6 +- driver/info.c | 39 +++++-- driver/setup.c | 184 +++++++++++++++++-------------- test/test_dsn.cc | 99 +++++++++++++++++ 11 files changed, 610 insertions(+), 270 deletions(-) create mode 100644 test/test_dsn.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 794888c1..4e08e8aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -226,6 +226,8 @@ message("Driver include paths: " ${ODBC_INC} ${DRV_SRC_DIR} # generate resource file configure_file(${DRV_SRC_DIR}/driver.rc.cmake ${CMAKE_BINARY_DIR}/driver.rc) +link_directories(C:\\Users\\bpi\\AppData\\Local\\Temp\\esodbc) + # # finally, set destination library # @@ -235,9 +237,12 @@ add_library(${DRV_NAME} SHARED ${DRV_SRC} ${CMAKE_BINARY_DIR}/${DRV_NAME}.def target_compile_definitions(${DRV_NAME} PRIVATE "DRIVER_BUILD") include_directories(${ODBC_INC} ${DRV_SRC_DIR} ${LIBCURL_INC_PATH} - ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC}) + ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC} + C:\\Users\\bpi\\RnD\\elastic\\misc\\EsOdbcDsn\\EsOdbcDsnBinding) -target_link_libraries(${DRV_NAME} odbccp32 legacy_stdio_definitions libcurl) +target_link_libraries(${DRV_NAME} odbccp32 legacy_stdio_definitions libcurl + ## ) + esdsnbnd) # add testing project/target enable_testing() diff --git a/driver/connect.c b/driver/connect.c index b7b22a61..0f1bdfa9 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -821,7 +821,7 @@ void cleanup_dbc(esodbc_dbc_st *dbc) cleanup_curl(dbc); } -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; @@ -1540,6 +1540,36 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) return ret; } +static int receive_dsn_cb(void *arg, const wchar_t *list00, + wchar_t *err_out, size_t eo_max, unsigned int flags) +{ + esodbc_dsn_attrs_st *attrs = (esodbc_dsn_attrs_st *)arg; + + TRACE; + assert(arg); + + if (! list00) { + ERR("invalid NULL 00-list received."); + return ESODBC_DSN_ISNULL_ERROR; + } + +#if 0 + if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { +#else + if (! parse_connection_string(attrs, (SQLWCHAR *)list00, + (SQLSMALLINT)wcslen(list00))) { +#endif + ERR("failed to parse received 00-list."); + return ESODBC_DSN_INVALID_ERROR; + } + + // + // TODO: validate attrs vals FIXME + // + + return 0; +} + SQLRETURN EsSQLDriverConnectW ( SQLHDBC hdbc, @@ -1570,9 +1600,9 @@ 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); @@ -1655,6 +1685,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: @@ -1663,8 +1695,8 @@ 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."); RET_HDIAGS(dbc, SQL_STATE_IM008); @@ -1721,11 +1753,13 @@ 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; } } diff --git a/driver/connect.h b/driver/connect.h index f2072ca2..4b2730b3 100644 --- a/driver/connect.h +++ b/driver/connect.h @@ -9,11 +9,14 @@ #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 EsSQLDriverConnectW diff --git a/driver/dsn.c b/driver/dsn.c index dfbb83fc..c5e3860b 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -9,13 +9,15 @@ #include "dsn.h" #include "log.h" +#include "connect.h" +#include "info.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; @@ -272,7 +274,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) { @@ -302,7 +304,8 @@ BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, } if (! skip_ws(&pos, end, FALSE)) { - return FALSE; + continue; /* empty values are acceptable */ + //return FALSE; } if (! parse_token(TRUE, &pos, end, &value)) { @@ -323,7 +326,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 +346,97 @@ 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_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 +446,44 @@ 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))) { + msg_len /= sizeof(*buff); + + assert(pos <= eb_max); + res = swprintf(err_buff + pos, eb_max - pos, L"%.*s (%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 (res < msg_len) { + WARN("reached error buffer end (%zd) before copying all " + "errors.", eb_max); + break; + } + } + } + return pos; +} + /* * Checks if a DSN entry with the given name exists already. * Returns: @@ -503,6 +631,8 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *attrs, BOOL create_new) ".INI.", LWSTR(&attrs->dsn), LWSTR(&attrs->driver)); return FALSE; } + } else { + assert(0 < system_dsn_exists(&attrs->dsn)); } for (iter = &map[0]; iter->kw; iter ++) { @@ -522,28 +652,9 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *attrs, BOOL create_new) 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; @@ -553,7 +664,7 @@ BOOL write_connection_string(esodbc_dsn_attrs_st *attrs, char *kw; } *iter, map[] = { {&attrs->driver, ESODBC_DSN_DRIVER}, - /* Description */ + {&attrs->description, ESODBC_DSN_DESCRIPTION}, {&attrs->dsn, ESODBC_DSN_DSN}, {&attrs->pwd, ESODBC_DSN_PWD}, {&attrs->uid, ESODBC_DSN_UID}, @@ -598,23 +709,22 @@ BOOL write_connection_string(esodbc_dsn_attrs_st *attrs, ERRN("failed to outprint connection string (space " "left: %d; needed: %d).", cchConnStrOutMax - pos, iter->val->cnt); - return FALSE; + return -1; } else { pos += n; } } else { /* simply increment the counter, since the untruncated length - * need to be returned to the app */ + * needs to be returned to the app */ pos += iter->val->cnt + braces; } } } - *pcchConnStrOut = (SQLSMALLINT)pos; - DBG("Output connection string: `" LWPD "`; out len: %d.", szConnStrOut, pos); - return TRUE; + assert(pos < LONG_MAX); + return (long)pos; } BOOL assign_dsn_defaults(esodbc_dsn_attrs_st *attrs) @@ -792,60 +902,73 @@ 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) +static int test_connect(void *arg, const wchar_t *list00, + wchar_t *err_out, size_t eo_max, unsigned int _) { - static int attempts = 0; - - if (! hwndParent) { - INFO("no window handler provided -- configuration skipped."); - return 1; - } - 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; - } -#if 1 - if (assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), - &MK_WSTR("INFO"), FALSE) <= 0) { - return -1; - } + esodbc_dsn_attrs_st attrs; + esodbc_dbc_st dbc; + SQLRETURN res, r; + + assert(! arg); /* change santinel */ + if (! list00) { + ERR("invalid NULL 00-list received."); + return ESODBC_DSN_ISNULL_ERROR; + } + + init_dsn_attrs(&attrs); +#if 0 + if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { +#else + if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, + (SQLSMALLINT)wcslen(list00))) { #endif - if (1 < attempts ++) { - /* prevent infinite loops */ - return 0; + ERR("failed to parse received 00-list."); + return ESODBC_DSN_INVALID_ERROR; } - if (SQL_MAX_DSN_LENGTH < attrs->dsn.cnt) { - ERR("DSN name longer than max (%d).", SQL_MAX_DSN_LENGTH); + init_dbc(&dbc, NULL); + res = do_connect(&dbc, &attrs); + if (! SQL_SUCCEEDED(res)) { + r = 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(r)); + return ESODBC_DSN_GENERIC_ERROR; } - return 1; + return 0; } -// 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) +/* + * 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) { - if (! hwndParent) { - INFO("no window handler provided -- forcing overwrite."); - return 1; + int ret; + wchar_t list00[sizeof(attrs->buff)/sizeof(*attrs->buff)]; + +#if 0 + if (write_00_list(attrs, (SQLWCHAR *)list00, + sizeof(list00)/sizeof(*list00)) <= 0) { +#else + if (write_connection_string(attrs, (SQLWCHAR *)list00, + sizeof(list00)/sizeof(*list00)) <= 0) { +#endif + ERR("failed to serialize attributes into a 00-list."); + return FALSE; } - TRACE; - return 1; + + ret = EsOdbcDsnEdit(hwnd, on_conn, list00, &test_connect, NULL, + save_cb, attrs); + 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..1e58f898 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" @@ -59,25 +60,28 @@ typedef struct { SQLWCHAR buff[ESODBC_DSN_ATTRS_COUNT * ESODBC_DSN_MAX_ATTR_LEN]; } esodbc_dsn_attrs_st; -void init_dsn_attrs(esodbc_dsn_attrs_st *attrs); +void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs); BOOL 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); + 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 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); -BOOL prompt_user_config(HWND hwndParent, esodbc_dsn_attrs_st *attrs, - BOOL disable_nonconn); -int prompt_user_overwrite(HWND hwndParent, wstr_st *dsn); +size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max); +int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, + driver_callback_ft save_cb); #endif /* __DSN_H__ */ diff --git a/driver/error.h b/driver/error.h index 71d0f5d2..b13a0b01 100644 --- a/driver/error.h +++ b/driver/error.h @@ -448,7 +448,7 @@ SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, 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*/) \ +#define RET_DIAG(_d/*iag dest*/, _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*/) \ diff --git a/driver/handles.c b/driver/handles.c index 7e790432..ab4ceb98 100644 --- a/driver/handles.c +++ b/driver/handles.c @@ -55,6 +55,25 @@ static void init_hheader(esodbc_hhdr_st *hdr, SQLSMALLINT type, void *parent) hdr->parent = parent; } +/* + * "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, desc_type_et type, SQLSMALLINT alloc_type) { @@ -72,6 +91,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 +121,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 +192,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 +213,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 +244,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. */ - } - if (! OutputHandle) { - ERR("null output handle provided."); - RET_STATE(SQL_STATE_HY009); + *OutputHandle = (SQLHANDLE)malloc(sizeof(esodbc_env_st)); + if (*OutputHandle) { + init_env(*OutputHandle); + DBG("new Environment handle @0x%p.", *OutputHandle); } - *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 +260,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_DIAG(&HDRH(InputHandle)->diag, SQL_STATE_HY001, NULL, 0); + } else { + RET_STATE(SQL_STATE_HY001); + } + } else { + /* new handle has been init'ed */ + assert(HDRH(*OutputHandle)->type); + } return SQL_SUCCESS; } @@ -845,6 +860,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); diff --git a/driver/handles.h b/driver/handles.h index c57233c3..ca70d1f7 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -358,6 +358,8 @@ 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, @@ -467,10 +469,10 @@ SQLRETURN EsSQLSetDescRec( /* wraper of RET_CDIAG, compatible with any defined handle */ #define RET_HDIAG(_hp/*handle ptr*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \ - RET_CDIAG(&(_hp)->hdr.diag, _s, _t, _c) + RET_CDIAG(&HDRH(_hp)->diag, _s, _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) + RET_DIAG(&HDRH(_hp)->diag, _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 */ diff --git a/driver/info.c b/driver/info.c index 66b7f6da..8ee84fe8 100644 --- a/driver/info.c +++ b/driver/info.c @@ -885,6 +885,7 @@ SQLRETURN EsSQLGetDiagFieldW( esodbc_env_st dummy; SQLSMALLINT used; size_t len; + void *srcptr; wstr_st *wstrp, wstr; SQLRETURN ret; @@ -1035,11 +1036,37 @@ 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) { @@ -1143,7 +1170,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/setup.c b/driver/setup.c index ef6623f9..07aaccfc 100644 --- a/driver/setup.c +++ b/driver/setup.c @@ -29,7 +29,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 +38,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); @@ -118,6 +116,87 @@ BOOL SQL_API ConfigDriverW( # undef _DSN_END_MARKER } +static int save_dsn_cb(void *arg, const wchar_t *list00, + wchar_t *err_out, size_t eo_max, unsigned int flags) +{ + size_t cnt; + int res; + esodbc_dsn_attrs_st attrs; + BOOL create_new, remove_old = FALSE; + esodbc_dsn_attrs_st *old_attrs = (esodbc_dsn_attrs_st *)arg; + wstr_st old_dsn = old_attrs->dsn; + + if (! list00) { + ERR("invalid NULL 00-list received."); + return ESODBC_DSN_ISNULL_ERROR; + } + + init_dsn_attrs(&attrs); +#if 0 + if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { +#else + if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, + (SQLSMALLINT)wcslen(list00))) { +#endif + ERR("failed to parse received 00-list."); + return ESODBC_DSN_INVALID_ERROR; + } + + // + // TODO: validate attrs vals FIXME + // + + /* is it a brand new DSN name 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) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to check if DSN `" LWPDL "` already exists: " + LWPDL ".", LWSTR(&attrs.dsn), cnt, err_out); + goto err; + } else if (res) { + DBG("overwrite confirmed? %s!", flags & ESODBC_DSN_OVERWRITE_FLAG + ? "yes" : "no"); + if (! (flags & ESODBC_DSN_OVERWRITE_FLAG)) { + return ESODBC_DSN_EXISTS_ERROR; + } + } + /* if an old DSN exists, delete it */ + remove_old = !!old_dsn.cnt; + create_new = TRUE; + } else { + create_new = FALSE; + } + + /* create or update the DSN */ + if (! write_system_dsn(&attrs, create_new)) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to add DSN to the system: " LWPDL ".", cnt, err_out); + goto err; + } + + /* 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; +err: + SQLPostInstallerError(ODBC_ERROR_REQUEST_FAILED, NULL); + return ESODBC_DSN_GENERIC_ERROR; +} + + BOOL SQL_API ConfigDSNW( HWND hwndParent, WORD fRequest, @@ -126,23 +205,13 @@ 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); 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 +219,51 @@ BOOL SQL_API ConfigDSNW( (SQLWCHAR *)lpszDriver, wcslen(lpszDriver) }; + + /* 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 `" 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, "dphWW", 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/test/test_dsn.cc b/test/test_dsn.cc new file mode 100644 index 00000000..a435113a --- /dev/null +++ b/test/test_dsn.cc @@ -0,0 +1,99 @@ +/* + * 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, sizeof(dst)/sizeof(*dst)); + ASSERT_TRUE(0 < written); + + ASSERT_TRUE(memcmp(src.str, dst, written) == 0); +} + + +} // test namespace + +/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ From ac5ca5ff06c38e0915a654f715a7736b8f7d5f95 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 28 Sep 2018 14:36:34 +0200 Subject: [PATCH 02/35] add DSN config validation - check for minimum requirements when configuring a DSN (it's name and server location); also perform a DBC configuration with the data, to prevent incomplete data be saved; the connection could still fail later, though, this is just a consistency check with no reachability checks. - perform a connection test in handler when prompting from SQLDriverConnect(), to fail there, rather than later in the function when the error message will not be forwarded back to the user. --- driver/connect.c | 34 +++++++++++----- driver/connect.h | 1 + driver/dsn.c | 101 ++++++++++++++++++++--------------------------- driver/dsn.h | 6 ++- driver/setup.c | 25 +++++++++--- 5 files changed, 92 insertions(+), 75 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index 0f1bdfa9..55691e61 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -597,7 +597,7 @@ static SQLRETURN test_connect(esodbc_dbc_st *dbc) /* * 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; @@ -755,7 +755,7 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) return SQL_SUCCESS; err: if (state == SQL_STATE_HY000) { - RET_HDIAG(dbc, state, "invalid configuration parameter", 0); + RET_HDIAG(dbc, state, "invalid configuration parameters", 0); } RET_HDIAGS(dbc, state); } @@ -829,7 +829,7 @@ 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; } @@ -1544,6 +1544,9 @@ static int receive_dsn_cb(void *arg, const wchar_t *list00, wchar_t *err_out, size_t eo_max, unsigned int flags) { esodbc_dsn_attrs_st *attrs = (esodbc_dsn_attrs_st *)arg; + esodbc_dbc_st dbc; + SQLRETURN res; + int ret; TRACE; assert(arg); @@ -1553,21 +1556,34 @@ static int receive_dsn_cb(void *arg, const wchar_t *list00, return ESODBC_DSN_ISNULL_ERROR; } -#if 0 +#ifdef ESODBC_DSN_API_WITH_00_LIST if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { #else if (! parse_connection_string(attrs, (SQLWCHAR *)list00, (SQLSMALLINT)wcslen(list00))) { -#endif +#endif /* ESODBC_DSN_API_WITH_00_LIST */ ERR("failed to parse received 00-list."); return ESODBC_DSN_INVALID_ERROR; } - // - // TODO: validate attrs vals FIXME - // + /* try configure and 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. */ + init_dbc(&dbc, NULL); + res = do_connect(&dbc, attrs); + if (! SQL_SUCCEEDED(res)) { + res = 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(res)); + ret = ESODBC_DSN_GENERIC_ERROR; + } else { + ret = 0; + } - return 0; + cleanup_dbc(&dbc); + return ret; } SQLRETURN EsSQLDriverConnectW diff --git a/driver/connect.h b/driver/connect.h index 4b2730b3..210f7427 100644 --- a/driver/connect.h +++ b/driver/connect.h @@ -17,6 +17,7 @@ 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 diff --git a/driver/dsn.c b/driver/dsn.c index c5e3860b..2fbd51a8 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -305,7 +305,6 @@ BOOL TEST_API parse_connection_string(esodbc_dsn_attrs_st *attrs, if (! skip_ws(&pos, end, FALSE)) { continue; /* empty values are acceptable */ - //return FALSE; } if (! parse_token(TRUE, &pos, end, &value)) { @@ -475,7 +474,7 @@ size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max) } else { pos += (size_t)res; if (res < msg_len) { - WARN("reached error buffer end (%zd) before copying all " + WARN("reached error buffer end (%zu) before copying all " "errors.", eb_max); break; } @@ -512,13 +511,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) { @@ -583,10 +579,9 @@ 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; } @@ -727,60 +722,44 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, 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, + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &MK_WSTR(ESODBC_DEF_TRACE_LEVEL), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } + /*overwrite?*/FALSE); - return TRUE; + assert(0 <= res); } @@ -907,7 +886,8 @@ static int test_connect(void *arg, const wchar_t *list00, { esodbc_dsn_attrs_st attrs; esodbc_dbc_st dbc; - SQLRETURN res, r; + SQLRETURN res; + int ret; assert(! arg); /* change santinel */ if (! list00) { @@ -916,12 +896,12 @@ static int test_connect(void *arg, const wchar_t *list00, } init_dsn_attrs(&attrs); -#if 0 +#ifdef ESODBC_DSN_API_WITH_00_LIST if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { #else if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, (SQLSMALLINT)wcslen(list00))) { -#endif +#endif /* ESODBC_DSN_API_WITH_00_LIST */ ERR("failed to parse received 00-list."); return ESODBC_DSN_INVALID_ERROR; } @@ -929,15 +909,18 @@ static int test_connect(void *arg, const wchar_t *list00, init_dbc(&dbc, NULL); res = do_connect(&dbc, &attrs); if (! SQL_SUCCEEDED(res)) { - r = EsSQLGetDiagFieldW(SQL_HANDLE_DBC, &dbc, /*rec#*/1, + res = 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(r)); - return ESODBC_DSN_GENERIC_ERROR; + assert(SQL_SUCCEEDED(res)); + ret = ESODBC_DSN_GENERIC_ERROR; + } else { + ret = 0; } - return 0; + cleanup_dbc(&dbc); + return ret; } /* diff --git a/driver/dsn.h b/driver/dsn.h index 1e58f898..c9ac5ce3 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -61,7 +61,7 @@ typedef struct { } esodbc_dsn_attrs_st; void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs); -BOOL assign_dsn_defaults(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); @@ -83,6 +83,10 @@ size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max); int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, driver_callback_ft save_cb); +/* enable 00-list format (vs. connection string, `;`/`|`-separated) at the + * interface with the GUI API */ +//#define ESODBC_DSN_API_WITH_00_LIST + #endif /* __DSN_H__ */ diff --git a/driver/setup.c b/driver/setup.c index 07aaccfc..09ac8f5f 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" @@ -122,6 +123,7 @@ static int save_dsn_cb(void *arg, const wchar_t *list00, size_t cnt; int res; esodbc_dsn_attrs_st attrs; + esodbc_dbc_st dbc; BOOL create_new, remove_old = FALSE; esodbc_dsn_attrs_st *old_attrs = (esodbc_dsn_attrs_st *)arg; wstr_st old_dsn = old_attrs->dsn; @@ -132,19 +134,29 @@ static int save_dsn_cb(void *arg, const wchar_t *list00, } init_dsn_attrs(&attrs); -#if 0 +#ifdef ESODBC_DSN_API_WITH_00_LIST if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { #else if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, (SQLSMALLINT)wcslen(list00))) { -#endif +#endif /* ESODBC_DSN_API_WITH_00_LIST */ ERR("failed to parse received 00-list."); return ESODBC_DSN_INVALID_ERROR; } - // - // TODO: validate attrs vals FIXME - // + /* + * validate the DSN set + */ + if (! (attrs.dsn.cnt & attrs.server.cnt)) { + ERR("DSN name (" LWPDL ") and server address (" LWPDL ") must not be" + " empty.", LWSTR(&attrs.dsn), LWSTR(&attrs.server)); + return ESODBC_DSN_INVALID_ERROR; + } + init_dbc(&dbc, NULL); + if (! SQL_SUCCEEDED(config_dbc(&dbc, &attrs))) { + ERR("test DBC configuration failed."); + return ESODBC_DSN_INVALID_ERROR; + } /* is it a brand new DSN name or has the DSN name changed? */ DBG("old DSN: `" LWPDL "`, new DSN: `" LWPDL "`.", @@ -178,7 +190,8 @@ static int save_dsn_cb(void *arg, const wchar_t *list00, goto err; } - /* only remove old if new is succesfully created */ + /* only remove old if new is succesfully created (even though the + * documentation says otherwise). */ if (remove_old) { assert(old_dsn.cnt); if (! SQLRemoveDSNFromIniW(old_dsn.str)) { From 88d0da9e8077caae332a1e4d2ca82f0719f36bca Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 28 Sep 2018 00:09:19 +0200 Subject: [PATCH 03/35] add integration with the .net installer - implement prompt_user_config() based on EsOdbcDsnEdit() entry point.; - implement the callbacks on the GUI actions; - implement DSN bundle serializing to 00-list (which is going to be the way to send/receive the DSN at interfacing point). - start a unit test on the 'dsn' module. --- CMakeLists.txt | 7 +- driver/connect.c | 48 +++++++-- driver/connect.h | 3 + driver/dsn.c | 275 ++++++++++++++++++++++++++++++++++------------- driver/dsn.h | 20 ++-- driver/error.h | 2 +- driver/handles.c | 195 ++++++++++++++++++--------------- driver/handles.h | 6 +- driver/info.c | 39 +++++-- driver/setup.c | 184 +++++++++++++++++-------------- test/test_dsn.cc | 99 +++++++++++++++++ 11 files changed, 608 insertions(+), 270 deletions(-) create mode 100644 test/test_dsn.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index d45dc7e7..ce27c077 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,6 +267,8 @@ message("Driver include paths: " ${ODBC_INC} ${DRV_SRC_DIR} # generate resource file configure_file(${DRV_SRC_DIR}/driver.rc.cmake ${CMAKE_BINARY_DIR}/driver.rc) +link_directories(C:\\Users\\bpi\\AppData\\Local\\Temp\\esodbc) + # # finally, set destination library # @@ -276,10 +278,11 @@ add_library(${DRV_NAME} SHARED ${DRV_SRC} ${CMAKE_BINARY_DIR}/${DRV_NAME}.def target_compile_definitions(${DRV_NAME} PRIVATE "DRIVER_BUILD") include_directories(${ODBC_INC} ${DRV_SRC_DIR} ${LIBCURL_INC_PATH} - ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC}) + ${UJSON4C_INC} ${CTIMESTAMP_PATH_SRC} + C:\\Users\\bpi\\RnD\\elastic\\misc\\EsOdbcDsn\\EsOdbcDsnBinding) target_link_libraries(${DRV_NAME} odbccp32 legacy_stdio_definitions libcurl - ${LIBCURL_WIN_LIBS}) + ${LIBCURL_WIN_LIBS} esdsnbnd) # add testing project/target enable_testing() diff --git a/driver/connect.c b/driver/connect.c index 93f58eda..b80976b5 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -837,7 +837,7 @@ void cleanup_dbc(esodbc_dbc_st *dbc) cleanup_curl(dbc); } -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; @@ -1556,6 +1556,36 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) return ret; } +static int receive_dsn_cb(void *arg, const wchar_t *list00, + wchar_t *err_out, size_t eo_max, unsigned int flags) +{ + esodbc_dsn_attrs_st *attrs = (esodbc_dsn_attrs_st *)arg; + + TRACE; + assert(arg); + + if (! list00) { + ERR("invalid NULL 00-list received."); + return ESODBC_DSN_ISNULL_ERROR; + } + +#if 0 + if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { +#else + if (! parse_connection_string(attrs, (SQLWCHAR *)list00, + (SQLSMALLINT)wcslen(list00))) { +#endif + ERR("failed to parse received 00-list."); + return ESODBC_DSN_INVALID_ERROR; + } + + // + // TODO: validate attrs vals FIXME + // + + return 0; +} + SQLRETURN EsSQLDriverConnectW ( SQLHDBC hdbc, @@ -1586,9 +1616,9 @@ 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); @@ -1671,6 +1701,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,8 +1711,8 @@ 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."); RET_HDIAGS(dbc, SQL_STATE_IM008); @@ -1737,11 +1769,13 @@ 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; } } diff --git a/driver/connect.h b/driver/connect.h index f2072ca2..4b2730b3 100644 --- a/driver/connect.h +++ b/driver/connect.h @@ -9,11 +9,14 @@ #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 EsSQLDriverConnectW diff --git a/driver/dsn.c b/driver/dsn.c index dfbb83fc..c5e3860b 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -9,13 +9,15 @@ #include "dsn.h" #include "log.h" +#include "connect.h" +#include "info.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; @@ -272,7 +274,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) { @@ -302,7 +304,8 @@ BOOL parse_connection_string(esodbc_dsn_attrs_st *attrs, } if (! skip_ws(&pos, end, FALSE)) { - return FALSE; + continue; /* empty values are acceptable */ + //return FALSE; } if (! parse_token(TRUE, &pos, end, &value)) { @@ -323,7 +326,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 +346,97 @@ 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_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 +446,44 @@ 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))) { + msg_len /= sizeof(*buff); + + assert(pos <= eb_max); + res = swprintf(err_buff + pos, eb_max - pos, L"%.*s (%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 (res < msg_len) { + WARN("reached error buffer end (%zd) before copying all " + "errors.", eb_max); + break; + } + } + } + return pos; +} + /* * Checks if a DSN entry with the given name exists already. * Returns: @@ -503,6 +631,8 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *attrs, BOOL create_new) ".INI.", LWSTR(&attrs->dsn), LWSTR(&attrs->driver)); return FALSE; } + } else { + assert(0 < system_dsn_exists(&attrs->dsn)); } for (iter = &map[0]; iter->kw; iter ++) { @@ -522,28 +652,9 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *attrs, BOOL create_new) 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; @@ -553,7 +664,7 @@ BOOL write_connection_string(esodbc_dsn_attrs_st *attrs, char *kw; } *iter, map[] = { {&attrs->driver, ESODBC_DSN_DRIVER}, - /* Description */ + {&attrs->description, ESODBC_DSN_DESCRIPTION}, {&attrs->dsn, ESODBC_DSN_DSN}, {&attrs->pwd, ESODBC_DSN_PWD}, {&attrs->uid, ESODBC_DSN_UID}, @@ -598,23 +709,22 @@ BOOL write_connection_string(esodbc_dsn_attrs_st *attrs, ERRN("failed to outprint connection string (space " "left: %d; needed: %d).", cchConnStrOutMax - pos, iter->val->cnt); - return FALSE; + return -1; } else { pos += n; } } else { /* simply increment the counter, since the untruncated length - * need to be returned to the app */ + * needs to be returned to the app */ pos += iter->val->cnt + braces; } } } - *pcchConnStrOut = (SQLSMALLINT)pos; - DBG("Output connection string: `" LWPD "`; out len: %d.", szConnStrOut, pos); - return TRUE; + assert(pos < LONG_MAX); + return (long)pos; } BOOL assign_dsn_defaults(esodbc_dsn_attrs_st *attrs) @@ -792,60 +902,73 @@ 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) +static int test_connect(void *arg, const wchar_t *list00, + wchar_t *err_out, size_t eo_max, unsigned int _) { - static int attempts = 0; - - if (! hwndParent) { - INFO("no window handler provided -- configuration skipped."); - return 1; - } - 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; - } -#if 1 - if (assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), - &MK_WSTR("INFO"), FALSE) <= 0) { - return -1; - } + esodbc_dsn_attrs_st attrs; + esodbc_dbc_st dbc; + SQLRETURN res, r; + + assert(! arg); /* change santinel */ + if (! list00) { + ERR("invalid NULL 00-list received."); + return ESODBC_DSN_ISNULL_ERROR; + } + + init_dsn_attrs(&attrs); +#if 0 + if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { +#else + if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, + (SQLSMALLINT)wcslen(list00))) { #endif - if (1 < attempts ++) { - /* prevent infinite loops */ - return 0; + ERR("failed to parse received 00-list."); + return ESODBC_DSN_INVALID_ERROR; } - if (SQL_MAX_DSN_LENGTH < attrs->dsn.cnt) { - ERR("DSN name longer than max (%d).", SQL_MAX_DSN_LENGTH); + init_dbc(&dbc, NULL); + res = do_connect(&dbc, &attrs); + if (! SQL_SUCCEEDED(res)) { + r = 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(r)); + return ESODBC_DSN_GENERIC_ERROR; } - return 1; + return 0; } -// 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) +/* + * 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) { - if (! hwndParent) { - INFO("no window handler provided -- forcing overwrite."); - return 1; + int ret; + wchar_t list00[sizeof(attrs->buff)/sizeof(*attrs->buff)]; + +#if 0 + if (write_00_list(attrs, (SQLWCHAR *)list00, + sizeof(list00)/sizeof(*list00)) <= 0) { +#else + if (write_connection_string(attrs, (SQLWCHAR *)list00, + sizeof(list00)/sizeof(*list00)) <= 0) { +#endif + ERR("failed to serialize attributes into a 00-list."); + return FALSE; } - TRACE; - return 1; + + ret = EsOdbcDsnEdit(hwnd, on_conn, list00, &test_connect, NULL, + save_cb, attrs); + 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..1e58f898 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" @@ -59,25 +60,28 @@ typedef struct { SQLWCHAR buff[ESODBC_DSN_ATTRS_COUNT * ESODBC_DSN_MAX_ATTR_LEN]; } esodbc_dsn_attrs_st; -void init_dsn_attrs(esodbc_dsn_attrs_st *attrs); +void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs); BOOL 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); + 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 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); -BOOL prompt_user_config(HWND hwndParent, esodbc_dsn_attrs_st *attrs, - BOOL disable_nonconn); -int prompt_user_overwrite(HWND hwndParent, wstr_st *dsn); +size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max); +int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, + driver_callback_ft save_cb); #endif /* __DSN_H__ */ diff --git a/driver/error.h b/driver/error.h index 71d0f5d2..b13a0b01 100644 --- a/driver/error.h +++ b/driver/error.h @@ -448,7 +448,7 @@ SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, 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*/) \ +#define RET_DIAG(_d/*iag dest*/, _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*/) \ diff --git a/driver/handles.c b/driver/handles.c index 7e790432..ab4ceb98 100644 --- a/driver/handles.c +++ b/driver/handles.c @@ -55,6 +55,25 @@ static void init_hheader(esodbc_hhdr_st *hdr, SQLSMALLINT type, void *parent) hdr->parent = parent; } +/* + * "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, desc_type_et type, SQLSMALLINT alloc_type) { @@ -72,6 +91,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 +121,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 +192,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 +213,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 +244,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. */ - } - if (! OutputHandle) { - ERR("null output handle provided."); - RET_STATE(SQL_STATE_HY009); + *OutputHandle = (SQLHANDLE)malloc(sizeof(esodbc_env_st)); + if (*OutputHandle) { + init_env(*OutputHandle); + DBG("new Environment handle @0x%p.", *OutputHandle); } - *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 +260,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_DIAG(&HDRH(InputHandle)->diag, SQL_STATE_HY001, NULL, 0); + } else { + RET_STATE(SQL_STATE_HY001); + } + } else { + /* new handle has been init'ed */ + assert(HDRH(*OutputHandle)->type); + } return SQL_SUCCESS; } @@ -845,6 +860,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); diff --git a/driver/handles.h b/driver/handles.h index c57233c3..ca70d1f7 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -358,6 +358,8 @@ 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, @@ -467,10 +469,10 @@ SQLRETURN EsSQLSetDescRec( /* wraper of RET_CDIAG, compatible with any defined handle */ #define RET_HDIAG(_hp/*handle ptr*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \ - RET_CDIAG(&(_hp)->hdr.diag, _s, _t, _c) + RET_CDIAG(&HDRH(_hp)->diag, _s, _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) + RET_DIAG(&HDRH(_hp)->diag, _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 */ diff --git a/driver/info.c b/driver/info.c index 66b7f6da..8ee84fe8 100644 --- a/driver/info.c +++ b/driver/info.c @@ -885,6 +885,7 @@ SQLRETURN EsSQLGetDiagFieldW( esodbc_env_st dummy; SQLSMALLINT used; size_t len; + void *srcptr; wstr_st *wstrp, wstr; SQLRETURN ret; @@ -1035,11 +1036,37 @@ 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) { @@ -1143,7 +1170,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/setup.c b/driver/setup.c index ef6623f9..07aaccfc 100644 --- a/driver/setup.c +++ b/driver/setup.c @@ -29,7 +29,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 +38,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); @@ -118,6 +116,87 @@ BOOL SQL_API ConfigDriverW( # undef _DSN_END_MARKER } +static int save_dsn_cb(void *arg, const wchar_t *list00, + wchar_t *err_out, size_t eo_max, unsigned int flags) +{ + size_t cnt; + int res; + esodbc_dsn_attrs_st attrs; + BOOL create_new, remove_old = FALSE; + esodbc_dsn_attrs_st *old_attrs = (esodbc_dsn_attrs_st *)arg; + wstr_st old_dsn = old_attrs->dsn; + + if (! list00) { + ERR("invalid NULL 00-list received."); + return ESODBC_DSN_ISNULL_ERROR; + } + + init_dsn_attrs(&attrs); +#if 0 + if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { +#else + if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, + (SQLSMALLINT)wcslen(list00))) { +#endif + ERR("failed to parse received 00-list."); + return ESODBC_DSN_INVALID_ERROR; + } + + // + // TODO: validate attrs vals FIXME + // + + /* is it a brand new DSN name 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) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to check if DSN `" LWPDL "` already exists: " + LWPDL ".", LWSTR(&attrs.dsn), cnt, err_out); + goto err; + } else if (res) { + DBG("overwrite confirmed? %s!", flags & ESODBC_DSN_OVERWRITE_FLAG + ? "yes" : "no"); + if (! (flags & ESODBC_DSN_OVERWRITE_FLAG)) { + return ESODBC_DSN_EXISTS_ERROR; + } + } + /* if an old DSN exists, delete it */ + remove_old = !!old_dsn.cnt; + create_new = TRUE; + } else { + create_new = FALSE; + } + + /* create or update the DSN */ + if (! write_system_dsn(&attrs, create_new)) { + cnt = copy_installer_errors(err_out, eo_max); + ERR("failed to add DSN to the system: " LWPDL ".", cnt, err_out); + goto err; + } + + /* 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; +err: + SQLPostInstallerError(ODBC_ERROR_REQUEST_FAILED, NULL); + return ESODBC_DSN_GENERIC_ERROR; +} + + BOOL SQL_API ConfigDSNW( HWND hwndParent, WORD fRequest, @@ -126,23 +205,13 @@ 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); 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 +219,51 @@ BOOL SQL_API ConfigDSNW( (SQLWCHAR *)lpszDriver, wcslen(lpszDriver) }; + + /* 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 `" 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, "dphWW", 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/test/test_dsn.cc b/test/test_dsn.cc new file mode 100644 index 00000000..a435113a --- /dev/null +++ b/test/test_dsn.cc @@ -0,0 +1,99 @@ +/* + * 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, sizeof(dst)/sizeof(*dst)); + ASSERT_TRUE(0 < written); + + ASSERT_TRUE(memcmp(src.str, dst, written) == 0); +} + + +} // test namespace + +/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */ From 785b3669ce3e1409d20200ebcfbe54a0cd544857 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 28 Sep 2018 14:36:34 +0200 Subject: [PATCH 04/35] add DSN config validation - check for minimum requirements when configuring a DSN (it's name and server location); also perform a DBC configuration with the data, to prevent incomplete data be saved; the connection could still fail later, though, this is just a consistency check with no reachability checks. - perform a connection test in handler when prompting from SQLDriverConnect(), to fail there, rather than later in the function when the error message will not be forwarded back to the user. --- driver/connect.c | 34 +++++++++++----- driver/connect.h | 1 + driver/dsn.c | 101 ++++++++++++++++++++--------------------------- driver/dsn.h | 6 ++- driver/setup.c | 25 +++++++++--- 5 files changed, 92 insertions(+), 75 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index b80976b5..6de2ff64 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -613,7 +613,7 @@ static SQLRETURN test_connect(esodbc_dbc_st *dbc) /* * 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; @@ -771,7 +771,7 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) return SQL_SUCCESS; err: if (state == SQL_STATE_HY000) { - RET_HDIAG(dbc, state, "invalid configuration parameter", 0); + RET_HDIAG(dbc, state, "invalid configuration parameters", 0); } RET_HDIAGS(dbc, state); } @@ -845,7 +845,7 @@ 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; } @@ -1560,6 +1560,9 @@ static int receive_dsn_cb(void *arg, const wchar_t *list00, wchar_t *err_out, size_t eo_max, unsigned int flags) { esodbc_dsn_attrs_st *attrs = (esodbc_dsn_attrs_st *)arg; + esodbc_dbc_st dbc; + SQLRETURN res; + int ret; TRACE; assert(arg); @@ -1569,21 +1572,34 @@ static int receive_dsn_cb(void *arg, const wchar_t *list00, return ESODBC_DSN_ISNULL_ERROR; } -#if 0 +#ifdef ESODBC_DSN_API_WITH_00_LIST if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { #else if (! parse_connection_string(attrs, (SQLWCHAR *)list00, (SQLSMALLINT)wcslen(list00))) { -#endif +#endif /* ESODBC_DSN_API_WITH_00_LIST */ ERR("failed to parse received 00-list."); return ESODBC_DSN_INVALID_ERROR; } - // - // TODO: validate attrs vals FIXME - // + /* try configure and 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. */ + init_dbc(&dbc, NULL); + res = do_connect(&dbc, attrs); + if (! SQL_SUCCEEDED(res)) { + res = 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(res)); + ret = ESODBC_DSN_GENERIC_ERROR; + } else { + ret = 0; + } - return 0; + cleanup_dbc(&dbc); + return ret; } SQLRETURN EsSQLDriverConnectW diff --git a/driver/connect.h b/driver/connect.h index 4b2730b3..210f7427 100644 --- a/driver/connect.h +++ b/driver/connect.h @@ -17,6 +17,7 @@ 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 diff --git a/driver/dsn.c b/driver/dsn.c index c5e3860b..2fbd51a8 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -305,7 +305,6 @@ BOOL TEST_API parse_connection_string(esodbc_dsn_attrs_st *attrs, if (! skip_ws(&pos, end, FALSE)) { continue; /* empty values are acceptable */ - //return FALSE; } if (! parse_token(TRUE, &pos, end, &value)) { @@ -475,7 +474,7 @@ size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max) } else { pos += (size_t)res; if (res < msg_len) { - WARN("reached error buffer end (%zd) before copying all " + WARN("reached error buffer end (%zu) before copying all " "errors.", eb_max); break; } @@ -512,13 +511,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) { @@ -583,10 +579,9 @@ 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; } @@ -727,60 +722,44 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, 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, + res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &MK_WSTR(ESODBC_DEF_TRACE_LEVEL), - /*overwrite?*/FALSE) < 0) { - return FALSE; - } + /*overwrite?*/FALSE); - return TRUE; + assert(0 <= res); } @@ -907,7 +886,8 @@ static int test_connect(void *arg, const wchar_t *list00, { esodbc_dsn_attrs_st attrs; esodbc_dbc_st dbc; - SQLRETURN res, r; + SQLRETURN res; + int ret; assert(! arg); /* change santinel */ if (! list00) { @@ -916,12 +896,12 @@ static int test_connect(void *arg, const wchar_t *list00, } init_dsn_attrs(&attrs); -#if 0 +#ifdef ESODBC_DSN_API_WITH_00_LIST if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { #else if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, (SQLSMALLINT)wcslen(list00))) { -#endif +#endif /* ESODBC_DSN_API_WITH_00_LIST */ ERR("failed to parse received 00-list."); return ESODBC_DSN_INVALID_ERROR; } @@ -929,15 +909,18 @@ static int test_connect(void *arg, const wchar_t *list00, init_dbc(&dbc, NULL); res = do_connect(&dbc, &attrs); if (! SQL_SUCCEEDED(res)) { - r = EsSQLGetDiagFieldW(SQL_HANDLE_DBC, &dbc, /*rec#*/1, + res = 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(r)); - return ESODBC_DSN_GENERIC_ERROR; + assert(SQL_SUCCEEDED(res)); + ret = ESODBC_DSN_GENERIC_ERROR; + } else { + ret = 0; } - return 0; + cleanup_dbc(&dbc); + return ret; } /* diff --git a/driver/dsn.h b/driver/dsn.h index 1e58f898..c9ac5ce3 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -61,7 +61,7 @@ typedef struct { } esodbc_dsn_attrs_st; void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs); -BOOL assign_dsn_defaults(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); @@ -83,6 +83,10 @@ size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max); int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, driver_callback_ft save_cb); +/* enable 00-list format (vs. connection string, `;`/`|`-separated) at the + * interface with the GUI API */ +//#define ESODBC_DSN_API_WITH_00_LIST + #endif /* __DSN_H__ */ diff --git a/driver/setup.c b/driver/setup.c index 07aaccfc..09ac8f5f 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" @@ -122,6 +123,7 @@ static int save_dsn_cb(void *arg, const wchar_t *list00, size_t cnt; int res; esodbc_dsn_attrs_st attrs; + esodbc_dbc_st dbc; BOOL create_new, remove_old = FALSE; esodbc_dsn_attrs_st *old_attrs = (esodbc_dsn_attrs_st *)arg; wstr_st old_dsn = old_attrs->dsn; @@ -132,19 +134,29 @@ static int save_dsn_cb(void *arg, const wchar_t *list00, } init_dsn_attrs(&attrs); -#if 0 +#ifdef ESODBC_DSN_API_WITH_00_LIST if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { #else if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, (SQLSMALLINT)wcslen(list00))) { -#endif +#endif /* ESODBC_DSN_API_WITH_00_LIST */ ERR("failed to parse received 00-list."); return ESODBC_DSN_INVALID_ERROR; } - // - // TODO: validate attrs vals FIXME - // + /* + * validate the DSN set + */ + if (! (attrs.dsn.cnt & attrs.server.cnt)) { + ERR("DSN name (" LWPDL ") and server address (" LWPDL ") must not be" + " empty.", LWSTR(&attrs.dsn), LWSTR(&attrs.server)); + return ESODBC_DSN_INVALID_ERROR; + } + init_dbc(&dbc, NULL); + if (! SQL_SUCCEEDED(config_dbc(&dbc, &attrs))) { + ERR("test DBC configuration failed."); + return ESODBC_DSN_INVALID_ERROR; + } /* is it a brand new DSN name or has the DSN name changed? */ DBG("old DSN: `" LWPDL "`, new DSN: `" LWPDL "`.", @@ -178,7 +190,8 @@ static int save_dsn_cb(void *arg, const wchar_t *list00, goto err; } - /* only remove old if new is succesfully created */ + /* only remove old if new is succesfully created (even though the + * documentation says otherwise). */ if (remove_old) { assert(old_dsn.cnt); if (! SQLRemoveDSNFromIniW(old_dsn.str)) { From 80880f65492d168e1b8af2c0c4faeaf4a713ed5c Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Wed, 3 Oct 2018 23:05:13 +0200 Subject: [PATCH 05/35] Fix installer error reporing - documentation indicates that last parameter returns number of written bytes, but it's actually characters. Also: - rename 'list00' paramter to 'dsn_str' since the GUI-driver interface will now pass connection strings (since .net framework has a parser for it); - add more DSN validation and fill in the default values for attributs not provided by user. --- driver/connect.c | 31 ++++++++++++++++++++++--------- driver/dsn.c | 43 ++++++++++++++++++++++++++----------------- driver/dsn.h | 4 ++-- driver/setup.c | 29 +++++++++++++++++------------ 4 files changed, 67 insertions(+), 40 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index 6de2ff64..ddcf2647 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -116,8 +116,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); @@ -1556,7 +1556,7 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) return ret; } -static int receive_dsn_cb(void *arg, const wchar_t *list00, +static int receive_dsn_cb(void *arg, const wchar_t *dsn_str, wchar_t *err_out, size_t eo_max, unsigned int flags) { esodbc_dsn_attrs_st *attrs = (esodbc_dsn_attrs_st *)arg; @@ -1567,19 +1567,32 @@ static int receive_dsn_cb(void *arg, const wchar_t *list00, TRACE; assert(arg); - if (! list00) { - ERR("invalid NULL 00-list received."); + if (! dsn_str) { + ERR("invalid NULL DSN string received."); return ESODBC_DSN_ISNULL_ERROR; + } else { + DBG("received DSN string: `" LWPD "`.", dsn_str); } #ifdef ESODBC_DSN_API_WITH_00_LIST - if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { + if (! parse_00_list(attrs, (SQLWCHAR *)dsn_str)) { #else - if (! parse_connection_string(attrs, (SQLWCHAR *)list00, - (SQLSMALLINT)wcslen(list00))) { + if (! parse_connection_string(attrs, (SQLWCHAR *)dsn_str, + (SQLSMALLINT)wcslen(dsn_str))) { #endif /* ESODBC_DSN_API_WITH_00_LIST */ - ERR("failed to parse received 00-list."); + ERR("failed to parse received DSN string."); + return ESODBC_DSN_INVALID_ERROR; + } + /* + * validate the DSN set + */ + if (! (attrs->dsn.cnt & attrs->server.cnt)) { + ERR("DSN name (" LWPDL ") and server address (" LWPDL ") cannot be" + " empty.", LWSTR(&attrs->dsn), LWSTR(&attrs->server)); return ESODBC_DSN_INVALID_ERROR; + } else { + /* fill in whatever's missing */ + assign_dsn_defaults(attrs); } /* try configure and connect here, to be able to report an error back to diff --git a/driver/dsn.c b/driver/dsn.c index 2fbd51a8..2bbb75e0 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -462,10 +462,15 @@ size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max) pos = 0; while (SQL_SUCCEEDED(SQLInstallerError(++ i, &ecode, buff, sizeof(buff)/sizeof(buff[0]), &msg_len))) { - msg_len /= sizeof(*buff); + /* 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); - res = swprintf(err_buff + pos, eb_max - pos, L"%.*s (%d).\n", + 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, @@ -473,7 +478,7 @@ size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max) /* allow execution to continue, though */ } else { pos += (size_t)res; - if (res < msg_len) { + if (pos <= msg_len) { WARN("reached error buffer end (%zu) before copying all " "errors.", eb_max); break; @@ -881,7 +886,7 @@ BOOL read_system_info(esodbc_dsn_attrs_st *attrs) #error "unsupported platform" /* TODO */ #endif /* defined(_WIN32) || defined (WIN32) */ -static int test_connect(void *arg, const wchar_t *list00, +static int test_connect(void *arg, const wchar_t *dsn_str, wchar_t *err_out, size_t eo_max, unsigned int _) { esodbc_dsn_attrs_st attrs; @@ -890,21 +895,25 @@ static int test_connect(void *arg, const wchar_t *list00, int ret; assert(! arg); /* change santinel */ - if (! list00) { - ERR("invalid NULL 00-list received."); + if (! dsn_str) { + ERR("invalid NULL DSN string received."); return ESODBC_DSN_ISNULL_ERROR; + } else { + DBG("received DSN string: `" LWPD "`.", dsn_str); } init_dsn_attrs(&attrs); #ifdef ESODBC_DSN_API_WITH_00_LIST - if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { + if (! parse_00_list(&attrs, (SQLWCHAR *)dsn_str)) { #else - if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, - (SQLSMALLINT)wcslen(list00))) { + if (! parse_connection_string(&attrs, (SQLWCHAR *)dsn_str, + (SQLSMALLINT)wcslen(dsn_str))) { #endif /* ESODBC_DSN_API_WITH_00_LIST */ - ERR("failed to parse received 00-list."); + ERR("failed to parse received DSN string."); return ESODBC_DSN_INVALID_ERROR; } + /* fill in whatever's missing */ + assign_dsn_defaults(&attrs); init_dbc(&dbc, NULL); res = do_connect(&dbc, &attrs); @@ -933,20 +942,20 @@ int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, driver_callback_ft save_cb) { int ret; - wchar_t list00[sizeof(attrs->buff)/sizeof(*attrs->buff)]; + wchar_t dsn_str[sizeof(attrs->buff)/sizeof(*attrs->buff)]; #if 0 - if (write_00_list(attrs, (SQLWCHAR *)list00, - sizeof(list00)/sizeof(*list00)) <= 0) { + if (write_00_list(attrs, (SQLWCHAR *)dsn_str, + sizeof(dsn_str)/sizeof(*dsn_str)) <= 0) { #else - if (write_connection_string(attrs, (SQLWCHAR *)list00, - sizeof(list00)/sizeof(*list00)) <= 0) { + if (write_connection_string(attrs, (SQLWCHAR *)dsn_str, + sizeof(dsn_str)/sizeof(*dsn_str)) <= 0) { #endif - ERR("failed to serialize attributes into a 00-list."); + ERR("failed to serialize attributes into a DSN string."); return FALSE; } - ret = EsOdbcDsnEdit(hwnd, on_conn, list00, &test_connect, NULL, + ret = EsOdbcDsnEdit(hwnd, on_conn, dsn_str, &test_connect, NULL, save_cb, attrs); if (ret < 0) { ERR("failed to bring up the GUI; code:%d.", ret); diff --git a/driver/dsn.h b/driver/dsn.h index c9ac5ce3..b2c7ce84 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -83,8 +83,8 @@ size_t copy_installer_errors(wchar_t *err_buff, size_t eb_max); int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, driver_callback_ft save_cb); -/* enable 00-list format (vs. connection string, `;`/`|`-separated) at the - * interface with the GUI API */ +/* Uncomment to enable 00-list format (vs. connection string, + * `;`/`|`-separated) at the interface with the GUI API */ //#define ESODBC_DSN_API_WITH_00_LIST #endif /* __DSN_H__ */ diff --git a/driver/setup.c b/driver/setup.c index 09ac8f5f..0a74bec2 100644 --- a/driver/setup.c +++ b/driver/setup.c @@ -117,7 +117,7 @@ BOOL SQL_API ConfigDriverW( # undef _DSN_END_MARKER } -static int save_dsn_cb(void *arg, const wchar_t *list00, +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; @@ -128,30 +128,35 @@ static int save_dsn_cb(void *arg, const wchar_t *list00, esodbc_dsn_attrs_st *old_attrs = (esodbc_dsn_attrs_st *)arg; wstr_st old_dsn = old_attrs->dsn; - if (! list00) { - ERR("invalid NULL 00-list received."); + if (! dsn_str) { + ERR("invalid NULL DSN string received."); return ESODBC_DSN_ISNULL_ERROR; + } else { + DBG("received DSN string: `" LWPD "`.", dsn_str); } init_dsn_attrs(&attrs); #ifdef ESODBC_DSN_API_WITH_00_LIST - if (! parse_00_list(&attrs, (SQLWCHAR *)list00)) { + if (! parse_00_list(&attrs, (SQLWCHAR *)dsn_str)) { #else - if (! parse_connection_string(&attrs, (SQLWCHAR *)list00, - (SQLSMALLINT)wcslen(list00))) { + if (! parse_connection_string(&attrs, (SQLWCHAR *)dsn_str, + (SQLSMALLINT)wcslen(dsn_str))) { #endif /* ESODBC_DSN_API_WITH_00_LIST */ - ERR("failed to parse received 00-list."); + ERR("failed to parse received DSN string."); return ESODBC_DSN_INVALID_ERROR; } - - /* - * validate the DSN set + /* + * validate the DSN set */ if (! (attrs.dsn.cnt & attrs.server.cnt)) { - ERR("DSN name (" LWPDL ") and server address (" LWPDL ") must not be" - " empty.", LWSTR(&attrs.dsn), LWSTR(&attrs.server)); + ERR("DSN name (" LWPDL ") and server address (" LWPDL ") cannot be" + " empty.", LWSTR(&attrs.dsn), LWSTR(&attrs.server)); return ESODBC_DSN_INVALID_ERROR; + } else { + /* fill in whatever's missing */ + assign_dsn_defaults(&attrs); } + init_dbc(&dbc, NULL); if (! SQL_SUCCEEDED(config_dbc(&dbc, &attrs))) { ERR("test DBC configuration failed."); From 3adba710e71e43a8098ae92792f4d418bd9602fc Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Wed, 3 Oct 2018 23:09:40 +0200 Subject: [PATCH 06/35] Add an initial "simplistic" DSN editor. - Add an editor that is able to edit the DSN as a connection string. - Add it into CMakeLists.txt as a "custom" target, that is also target-aware. - change the build.bat to "recognize" on subsequent invocations the initially set build type. --- CMakeLists.txt | 108 ++++++--- build.bat | 59 +++-- dsneditor/EsOdbcDsn.sln | 49 ++++ .../EsOdbcDsnBinding/EsOdbcDsnBinding.cpp | Bin 0 -> 11470 bytes dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h | 68 ++++++ .../EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj | 195 ++++++++++++++++ .../EsOdbcDsnBinding.vcxproj.filters | 27 +++ dsneditor/EsOdbcDsnEditor/App.config | 6 + .../EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 114 +++++++++ dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 144 ++++++++++++ dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx | 220 ++++++++++++++++++ .../EsOdbcDsnEditor/EsOdbcDsnEditor.csproj | 110 +++++++++ .../Properties/AssemblyInfo.cs | 36 +++ .../Properties/Resources.Designer.cs | 63 +++++ .../EsOdbcDsnEditor/Properties/Resources.resx | 117 ++++++++++ .../Properties/Settings.Designer.cs | 26 +++ dsneditor/EsOdbcDsnEditor/SQL_Icon.ico | Bin 0 -> 5696 bytes test/CMakeLists.txt | 2 +- 18 files changed, 1293 insertions(+), 51 deletions(-) create mode 100644 dsneditor/EsOdbcDsn.sln create mode 100644 dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp create mode 100644 dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h create mode 100644 dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj create mode 100644 dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters create mode 100644 dsneditor/EsOdbcDsnEditor/App.config create mode 100644 dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs create mode 100644 dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs create mode 100644 dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx create mode 100644 dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj create mode 100644 dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs create mode 100644 dsneditor/EsOdbcDsnEditor/Properties/Resources.Designer.cs create mode 100644 dsneditor/EsOdbcDsnEditor/Properties/Resources.resx create mode 100644 dsneditor/EsOdbcDsnEditor/Properties/Settings.Designer.cs create mode 100644 dsneditor/EsOdbcDsnEditor/SQL_Icon.ico diff --git a/CMakeLists.txt b/CMakeLists.txt index ce27c077..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,44 +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) - -link_directories(C:\\Users\\bpi\\AppData\\Local\\Temp\\esodbc) # # 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} - C:\\Users\\bpi\\RnD\\elastic\\misc\\EsOdbcDsn\\EsOdbcDsnBinding) + ${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} esdsnbnd) -# 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. @@ -304,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) @@ -320,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..5cb4c9a8 100644 --- a/build.bat +++ b/build.bat @@ -204,8 +204,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 +354,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/dsneditor/EsOdbcDsn.sln b/dsneditor/EsOdbcDsn.sln new file mode 100644 index 00000000..d5b79774 --- /dev/null +++ b/dsneditor/EsOdbcDsn.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2047 +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 +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 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {208F0E95-4408-4AEB-BE40-8230AF03964B} + EndGlobalSection +EndGlobal diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp new file mode 100644 index 0000000000000000000000000000000000000000..12f1266736ab53640d07f88b87b9ba18adca9bb1 GIT binary patch literal 11470 zcmds-ZBJXt702hTEA=}#u3DUA6SCX(4V!L7z}byTLJCw=+U_DA4UMsp4GA>euip0m zo8#k|xz|rbjkc;R;QHQk=REz-!<^yY|5^>Jp&5?CFr0?N@J`o<;ab1H3}1z3`g$xYPG*U0;T; z<6HN6V=b(O9ld)d%bse*Yw;S0&!?@lr!{W1#;3i#6RnN}=!j&mBng@gCDWBG#?^a0 zNAuB`3m90WaUBmG`SL<*54565%R1PR*nmS*-q@F4Hg$C%q<@esCmHQDU)1@OxNC-w z6Tdcumq+niLmWWh+oC-dJz)#94*wBF(T~W2{|6S4WbPtkTr)zM_iXL=94! zy`kFGH_s8x@T2r4R`I$$NuGiFB+|auDnohXIO^IHCv<3#KNsEe@CW^V zCf*+1V3m*6U8t;5jC3`S9=(XUzOM1yx#YXj2RowYktn!tQjnvlJA-Hsp4%$a%i{^9 zt%LPWC-H4EaEH7w)apo?@Ye`GMH@M?a(ON1kV{g0nFpwCL&-o!As~MmyoC`$v2Zb z&FG`I@r^z4&sW|U%RZXn@e&*O6c3&0gC&q0eekUL)$?*&5)LBrF7yV~6@(;&H)HH1 zj3*6Cd*<(yQL!JsmmTiYUHl*kKG2l-(dYbgII(li&zv}G@5U*4OkeB$rf2!B1-59k<0JoOjYVpc}l z6I#(Aucw|@JUe*=H-+&$gCsrC9edbbg6L;W>qD&K9*%KOPP-dZLqoU5I^K!o*|MYi zRspK2Bh}UMP8NHkvJ-y5dJZ#N&A;nvd*y<~F+}zNM$^TYAf~z11bU zO@GgNPx^|1bi_~SjAsXhH4OWnxxJS&PlE*Ou6S;B0}EScmUHrslb&;WCzB*8yT-DM zYZsQIdzey-txwLJ-;XWoQ;{81&hJX0nx%QdXPHx{XFQNmb_8jE(hwIg zlYH~PgOajq{{O&5sZ&Bi{r-R3_n^SzweEkf9LALjKU6j+C)%Er%t0K``%zJB=i`-v zOq%z1%rJ)fv3$duUQfxwtkaFzncR||5f#?YUn<6o6SQROUhF8Xr=~ll?hYsVfZ;s< zq&+XSp`#V%&5YXOHhWKtkE(s!BT3-<*?si^ZrA(l9yi1@?VQUT2+w?9woJD86dCHM zt8*=Brtib3AXHoSP-s=^X0@??KUFa7=JvF#ZttYK`?AP`*x|kjf6+c|OW*&fpV<{R zf0upst1tGVt-qLVkq)KLlGNq>`Er)Dxn$*&sw~!3 zS7SVN(@ackvO$NLVmWnZXS#n8Bb;ne&j~$Y>`;iYE4yhmW znF%|L(l&1Ox((gNd4F4GQEGtA7LT=V)lNU^9yVR9VOzQPDEwWtR1@CA-dKtZPNth> z(RDp%_QN|C6SL+()1<5FT%vs#>)6LEbNiHJFP;}{$9%!`1Gza_N&7_Gq1f+{Bx7D+ zI|+7sHM z95?jQ><)Vo!_}#v%lEA)@|ghVb4;TxUsW?2z5xN82k;iQ2Omhxj?2$&ES7C2jNBIW zRMmX9&IAknp~JrT+jXDLpMH*h4eD~Q>-xNrBsP<_H4XV@JDU?bo>?XFy%yT>6nMf0 z#KMInvR)9cqf;IeUD3Og<~Q5R)0N^8w+OmdWu5NJb#cq{I~BqF(}`z)Hfc4URob>N zSb87KTkWz_n`@os*iLv?(jfs6%p9&N4cOnB$Z!3E-GlNvo{W$YZ3Z z3KO2ZD}V>Ni=1KJC9}QMY9@!pqeX*tFLjJGSYV#Ll9Wc<(tA(!W*sYKM`0SI zvkR(l;*{dH#4g@hCTmuahvh*$o!ZN2v&(x}zeIgq53)x({U{~AD`JL>ZF!Vx&J?@u zE8@te_#X>h9cSs3cv@BZoaYv&aZgoFdFp;HpTCgRZ>49F+ByvM=W0t>anE?R#U^w* zIf7e}`n7W0fwIz{^yWYHW8Yt?uijQq(NXudqpO`7`pjn@H&qWUs1CJ+omkb%%U6f3zd&Ealg0hZt`== zFn?FKoUG>6JTKnICheGA$<(0qH^SEZxeYc?bIW-)EF&iAQtJG3N?%Ii@;VM1LROyd zHf5Qb}(rHp+y@lOd2Ad`2bREb5DJo4PKb=hXPnCac z8c0oQDZ5Y!5A?xX{Fe;>8O0U>?V@o$#{nq!#13oXswp?qLbruPFE?J zIDWl4xmB0xY?usGI~(t6+)LG-=I8BaT)GF6e0pEwa${?YO|CvyS5vi)?}~kbb}Bs5 zJJZ{PO!5@FBqGFVm{Cyknd#UWc*^WmJUj`%t@N*n)_V8ZtJ?JFR5IAKGndWJ=F@5= r4xec6=E(Al4O)$-&0Ic=QoDaa>V literal 0 HcmV?d00001 diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h new file mode 100644 index 00000000..8c073d08 --- /dev/null +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h @@ -0,0 +1,68 @@ +#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 (connection/00-list) couldn't be parsed. */ +#define ESODBC_DSN_INVALID_ERROR -3 +/* 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 */ +#ifdef _WINDLL +__declspec(dllexport) +#else /* _WINDLL */ +__declspec(dllimport) +#endif /* _WINDLL */ +int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, + driver_callback_ft cbConnectionTest, void *argConnectionTest, + driver_callback_ft cbSaveDsn, void *argSaveDsn); \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj new file mode 100644 index 00000000..a77f1942 --- /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..2f9e6e7f --- /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 + + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/App.config b/dsneditor/EsOdbcDsnEditor/App.config new file mode 100644 index 00000000..74ade9db --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs new file mode 100644 index 00000000..bbde0645 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -0,0 +1,114 @@ +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.connStringBox = new System.Windows.Forms.TextBox(); + this.ConnStrLabel = new System.Windows.Forms.Label(); + this.saveButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.testButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // connStringBox + // + this.connStringBox.Location = new System.Drawing.Point(109, 23); + this.connStringBox.Name = "connStringBox"; + this.connStringBox.Size = new System.Drawing.Size(297, 20); + this.connStringBox.TabIndex = 0; + this.connStringBox.TextChanged += new System.EventHandler(this.connStringBox_TextChanged); + // + // ConnStrLabel + // + this.ConnStrLabel.AutoSize = true; + this.ConnStrLabel.Location = new System.Drawing.Point(12, 26); + this.ConnStrLabel.Name = "ConnStrLabel"; + this.ConnStrLabel.Size = new System.Drawing.Size(91, 13); + this.ConnStrLabel.TabIndex = 1; + this.ConnStrLabel.Text = "Connection String"; + // + // saveButton + // + this.saveButton.Location = new System.Drawing.Point(331, 52); + this.saveButton.Name = "saveButton"; + this.saveButton.Size = new System.Drawing.Size(75, 23); + this.saveButton.TabIndex = 2; + 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(412, 52); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 3; + 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(412, 23); + this.testButton.Name = "testButton"; + this.testButton.Size = new System.Drawing.Size(75, 23); + this.testButton.TabIndex = 4; + this.testButton.Text = "Test"; + this.testButton.UseVisualStyleBackColor = true; + this.testButton.Click += new System.EventHandler(this.testButton_Click); + // + // DSNEditorForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(506, 89); + this.Controls.Add(this.testButton); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.saveButton); + this.Controls.Add(this.ConnStrLabel); + this.Controls.Add(this.connStringBox); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "DSNEditorForm"; + this.Text = "Elasticsearch ODBC DSN Configuration"; + this.Load += new System.EventHandler(this.DsnEditorForm_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox connStringBox; + private System.Windows.Forms.Label ConnStrLabel; + private System.Windows.Forms.Button saveButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button testButton; + } +} + diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs new file mode 100644 index 00000000..3c80773d --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -0,0 +1,144 @@ +// break at 120 columns + +using System; +using System.Resources; +using System.Windows.Forms; + +// uncomment to have the assembley 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 String dsn; // the connection string (also called DSN, in this project) + private DriverCallbackDelegate delegConnectionTest; + private DriverCallbackDelegate delegSaveDsn; + + public string Dsn { get => dsn; set => dsn = value; } + + + public DsnEditorForm(bool onConnect, String dsn, + DriverCallbackDelegate delegConnectionTest, DriverCallbackDelegate delegSaveDsn) + { + InitializeComponent(); + AcceptButton = saveButton; + CancelButton = cancelButton; + // TODO: how to map the X (closing) button (ALT-F4 & co.) to the CancelButton? + + /* + * If this is a call serving a connect request, call the button "Connect". + * Otherwise it's a DSN editing, so it's going to be a "Save". + */ + saveButton.Text = onConnect ? "Connect" : "Save"; + + Dsn = dsn; + + this.delegConnectionTest = delegConnectionTest; + this.delegSaveDsn = delegSaveDsn; + } + + private void DsnEditorForm_Load(object sender, EventArgs e) + { + connStringBox.Text = Dsn; + connStringBox_TextChanged(null, null); + } + + //private void label1_Click(object sender, EventArgs e) {} + + /* + * 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) + { + String errorMessage = ""; // must be init'ed + Dsn = connStringBox.Text; + + int ret = delegSaveDsn(Dsn, ref errorMessage, 0); + + if (0 <= ret) + { + Close(); + return; + } + // saving failed + if (errorMessage.Length <= 0) + { + errorMessage = "Saving the DSN failed"; + } + MessageBox.Show(errorMessage, "Operation failure", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + private void cancelButton_Click(object sender, EventArgs e) + { + // Empty DSN signals the user canceling the editing. + Dsn = ""; + Close(); + } + + /* + * 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 testButton_Click(object sender, EventArgs e) + { + String errorMessage = ""; // must be init'ed + Dsn = connStringBox.Text; + + int ret = delegConnectionTest(Dsn, ref errorMessage, 0); + + if (0 <= ret) + { + MessageBox.Show("Connection success.", "Connection Test", MessageBoxButtons.OK); + } + else + { + String message = "Connection failed"; + if (0 < errorMessage.Length) + { + message += ": " + errorMessage; + } + message += "."; + MessageBox.Show(message, "Connection Test", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + } + + private void connStringBox_TextChanged(object sender, EventArgs e) + { + /* + * only enable the Save and Test buttons if there's some input to work with. + */ + testButton.Enabled = 0 < connStringBox.Text.Length ? true : false; + saveButton.Enabled = 0 < connStringBox.Text.Length ? true : false; + } + + // TODO: register this in a handler. + private void DSNEditorForm_FormClosing(object sender, FormClosingEventArgs e) + { + Dsn = ""; + } + } + + public static class DsnEditorFactory + { + public static int DsnEditor(bool onConnect, String dsnIn, + DriverCallbackDelegate delegConnectionTest, DriverCallbackDelegate delegSaveDsn) + { + Application.EnableVisualStyles(); + // this would trigger errors on subsequent factory invocations + //Application.SetCompatibleTextRenderingDefault(false); + + DsnEditorForm form = new DsnEditorForm(onConnect, dsnIn, delegConnectionTest, delegSaveDsn); + Application.Run(form); + return form.Dsn.Length; + } + } +} diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx new file mode 100644 index 00000000..6ed23b54 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + AAABAAEAAAAAAAEAIAAqFgAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAEAAAABAAgGAAAAXHKoZgAAFfFJ + REFUeNrtnQl0VFWax19tIUHQZsSlp51uHbU97Yo6KjsubR/bme7pmZ6xHc/oLGo7Padt7dbBLIYAYkAW + UdzYTFKVVBZIUlnYyQKyIwJKWAJZCBBkB9khwJ37vSSYTuqFyiKdqvr9zvmfsCT10PO+37vv3u/dZxgA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQjDwxd44xsCRfxxemyTeGlMwybk6aaRgxyYYR + pzM8y3CMn2M4Jsw145yy2nB5K8I+EZL0GoomFBi8dKYxeJHPGLA41+hbXOjsX5IbFY4ZUJwf9XBJkfOJ + VfmG8fK0COPNpCuMUTlXOiYVX+v4oOQ6iXP6mqtc3q1R4Z4I77ZII7XMcHqrDHt2JUUUrAwpzjcGL8+y + aQHcPrgk7w867+tM0Zkansl/r99c3yvd3/LE2WNSpttjPW77sHSfPSE9z56Qkef8aLnH5a2c6vJWhXs+ + 0RmhBfCoLbfC5UyvppiCUgBFeQ594v+zzhqdszoq3NN/vu/M1RPSTmkBqD9LrFs5P1yu9IlPvk2Vy1v9 + siOzJuIqPRqAIGKQvu8dXJz/sD7pqyj8JgJY4FPXTPQqBBBwdur8o1GAAIJNAFfqEz6TokcAnZBPI9Kr + elJVwTT5V5LXX6ecokcAnZBlOrdQVcElgKd1jlD0CKAT8qXOnVRVcAngeZ0zFD0C6IR8hQCCbg4g7wVm + /hEAIwAEQOEjAASAAAgCQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAAVDsCAABIACCABAAAiAI + AAEgAIIAEAACQACAABAAAgAEgAAQACAABIAAAAEgAAQACAABIABAAAgAAQACQAAIABAAAkAAgAAQAAIA + BIAAEAAgAASAAAABIAAEAAgAASAAQAAIAAEAAkAACAAQAAJAAIAAEAACAASAABAAIAAEgAAAASAABAAI + AAEgAEAACAABAAJAAAgAEAACQACAABAAAkAACAABIAAEQBAAAkAABAEgAASAABAAAkAACAABIIDQpf9C + 34sDFvrqdBSpz0CdvnNzVe93LQQwaalypW4jLbNB5y6qKoj4/kcZv79+Urq67n3SNNdOTFdRwz0tBaDj + GJmjHGPmkOYZN7/WObG0L1UVREQO97zqjHP7PdEJaVMSMg/bEwuHUFVBRESC+2VHHCcvQQBhiSM+7WW5 + r+UEJh2NIyHjG9eYgoepqiDCPizjj/Y4Dycw6XCcwzOORY7Ne4SqQgAk3BKNAIJTACOyX7UPy1T2eC9p + nlj/YnTFu1W3BNI8kaMyDmgBDKKqgmkOIKnsJWdS2TkdRRqSrDN9vXIk5rW8z41NUTdNz1R/V5hDmuQB + nT55vuwb3LOvoaqCCJe36kWdOrrYmsWzVTlGF/oVwG2eGXRL+s+0AaV53akqBIAAwjPTB5QgAASAABAA + IAAEgAAAASAABAAIAAEgAEAACAABAAJAAAgAEAACQACAABAAAgAEgAAQACAABIAAAAEgAAQAbebNTwxb + TLLhiE65dIYmGa707c+7vNVaANWKNCS92tzl1jHGQgCp9QIYUuo/CAAuK/bXUg27Lmhb/HTDFjc1Qgvg + Jl3gD+gMbDVDk/o7Jy15xzl13TkdRRoybb1yTl6jHKN8fgXwwymZqk9OtuqT2zL3+nLUg7Nyza3FEQB8 + 98RlGUbiFLnq97bHJj+nT9J8nTKdSp3tl0i1PS71gH1Y5gVzUxDSJBnKaqekiGFuFTXC4zfdR3pUj1Gp + 6uoJXnWrJ0v1nZeLAOC743v/85Rhi3Y/qE/MWTon27elUzLxG+v/Z7bo1mPukReXonqNS1P36NHCoGIE + AJ2MQ9/r22PcD+mTbTX70nXdvfJ6vJ1qSgABQOfxRpII4AcNQ/4LFFvXzvfGppmvG0MA0DkMTbLpE+ul + 9g/7yWXdMz82Rd2cnIUAoHOwRSdfrU+sTIorOCLzAr30KGDAAh8CgM4QQMpt+sRaR3EFjwBkheD+/BwE + AJ0xAZhyb8NSHgUWJJOBkcM96q6Z2aHcLIQALlvzT0zK/Tq7KK7gibw8486smQgAEEC4CuCOTAQACAAB + IABAAAgAAQACQAAIABAAAkAAgAAQAAKAriIAaWAxopP1V53v7BjJynijMUnNklx/fASAAOC7F4CtSe96 + j2Fp6m9Gz1B3TPSpW8blqN5vZShXnFs5mn1vR44ToT/vpney1SPT5qnfpC9SL+QsU/+bt0K95Fuunpvx + mfqHlCJ17/v56qrh3ovH7cgx/QUBIICwF4BcZZ2xbnXdqEz1T6kl6qMVm9Vn1XvU+t0H1aZ9R9SGPYfU + 57sOqNlbdqpxizeof/WWqh+Nmam6D0s1f1bSluPcNDbbLPLcshr15deHVM3h42r/idPq6Omz6viZOnVM + 5/DJM2r30ZOqfP83aon+t4zVxx00eY7qkZDW5lFB5Jse89/74/E56tZmuXpkOgJAAOErACmmG/SVXgpy + ec1es/jOX7igrDh3/oJZpBUHj6opq7aoX7qL1Q8Ss8xRw6WO8/23s9QrhatU2d7D6lTdOdUW6vRx9x8/ + pdxfVKh+H882j2cL8L/vzvfyTKEdOnlaHWyWP81arYyhSQgAAYSfAOQev9/Hs1Texhp1uo0F2YgI40U9 + dG9NAFKod+kizPqyut3HaYqMCv5z5hLVPT71khKQeYQ++jZCRjP+iJv/BQJAAOEnAJl866uLf0XNvg4V + 4/bDx9QjU+eahWZ1Bb79XZ8q2rZbXVCdx95jp9Tv9Kilmx7e2y4hgHtaEUAsAkAA4SYAKcrr385UM76q + brUo5Vbg7PnzlrcE8ufTVperHsNSL+6F1/zKL8fx6Xv9C51Z/Q3IHMG/pJVecgSAABAAAmiWX6eVqJNn + 6/wWhUzKZX5ZpRJLv1QJC9ep0Yu+UilrtqmF22pVpb73l/vxxgL8edICy6u/XJ1HFK1Xp84GNuz/5vRZ + tfPIcbVDH//QyTMB/cza2oPqJ3qEYUMACAABBBa5d/5k5Ra/BSGz/r9wF6krZIa/cS1ef5VluL9OzFKD + p8wxl+q86yrNz+iZkGZZeAMnz1Fb9f16a8ik4he1B9RwLQpZXXh8+nz1U51fpRar12evVgu21mpRnWt1 + lPLuko3mEiUCQAAIIID18F4j0tXS7Xv9FtPLBSsvThD6u3UwpaC/yvLZtaMyLY8Rpa/+4z8rU+daGfvL + hODkVVvMOQIpYCnEi01A+tfyb5AVCpmpl3t+K7YdOKru/6DA73IkAkAACKBZcfYemaG2+Lky1507by6x + WQ3pW3TwWaz/y89LUa/ffciyaM/oYyWt2Wo2GLW2ri9/J7cSv89fad4iWIlEZvNtCAABIIAABKCLbuuB + b/wOx5/OWNQp7b0yxyBFbkXBph1mc44tOjDZ9BrhVZ61FZafJ0uZV2uxNf88BIAAEEDzfeqHe1VxxW6/ + BSGF8vPkhWabbqAdfv4672Ti0App6JH7fRnqB7xyoQtZ5ib2Hfd/K7Bhz2F9G1DYYvSCABAAAmh+MsZ5 + 1JsL1qoLFvfnu46cMP/+5rE5F4fhbRlhiGAKN++0FMCy7XvN7kFbGz5X/g0/HDPD7Cfwh6wayLMDzYsZ + ASAABOCnmKQopCW3tdZbaRKS1t1bx+XUv/cuQAFco28x5IrsD1FO2rrK9r1gQ8dq9UJWJv9jxpIuLYCL + m4KGZqYNXJSPAIJBADZzFOBWf5y1Wh2zmFhrOlknD+O8qkUgy4AOixWCpp8tV/c9FrP2Z/XnjSxe3+pn + WIpLF2rMvC8s5xZkebKr3gJExLvVjdMz1d3Z2erumaGX2zNn5twwJeMaqjOIWoH/amS6+nD55lYn6xqR + B4BW7Nhn9uDLMqLVbYEIQEYMVo08sqb/SsGq9glAF/Pz2UstVwP+b87nXXYVwB5bL4HIBE/IpZt8HeHZ + 1P2ttHuoziARQGOxylr+xyu2mA/1BPTwjy4+mY2/d1KB33t4+cw73vWZj/NaiUSe+2/Ps/hSzP+WsUgd + OHHa72dL16L0E9i6ogBC/M1HzjfdFVEjU++nOoNIAI0FKyMBudeXZ/PPB9i0v1KPBv4+ZWGLpwDl834y + wVoAIprns9svAOn9t1oJGFG0zly9QACXP854d0XkW2kIINgE0DgpKFfOOyfmqZFF61XVwaMB3RbIY7my + gUhzAcguP/KsvT9OnK0zm3raewvwTOZiy8+OnremhZAQwOUSQOqObqMyHqI6g1AAjYXb2HV324RcFb9g + rdmjf/oSIlhes8/s+mucfGt8AnDHkeOWXXuxZtde+wTw29xl5s5B/hCxdNVJwDAQwK6IUZl9qc4gFUBz + GciVVLbQkn6A1h7oke5BWU1wxbr/rNV41Y79lg/vTFlV3q4mIynUYQvXqrrz5/2K5en0RV12GfDbJIde + ZKs3LYBuCCA0BND01kBE8LNPF5h7Alohz/tLe3Hj7sFXJqSp9HWVlt8/f2ut+RShrY1Skvv7Tz/f6vcz + 5THix6bNa9Fd2GUEEOdRjsRC5ZhQpBzjQy/OcfM+c432/S3VGUICaHprIFfX/cdPW7ThHjL7+huv6lKo + siTX2tN7/T+Z3eZWYBmRLKvZ6/cz5elG2eTT6KLPAtjjvcr58SrlSqtUrtSKEMy2JKdncw+qM2j6ANr2 + /bI1t9WwXrYFu3ls9sXik6J7VF+Nj5w6Y9lcFK+H8t2azdi3Gv3Zz2QstvzMaau3mi3Otq7aByAC+ORz + 5UqvVi5vVeglvWqaK6uGTsBgEEBEXP022U3v2wP5mdLKry1XA2Tm/6IA9Fd5p8Dc8l2Wo4DN+47UjwIC + mAto3FFYtiX3x2EtheekDfiNrvs4cMgLwFs13ZW5HQF0dQFIsctmHgu21Zr7/8sOP/J7c0OOxjf0mG8D + +vbNQI3D7y26aFt7uKdpMTv0z0pr7gmLbcek22BR1dfq7vfyLB84ajy+jD7k6UKrHYVlZCKCsLVjRyBp + LTZe/7TJ24kukXY+IYkAoMsIQB7Wqf3mhDkjLw078oTdUH3P/tj0eeaSnizjyZuBouJTzU5BeSFHTtl2 + c8bfHyKS5pN6jaMAmfCzai2SP1+9c7/6TXqpWcBmgQ1Nupgr9PFlpx/5/OMWnYrSWvw7LRpHO/cEnLCk + TN2iRy8/Hp8bUH7Q8DwEAkAAQS2AnX7W6Y+cOmtusimz+rLbr7QIS+FXHzpmOZSX4fdT3lL/bcH6Cv5k + 8kLLnoBvH+U9rfI37VBx89eq/85eavb7yzZgk1eWq417D7fanZizYXv9CkQ7BSAbkMoIRp58DCRj9EhE + eiUQAAIIOQG0FSlL2Ry0cQnQ/9yB22zQsZq88/esgCSQjuQl1XvVfR8UtDqheSkBtBURVSQCQADBLoBd + +hagoxRV7Dbf+NPafbEcT3YYlmahPa1s7NkWZEQgty2yf+GlVjM6WwC+jTUIAAEEtwDk4Z91HSgIuUJn + rK8yiz/QY0rRyDZgi6v2XHyvQHuQuYsPlm9Wt4zLDrh/AAEgAATQbFgue++/t3SjKttz2Jxdl+27rYbd + 8sfy9zKjL/v0/zZ3ubp2VEabZ8Tl+6VZRzYXkT0JpadfJhYDfWvQ10dPmhN+MjkY8BuJGwSwrvagOXLo + aHLLtiMABBD8jUBSGDLTf8u4HPVE0gIVPXeN2Uwzr7zW3PhDtvSWGXpZLkxdW2l29kmrbeOuQO1dDpPj + Ohp2DZIXh/yhYKWaurrc7BmQR4y/+vqQ+cahCxYz/iItGcEEenyZm5BjvTb7czVxyUY14bOOpEz918yl + li8hQQAIIKhagRvX2eWrnNRyZZP7dRGDLOvJV/m9LAdK01Dj93fWcwYSZ2z9cbs3HFeu7j/7dL5lz4G8 + VnxE8XrzewPtInQ0jHpk9r6jaVfxIwDoigLoqnFoKUhnn9XOP7KiILcRUfGedm0s8hcJAgAE0LZ3C0ib + 7imL7j95VZjsTyiysCEABIAAQityeyD7CsjeAWctNiWRBqVfeYoZASAABBCam03WP4Mwp3yXZTegPIos + rco2BIAAEEBojgQe+miWWehWyF4A8swAAkAACCBE80t3sblEWHXwWItUHDyq3l+2yexw7KxVCgSAABBA + F4osv0kPwo/emdkiN+rcMHqGuVxpYwSAABBACM8LWIRJQASAAAgCQAAIgCAABBDCOGKS79Mn1U4KK5gE + kK6ckxEAdM4I4C6dLRRWsCRZ2YdlKOfUtbpQEAB0WADuG/WJtYjCCpLIC1aGZylXUlmoFj8CuMy3AFH6 + xJpIcQXRA06JhcqVVoEAoOM4Y5IMe2zKk0wEBkniUuvfChS6xY8ALvttwNBpUY6YlAn6BKujyLr41f+d + Ocrl2YoAoBMFEJ9m2GJTrtcnWKrOaQqtCw/9k8tCvfgRwGXnqTdEAIY9Oul6R0xyvD7Zyim4LlT4su4/ + fqEu/o3hUPwI4C82EohOMhyvJ0fok+4+fUvwqv6aobNY/3qNzhdW0d+zJjLBXXNlYuqFK0enKvJteiam + qoh4/1t/OYZlKMfIbP95K1c5xsxWzvcWKee0dfLG3FBe9kMAXWZVYGiyFkGyEfFGisMem9zTHuPupYu8 + d6uJTul9f0HOq/3m++p0FKlPf50HZ+eq3hPSWgog1q2ck5Yol7tc39P7y9b6opdXgKeHTeEjgK6ALSHL + cEbrW4KYZOkTMHSRXzKDS/NeGFySd1ZHkYaU5qn+C3zqmole/wL4cHm4FTYCCFUGlSAAf0EACAABIAAE + gAAQAAJAAAgAASAABIAAEAACQAAIAAEgAIIAEAACIAgAASAAggAQAAJAAAgAASAABAAIAAEgAEAACAAB + AAJAAGGQqa6s7VFUFQJAAOGZid2ydzioqiBicInvWX3Cn6DoEUAHU6fzmhYARRVkI4DH9Qm/k6JHAB3M + dpe3eggVFWQ8XOy7Xp/wCyh6BNCBnNNJjvQwARh03LXaq28D8p7ROUjhI4B2ZoO++t+rBUBBBRvL/v2s + MaQ4N0qf9H/SqaX4EUAbr/xrnenVjxtTK2xUU5AypNhnDCjNjxhUkv+IPvk/0pmtMz+c02++b2HvCWmb + HLEpuxxxnj2OeO8xu5n0Y1oAtS5vZYU++cM15TordN6N8FbeaczcZXOkVlFIwcrARblG/5ICQwvA6LO6 + 0HisqCByYFFOz4FF2WGZQcU5Pe/OntGrx9ueByIT3E9Ejsj4hXN0wbOOxMJnHaN1Pl75pD7xB4drdOH3 + 0wK8yZh4wK5/b+gRAEUUKvw6J9t4tKjA0AII22gBGH1yZxg93/YYWgBG5Ih0w5mYbzgSC3QKDS0AQ078 + cI0WgE6lYZ++i4IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIVv4fheb220fhoCoAAAAASUVORK5CYII= + + + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj new file mode 100644 index 00000000..d0795533 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj @@ -0,0 +1,110 @@ + + + + + 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 + + + + DSNEditorForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + diff --git a/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs b/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8f2e18a4 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("Elasticserach DSN Editor")] +[assembly: AssemblyDescription("")] +[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..af7dbebb --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/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/EsOdbcDsnEditor/Properties/Settings.Designer.cs b/dsneditor/EsOdbcDsnEditor/Properties/Settings.Designer.cs new file mode 100644 index 00000000..6f71f0b6 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/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 EsOdbcDsnEditor.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.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/EsOdbcDsnEditor/SQL_Icon.ico b/dsneditor/EsOdbcDsnEditor/SQL_Icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ea70cbec4c51dfa2cc375c43b988d724839c7c24 GIT binary patch literal 5696 zcmcIocRZX=w|{I1Aqf(_MM8*Z!7p0EE+LW-y?4=x-og?~LW1ZC7OVFfJvxzuC`Ihl*Z=_i&Qk?B?N<|-Q6x-kjgT7#!mu=lSDI6RQaciJ3n7YGGjKo~Tp##J;wX)`?0G#LL0 zs^kJa(+3$)&{&b9Znh!t&D=?<=#cJPh$7r6@;Lo7_x`|M%WNZN1~YBTL>$7uFVTE> zDB=?(@|oMLlQHk7MCiv}QD@a;dnS>ep|ts84|`lcq+3ez^xj`ct(Y2^2`%?72D7BS#oUE$ar_k9=POUw~KHBn4pQ5+fyWr9;4Grt}342cV$>$oG)TpFzm*ugwL{T#dl74K>Bl9V=CCYq%^MH=#y*>7!dp% z&_iFBjn7g75HV_8gT;QDI_-ZQ>&1 z%K2YK2=J%>-(-KN0TN>NH?Kcj0ML2%hv&bs>wgXy^5;jS=VSP%iNFSO3G%ZM7Z`A8 zPrAZ>2%^QqLkHs{NP5q{&^_0gOR!3AMX2cR`=c`@=GNBNnhgzG0fT>ysjBE{bjS#j zWh^kkv)L?{W0sjET1TZSs)cpv6%?)>HlaUw;AMVYve&vMNYcidoPt6y-dc%` zS6B|P2;-97w5~5-BIRL^pq~VEe0_g&VhRHBnOR ziY##@Vgb$W+{-mh{gKZ#B%b$c2kIp~N?s&E$ojTyuufH#XBGuhB-Wu75z-Cg$=|DV zq#iTk=-Zsqm_3zo{naG7zSD(wiPHKiHbnmMKjij*)1=dKpW0<3mCE_|s`Z|sJj`X- z&Hl!w5kp`k{4eh2iK(SgkstCv;Xa5-6Ah*Sg!m7}G_N!Y*%_U9b}&fXm#3QJF`1)Y zz5Me2qj=l{UP0eZDX;1CIf^WdDpKA;qVL~fvoI2j-!|( zzw+peiq2>ZdH5p_Ue5Xm7?8UBymIaOdhP)={-^b{7+73!IyE%}$nf~A$ZJ-rR-zKl zFL`SZ)Qr7j-Uhk>f+1-j-A=e~hDvT^(STLR+}Q~m@xLM@N$xHknjB;d-0?xN9Wv-L zI4W~W;4L6zw5O)heJb5}r)(~)>EQyh1WsInkS~;TQ7Zr2+Ju$e0_@F2vf}sk5SR_7 zpWUyrMV|J>83+0k?PIQy5mx#X;}>mVk9K+7ukNFQEj0SER50UWeMc|VNGdtNMJeif zCK!|FQ#rylv|KowBdjUBD4<11sgrMBG%8C=*@5HUeZ1v`!5^cG=4T>0H@iem$w)Y1 zpBZ7s)`$vcujvAneYL{d=xq9w5!(^yFm9TBBriZ|I8^4{@Ji#=0+f+=kz0gBLEwHu z(TzxWVFR`D{XpHpgoUZU#3u!Irz%{_suUj5WpG+y; zefvzSj*wGB-VEbv*o)r+fanC`R>|3G=>v3zYrMs`2$T8!^oS)921 zW3jXMDFm|f0rLWI|MlXCK*he6tMm&89i78mS}VyRHeO^crzYulCu)sM)DfLCmifaN zY(Z`c$IPVAO&HXW79wy?%>(dtug@a2#afD-Ywj%R&TSm;CDf7bt_(L27a>s(yH34`6jeczCWN$X)>Y9aP8dyE9RSA-j8vAvP(_8SJmmq%l^%2Po}V zWAOpZX!0iaOe%wAo&)y7K>p7b{T2bN>fU^cOi4c_f{B`jT47y2TYNr<2_m!Fh# zC+Buu&8U(6wYbq~qt2z_)^tQv5x$IH6B>nyS4Fx`!D+k?3t(X0`tAPUIi%es*dEAt4?+KUb@K^XV;P|AXO z7tUOeD<K3?6b8GZ3g*4iQ>A6VLZE!u7f+)7@lniK}sPU{RW zF=2tjvn#>?{Zs(HMLYM#cu*iwXL%JqNhJ9tu8MlhNf-l8U!8XoGr?cCH+Pnhzyw=_wt#1YfRs2 zUW&>N#9h0kZ(7yAfpbjtck~CjcE&Cwm_RFR#!T^~+mln)%SgF1nhMX6r)~FW+REN* zzyk`Aob(V6=B`iLwF7alVWS+Yv0HKm&t1Pa5K2Z~jYOR$P-9dKbY{wUxcG)GsK$yK z_Et{Am)5X~Cf>~3licr2t%@ZCVa^nse~}JgM|uxe!Mc&ZDFfjk0+-P z>-8yN798kUdQ6vD8mSqF$E$&7(T8G7BELKH`W%0mj9Zh^tY}1ilV3_w&tY45B#d<1 zll6!^I29aWmIvASIdbZaop!1jRnK#X;&H6yXw8C3?y-p>=qVUJy{v1_94k7G zdyHBfTFvSQjW0BJAMe~p@*4W`_eN4o)lY*NuEOttD&peR=Tx zcYGF9+bl9VRoW6|OjrJHcRIOmypH7*)o&$Gd(vxPT$e0}tSy@$=L8)sdMrIF*A;Tn zS-sutkj!5ks@i5w15q$O6-PbCOkOyK4|bOmP10(akOg4%eDm9;c1;qpVSV_h;b;&j ziOOzDL%;U+VW)*)$V$$YqsTJ6q-z&do6-I0Pxb61o?awRDtc@egy%`2%^iV)JMhj1u-%4=_UwN?mc3M54 z|7;xVyBwD$u<$b_DC@UYpQ@biO(j*7x5tm!8@J+?8$Fn2eu!Q68*58 zAPn((bd4x$N9RhuDqQ7cc{PRdyxt9I8>x?p^M_h9ngpkI9OaqSu6$1#%;mTrPX5jT(5ODK+nQrAr&lBz7Ar<4j0$T(SJx+xx~`CsO?#6D5*yG;Z| z3MrCmdOl?oTeaJz0m4+!2x;4XOD&^0xHWEY7EU zvEQMnm$xk`7mWZ9SU&sJC2k&3d|>Qf4uQthM^GoY=+=Gcx%wp#IQPN>FZG3>nASSa z?7Nd5SMkv66l+JIIi7U$t}ZsU@BxX-*;`noOcYfjwW?_7w&}^fEq?C>62t zrqQXRN43}lW#9KDGZS3cZYJ~DZ6G!Yz4({$6m}e^9}z4c{Ir>&qu*;?&bR@ zT1#X+3>PTv7AN+nf!IZna^UO|3|4i!Oo82A0Cc|pP}o!Usi;@b_uAHv&}MsBG-Fo# zbHjC`M2GSZ4P&@K>r?8l(KB$=EeZ70J-^U;V|&X#O%(l9`fr~?h~+Pb9^h5JHen_cONv>YeqSmEoEObsE4^AJ}5u5370ql`^i2 zN;lM&PfydJfB_8p(pv{;;Jzh4aU=V6K{YI-`;%@Ekg*_X8Fv}_vO8Xd^y1Usn(MQa zbPeC50U^9o+km}BRMN*wIMU+mu7;x#0z#^L2^9`xTqmPRw;6w-RW`<}Uxc)$ETy78 zjqj0fKB8^2UMZAN077M?*Zh=J+?!-k-F#n!w2vycz^^lf&LZd4PblVu#jm|r6=f^u zRa%YTk_}MaE;>ujtkNg=MIsq>f6Z3$h_;(}SieAr@GIrt-F5Qg$t!yQieMBd6>5E_ zv+E_nmtRlnN3SmOkb!Td+H3d6l)CA`Bf!K)$K0-xL9UW^=W%!kN6OR?$HG=_M3YZ( znCeidOW97LgW z@e?NyNHk)Ags8E9idp>k%D={4{wDYzffkFH!m^T04Q+Z5aZe8qo38YcEGXi9;Tnc9 z2>6o= zjl$j|Ve*`+nJ9L-u$%Tv>Ird@-ZpPqaI?i^5IHi|p0m>uksL{hXX)6$!NS}lS_o_T zi6dfdB$`!=|E0E8jA+id?d3at(M;NLg!i(?;6Zq)7&vvvrNzYpbICr1t%9$S%`HtP z>J>($5stMq1Zsp_tV3jvJMJ$@^Lx%jZtKq2_fA=R29u}+8?BdvVhBdmy*FC(|#3qPNtFHx;~|*5Y=@Q;^Em|74_|_Eqk_?tm$!% z><;-mN6-1gAyCuLq*rZafiA1GfFj-96XGHNRdqqz0ElAJj)W<|-Ihsa_JGL{c)a-D zvjd*wB6*F Date: Thu, 4 Oct 2018 12:26:29 +0200 Subject: [PATCH 07/35] fix the running of the unittests - add the editor DLLs into the unit tests running dir --- test/CMakeLists.txt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8d4badcb..ee4268fc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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}) From bd781df996c14692ad2ff4b84d69d35d7d42e39b Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Thu, 4 Oct 2018 21:07:21 +1000 Subject: [PATCH 08/35] Update "simple" UI with "complex" UI --- dsneditor/EsOdbcDsnEditor/App.config | 4 +- .../EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 409 ++- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 211 +- dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx | 3087 ++++++++++++++++- dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs | 24 + .../EsOdbcDsnEditor/EsOdbcDsnEditor.csproj | 30 +- dsneditor/EsOdbcDsnEditor/ODBC.ico | Bin 0 -> 21238 bytes .../EsOdbcDsnEditor/Properties/Resources.resx | 13 +- .../Properties/Settings.Designer.cs | 26 - dsneditor/EsOdbcDsnEditor/SQL_Icon.ico | Bin 5696 -> 0 bytes dsneditor/EsOdbcDsnEditor/header.bmp | Bin 0 -> 157816 bytes 11 files changed, 3529 insertions(+), 275 deletions(-) create mode 100644 dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs create mode 100644 dsneditor/EsOdbcDsnEditor/ODBC.ico delete mode 100644 dsneditor/EsOdbcDsnEditor/Properties/Settings.Designer.cs delete mode 100644 dsneditor/EsOdbcDsnEditor/SQL_Icon.ico create mode 100644 dsneditor/EsOdbcDsnEditor/header.bmp diff --git a/dsneditor/EsOdbcDsnEditor/App.config b/dsneditor/EsOdbcDsnEditor/App.config index 74ade9db..160269e9 100644 --- a/dsneditor/EsOdbcDsnEditor/App.config +++ b/dsneditor/EsOdbcDsnEditor/App.config @@ -1,6 +1,4 @@ - - - + diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs index bbde0645..39cf44c0 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -29,86 +29,409 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DsnEditorForm)); - this.connStringBox = new System.Windows.Forms.TextBox(); - this.ConnStrLabel = new System.Windows.Forms.Label(); 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.label1 = new System.Windows.Forms.Label(); + this.groupBox1 = 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.tabBasic = new System.Windows.Forms.TabPage(); + this.textDescription = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textName = new System.Windows.Forms.TextBox(); + this.labelName = new System.Windows.Forms.Label(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + ((System.ComponentModel.ISupportInitialize)(this.header)).BeginInit(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).BeginInit(); + this.tabConfiguration.SuspendLayout(); + this.tabBasic.SuspendLayout(); + this.tabPage2.SuspendLayout(); this.SuspendLayout(); // - // connStringBox - // - this.connStringBox.Location = new System.Drawing.Point(109, 23); - this.connStringBox.Name = "connStringBox"; - this.connStringBox.Size = new System.Drawing.Size(297, 20); - this.connStringBox.TabIndex = 0; - this.connStringBox.TextChanged += new System.EventHandler(this.connStringBox_TextChanged); - // - // ConnStrLabel - // - this.ConnStrLabel.AutoSize = true; - this.ConnStrLabel.Location = new System.Drawing.Point(12, 26); - this.ConnStrLabel.Name = "ConnStrLabel"; - this.ConnStrLabel.Size = new System.Drawing.Size(91, 13); - this.ConnStrLabel.TabIndex = 1; - this.ConnStrLabel.Text = "Connection String"; - // // saveButton // - this.saveButton.Location = new System.Drawing.Point(331, 52); + this.saveButton.Location = new System.Drawing.Point(457, 541); + this.saveButton.Margin = new System.Windows.Forms.Padding(4); this.saveButton.Name = "saveButton"; - this.saveButton.Size = new System.Drawing.Size(75, 23); - this.saveButton.TabIndex = 2; + this.saveButton.Size = new System.Drawing.Size(100, 28); + this.saveButton.TabIndex = 17; this.saveButton.Text = "Save"; this.saveButton.UseVisualStyleBackColor = true; - this.saveButton.Click += new System.EventHandler(this.saveButton_Click); + this.saveButton.Click += new System.EventHandler(this.SaveButton_Click); // // cancelButton // - this.cancelButton.Location = new System.Drawing.Point(412, 52); + this.cancelButton.Location = new System.Drawing.Point(560, 541); + this.cancelButton.Margin = new System.Windows.Forms.Padding(4); this.cancelButton.Name = "cancelButton"; - this.cancelButton.Size = new System.Drawing.Size(75, 23); - this.cancelButton.TabIndex = 3; + this.cancelButton.Size = new System.Drawing.Size(100, 28); + this.cancelButton.TabIndex = 18; this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; - this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); + this.cancelButton.Click += new System.EventHandler(this.CancelButton_Click); // // testButton // - this.testButton.Location = new System.Drawing.Point(412, 23); + this.testButton.Location = new System.Drawing.Point(17, 541); + this.testButton.Margin = new System.Windows.Forms.Padding(4); this.testButton.Name = "testButton"; - this.testButton.Size = new System.Drawing.Size(75, 23); - this.testButton.TabIndex = 4; - this.testButton.Text = "Test"; + this.testButton.Size = new System.Drawing.Size(156, 28); + this.testButton.TabIndex = 16; + this.testButton.Text = "Test Connection"; this.testButton.UseVisualStyleBackColor = true; - this.testButton.Click += new System.EventHandler(this.testButton_Click); + 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(680, 58); + this.header.TabIndex = 5; + this.header.TabStop = false; + // + // certificatePathButton + // + this.certificatePathButton.Location = new System.Drawing.Point(524, 206); + this.certificatePathButton.Margin = new System.Windows.Forms.Padding(4); + this.certificatePathButton.Name = "certificatePathButton"; + this.certificatePathButton.Size = new System.Drawing.Size(100, 28); + 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(124, 209); + this.textCertificatePath.Margin = new System.Windows.Forms.Padding(4); + this.textCertificatePath.Name = "textCertificatePath"; + this.textCertificatePath.Size = new System.Drawing.Size(392, 22); + this.textCertificatePath.TabIndex = 14; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(16, 212); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(101, 17); + this.label1.TabIndex = 11; + this.label1.Text = "Certificate File:"; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.radioEnabledFull); + this.groupBox1.Controls.Add(this.radioEnabledHostname); + this.groupBox1.Controls.Add(this.radioEnabledNoHostname); + this.groupBox1.Controls.Add(this.radioEnabledNoValidation); + this.groupBox1.Controls.Add(this.radioButtonDisabled); + this.groupBox1.Location = new System.Drawing.Point(16, 16); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(608, 176); + this.groupBox1.TabIndex = 10; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Secure Sockets Layer (SSL):"; + // + // radioEnabledFull + // + this.radioEnabledFull.AutoSize = true; + this.radioEnabledFull.Location = new System.Drawing.Point(16, 139); + this.radioEnabledFull.Name = "radioEnabledFull"; + this.radioEnabledFull.Size = new System.Drawing.Size(304, 21); + 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(16, 112); + this.radioEnabledHostname.Name = "radioEnabledHostname"; + this.radioEnabledHostname.Size = new System.Drawing.Size(362, 21); + 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(16, 85); + this.radioEnabledNoHostname.Name = "radioEnabledNoHostname"; + this.radioEnabledNoHostname.Size = new System.Drawing.Size(386, 21); + 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(16, 58); + this.radioEnabledNoValidation.Name = "radioEnabledNoValidation"; + this.radioEnabledNoValidation.Size = new System.Drawing.Size(241, 21); + 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(16, 31); + this.radioButtonDisabled.Name = "radioButtonDisabled"; + this.radioButtonDisabled.Size = new System.Drawing.Size(299, 21); + 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(112, 246); + this.numericUpDownPort.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this.numericUpDownPort.Name = "numericUpDownPort"; + this.numericUpDownPort.Size = new System.Drawing.Size(104, 22); + this.numericUpDownPort.TabIndex = 5; + this.numericUpDownPort.Value = new decimal(new int[] { + 9200, + 0, + 0, + 0}); + // + // textPassword + // + this.textPassword.Location = new System.Drawing.Point(112, 324); + this.textPassword.Margin = new System.Windows.Forms.Padding(4); + this.textPassword.Name = "textPassword"; + this.textPassword.Size = new System.Drawing.Size(229, 22); + this.textPassword.TabIndex = 7; + // + // labelPassword // - // DSNEditorForm + this.labelPassword.AutoSize = true; + this.labelPassword.Location = new System.Drawing.Point(16, 327); + this.labelPassword.Name = "labelPassword"; + this.labelPassword.Size = new System.Drawing.Size(73, 17); + this.labelPassword.TabIndex = 6; + this.labelPassword.Text = "Password:"; // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + // textUsername + // + this.textUsername.Location = new System.Drawing.Point(112, 284); + this.textUsername.Margin = new System.Windows.Forms.Padding(4); + this.textUsername.Name = "textUsername"; + this.textUsername.Size = new System.Drawing.Size(229, 22); + this.textUsername.TabIndex = 6; + // + // labelUsername + // + this.labelUsername.AutoSize = true; + this.labelUsername.Location = new System.Drawing.Point(16, 287); + this.labelUsername.Name = "labelUsername"; + this.labelUsername.Size = new System.Drawing.Size(77, 17); + this.labelUsername.TabIndex = 4; + this.labelUsername.Text = "Username:"; + // + // textHostname + // + this.textHostname.Location = new System.Drawing.Point(112, 208); + this.textHostname.Margin = new System.Windows.Forms.Padding(4); + this.textHostname.Name = "textHostname"; + this.textHostname.Size = new System.Drawing.Size(506, 22); + this.textHostname.TabIndex = 4; + // + // labelPort + // + this.labelPort.AutoSize = true; + this.labelPort.Location = new System.Drawing.Point(16, 248); + this.labelPort.Name = "labelPort"; + this.labelPort.Size = new System.Drawing.Size(38, 17); + this.labelPort.TabIndex = 2; + this.labelPort.Text = "Port:"; + // + // labelHostname + // + this.labelHostname.AutoSize = true; + this.labelHostname.Location = new System.Drawing.Point(16, 211); + this.labelHostname.Name = "labelHostname"; + this.labelHostname.Size = new System.Drawing.Size(76, 17); + this.labelHostname.TabIndex = 0; + this.labelHostname.Text = "Hostname:"; + // + // certificateFileDialog + // + this.certificateFileDialog.Filter = "X509 Certificate|*.pem;*.der|All Files|*.*"; + // + // tabConfiguration + // + this.tabConfiguration.Controls.Add(this.tabBasic); + this.tabConfiguration.Controls.Add(this.tabPage2); + this.tabConfiguration.Location = new System.Drawing.Point(16, 74); + this.tabConfiguration.Name = "tabConfiguration"; + this.tabConfiguration.SelectedIndex = 0; + this.tabConfiguration.Size = new System.Drawing.Size(648, 460); + this.tabConfiguration.TabIndex = 8; + // + // tabBasic + // + this.tabBasic.Controls.Add(this.textDescription); + this.tabBasic.Controls.Add(this.label2); + this.tabBasic.Controls.Add(this.textName); + this.tabBasic.Controls.Add(this.labelName); + this.tabBasic.Controls.Add(this.textHostname); + this.tabBasic.Controls.Add(this.labelHostname); + this.tabBasic.Controls.Add(this.labelPort); + this.tabBasic.Controls.Add(this.labelUsername); + this.tabBasic.Controls.Add(this.textUsername); + this.tabBasic.Controls.Add(this.numericUpDownPort); + this.tabBasic.Controls.Add(this.labelPassword); + this.tabBasic.Controls.Add(this.textPassword); + this.tabBasic.Location = new System.Drawing.Point(4, 25); + this.tabBasic.Name = "tabBasic"; + this.tabBasic.Padding = new System.Windows.Forms.Padding(3); + this.tabBasic.Size = new System.Drawing.Size(640, 431); + this.tabBasic.TabIndex = 0; + this.tabBasic.Text = "Basic"; + this.tabBasic.UseVisualStyleBackColor = true; + // + // textDescription + // + this.textDescription.Location = new System.Drawing.Point(112, 56); + this.textDescription.Margin = new System.Windows.Forms.Padding(4); + this.textDescription.Multiline = true; + this.textDescription.Name = "textDescription"; + this.textDescription.Size = new System.Drawing.Size(506, 132); + this.textDescription.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(16, 59); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(83, 17); + this.label2.TabIndex = 10; + this.label2.Text = "Description:"; + // + // textName + // + this.textName.Location = new System.Drawing.Point(112, 16); + this.textName.Margin = new System.Windows.Forms.Padding(4); + this.textName.Name = "textName"; + this.textName.Size = new System.Drawing.Size(506, 22); + this.textName.TabIndex = 2; + // + // labelName + // + this.labelName.AutoSize = true; + this.labelName.Location = new System.Drawing.Point(16, 21); + this.labelName.Name = "labelName"; + this.labelName.Size = new System.Drawing.Size(49, 17); + this.labelName.TabIndex = 8; + this.labelName.Text = "Name:"; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.certificatePathButton); + this.tabPage2.Controls.Add(this.groupBox1); + this.tabPage2.Controls.Add(this.textCertificatePath); + this.tabPage2.Controls.Add(this.label1); + this.tabPage2.Location = new System.Drawing.Point(4, 25); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(640, 431); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Security"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // DsnEditorForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(506, 89); + this.ClientSize = new System.Drawing.Size(682, 588); + 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.Controls.Add(this.ConnStrLabel); - this.Controls.Add(this.connStringBox); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Name = "DSNEditorForm"; + this.Margin = new System.Windows.Forms.Padding(4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DsnEditorForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; this.Text = "Elasticsearch ODBC DSN Configuration"; - this.Load += new System.EventHandler(this.DsnEditorForm_Load); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DsnEditorForm_FormClosing); + ((System.ComponentModel.ISupportInitialize)(this.header)).EndInit(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).EndInit(); + this.tabConfiguration.ResumeLayout(false); + this.tabBasic.ResumeLayout(false); + this.tabBasic.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); this.ResumeLayout(false); - this.PerformLayout(); } #endregion - - private System.Windows.Forms.TextBox connStringBox; - private System.Windows.Forms.Label ConnStrLabel; 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 groupBox1; + 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 label1; + private System.Windows.Forms.OpenFileDialog certificateFileDialog; + private System.Windows.Forms.TabControl tabConfiguration; + private System.Windows.Forms.TabPage tabBasic; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.TextBox textDescription; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textName; + private System.Windows.Forms.Label labelName; } } diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index 3c80773d..3bb4cc8b 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -1,144 +1,191 @@ -// break at 120 columns - -using System; -using System.Resources; +using System; +using System.Data.Odbc; using System.Windows.Forms; +using System.IO; -// uncomment to have the assembley loading to ask for (various) resources; various solutions: +// 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. - */ + /// + /// Delegate for the driver callbacks. + /// public delegate int DriverCallbackDelegate(string connectionString, ref string errorMessage, uint flags); public partial class DsnEditorForm : Form { - private String dsn; // the connection string (also called DSN, in this project) - private DriverCallbackDelegate delegConnectionTest; - private DriverCallbackDelegate delegSaveDsn; + private DriverCallbackDelegate testConnection; + private DriverCallbackDelegate saveDsn; - public string Dsn { get => dsn; set => dsn = value; } - + public OdbcConnectionStringBuilder Builder { get; set; } = new OdbcConnectionStringBuilder(); - public DsnEditorForm(bool onConnect, String dsn, - DriverCallbackDelegate delegConnectionTest, DriverCallbackDelegate delegSaveDsn) + public DsnEditorForm( + bool onConnect, + string dsn, + DriverCallbackDelegate connectionTest, + DriverCallbackDelegate dsnSave) { InitializeComponent(); + AcceptButton = saveButton; CancelButton = cancelButton; - // TODO: how to map the X (closing) button (ALT-F4 & co.) to the CancelButton? - - /* - * If this is a call serving a connect request, call the button "Connect". - * Otherwise it's a DSN editing, so it's going to be a "Save". - */ + + // Save the delegates + testConnection = connectionTest; + saveDsn = dsnSave; + + // If this is a call serving a connect request, call the button "Connect". + // Otherwise it's a DSN editing, so it's going to be a "Save". saveButton.Text = onConnect ? "Connect" : "Save"; - Dsn = dsn; + // Parse the connection string using the builder + Builder.ConnectionString = dsn; + + // Basic Panel + textName.Text = Builder.ContainsKey("dsn") ? Builder["dsn"].ToString() : string.Empty; + textDescription.Text = Builder.ContainsKey("description") ? Builder["description"].ToString() : string.Empty; + textUsername.Text = Builder.ContainsKey("uid") ? Builder["uid"].ToString() : string.Empty; + textPassword.Text = Builder.ContainsKey("pwd") ? Builder["pwd"].ToString() : string.Empty; + textHostname.Text = Builder.ContainsKey("server") ? Builder["server"].ToString() : "localhost"; + numericUpDownPort.Text = Builder.ContainsKey("port") ? Builder["port"].ToString() : "9200"; - this.delegConnectionTest = delegConnectionTest; - this.delegSaveDsn = delegSaveDsn; - } + // Security Panel + if (Builder.ContainsKey("secure")) + { + var val = Convert.ToInt32(Builder["secure"]); + 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; + } + } + else + { + radioEnabledNoValidation.Checked = true; + } - private void DsnEditorForm_Load(object sender, EventArgs e) - { - connStringBox.Text = Dsn; - connStringBox_TextChanged(null, null); + textCertificatePath.Text = Builder.ContainsKey("capath") ? Builder["capath"].ToString() : string.Empty; } - //private void label1_Click(object sender, EventArgs e) {} - - /* - * 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) + /// + /// 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) { - String errorMessage = ""; // must be init'ed - Dsn = connStringBox.Text; + var errorMessage = string.Empty; - int ret = delegSaveDsn(Dsn, ref errorMessage, 0); + var dsnResult = RebuildAndValidateDsn(); + if (!dsnResult) return; - if (0 <= ret) + var dsn = Builder.ToString(); + + int result = saveDsn(dsn, ref errorMessage, 0); + if (result >= 0) { Close(); return; } - // saving failed + if (errorMessage.Length <= 0) { errorMessage = "Saving the DSN failed"; } + MessageBox.Show(errorMessage, "Operation failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } - private void cancelButton_Click(object sender, EventArgs e) + /// + /// 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) { - // Empty DSN signals the user canceling the editing. - Dsn = ""; - Close(); - } + var errorMessage = string.Empty; - /* - * 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 testButton_Click(object sender, EventArgs e) - { - String errorMessage = ""; // must be init'ed - Dsn = connStringBox.Text; + var dsnResult = RebuildAndValidateDsn(); + if (!dsnResult) return; + + var dsn = Builder.ToString(); - int ret = delegConnectionTest(Dsn, ref errorMessage, 0); + int result = testConnection(dsn, ref errorMessage, 0); - if (0 <= ret) + if (result >= 0) { - MessageBox.Show("Connection success.", "Connection Test", MessageBoxButtons.OK); + MessageBox.Show("Connection Success", "Connection Test", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { - String message = "Connection failed"; + var message = "Connection Failed"; if (0 < errorMessage.Length) { message += ": " + errorMessage; } - message += "."; MessageBox.Show(message, "Connection Test", MessageBoxButtons.OK, MessageBoxIcon.Error); } - } - private void connStringBox_TextChanged(object sender, EventArgs e) + private void DsnEditorForm_FormClosing(object sender, FormClosingEventArgs e) { - /* - * only enable the Save and Test buttons if there's some input to work with. - */ - testButton.Enabled = 0 < connStringBox.Text.Length ? true : false; - saveButton.Enabled = 0 < connStringBox.Text.Length ? true : false; + Builder.Clear(); } - // TODO: register this in a handler. - private void DSNEditorForm_FormClosing(object sender, FormClosingEventArgs e) + private void CancelButton_Click(object sender, EventArgs e) { - Dsn = ""; + Builder.Clear(); + Close(); } - } - public static class DsnEditorFactory - { - public static int DsnEditor(bool onConnect, String dsnIn, - DriverCallbackDelegate delegConnectionTest, DriverCallbackDelegate delegSaveDsn) + private void CertificatePathButton_Click(object sender, EventArgs e) + { + var result = certificateFileDialog.ShowDialog(); + if (result == DialogResult.OK) + { + string file = certificateFileDialog.FileName; + ValidateCertificateFile(file); + } + } + + private bool RebuildAndValidateDsn() + { + // Basic Panel + if (!string.IsNullOrEmpty(textName.Text)) Builder["dsn"] = textName.Text; + if (!string.IsNullOrEmpty(textDescription.Text)) Builder["description"] = textDescription.Text; + if (!string.IsNullOrEmpty(textUsername.Text)) Builder["uid"] = textUsername.Text; + if (!string.IsNullOrEmpty(textPassword.Text)) Builder["pwd"] = textPassword.Text; + if (!string.IsNullOrEmpty(textHostname.Text)) Builder["server"] = textHostname.Text; + if (!string.IsNullOrEmpty(numericUpDownPort.Text)) Builder["port"] = numericUpDownPort.Text; + + // Security Panel + 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; + + if (!string.IsNullOrEmpty(textCertificatePath.Text)) + { + Builder["capath"] = textCertificatePath.Text; + return ValidateCertificateFile(textCertificatePath.Text); + } + + return true; + } + + private bool ValidateCertificateFile(string file) { - Application.EnableVisualStyles(); - // this would trigger errors on subsequent factory invocations - //Application.SetCompatibleTextRenderingDefault(false); - - DsnEditorForm form = new DsnEditorForm(onConnect, dsnIn, delegConnectionTest, delegSaveDsn); - Application.Run(form); - return form.Dsn.Length; + if (File.Exists(file) && File.ReadAllBytes(file).Length > 0) + { + return true; + } + + MessageBox.Show("Certificate file invalid", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return false; } } -} +} \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx index 6ed23b54..21dffa8c 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx @@ -118,103 +118,3000 @@ 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 + - AAABAAEAAAAAAAEAIAAqFgAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAEAAAABAAgGAAAAXHKoZgAAFfFJ - REFUeNrtnQl0VFWax19tIUHQZsSlp51uHbU97Yo6KjsubR/bme7pmZ6xHc/oLGo7Padt7dbBLIYAYkAW - UdzYTFKVVBZIUlnYyQKyIwJKWAJZCBBkB9khwJ37vSSYTuqFyiKdqvr9zvmfsCT10PO+37vv3u/dZxgA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQjDwxd44xsCRfxxemyTeGlMwybk6aaRgxyYYR - pzM8y3CMn2M4Jsw145yy2nB5K8I+EZL0GoomFBi8dKYxeJHPGLA41+hbXOjsX5IbFY4ZUJwf9XBJkfOJ - VfmG8fK0COPNpCuMUTlXOiYVX+v4oOQ6iXP6mqtc3q1R4Z4I77ZII7XMcHqrDHt2JUUUrAwpzjcGL8+y - aQHcPrgk7w867+tM0Zkansl/r99c3yvd3/LE2WNSpttjPW77sHSfPSE9z56Qkef8aLnH5a2c6vJWhXs+ - 0RmhBfCoLbfC5UyvppiCUgBFeQ594v+zzhqdszoq3NN/vu/M1RPSTmkBqD9LrFs5P1yu9IlPvk2Vy1v9 - siOzJuIqPRqAIGKQvu8dXJz/sD7pqyj8JgJY4FPXTPQqBBBwdur8o1GAAIJNAFfqEz6TokcAnZBPI9Kr - elJVwTT5V5LXX6ecokcAnZBlOrdQVcElgKd1jlD0CKAT8qXOnVRVcAngeZ0zFD0C6IR8hQCCbg4g7wVm - /hEAIwAEQOEjAASAAAgCQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAAgAAVDsCAABIACCABAAAiAI - AAEgAIIAEAACQACAABAAAgAEgAAQACAABIAAAAEgAAQACAABIABAAAgAAQACQAAIABAAAkAAgAAQAAIA - BIAAEAAgAASAAAABIAAEAAgAASAAQAAIAAEAAkAACAAQAAJAAIAAEAACAASAABAAIAAEgAAAASAABAAI - AAEgAEAACAABAAJAAAgAEAACQACAABAAAkAACAABIAAEQBAAAkAABAEgAASAABAAAkAACAABIIDQpf9C - 34sDFvrqdBSpz0CdvnNzVe93LQQwaalypW4jLbNB5y6qKoj4/kcZv79+Urq67n3SNNdOTFdRwz0tBaDj - GJmjHGPmkOYZN7/WObG0L1UVREQO97zqjHP7PdEJaVMSMg/bEwuHUFVBRESC+2VHHCcvQQBhiSM+7WW5 - r+UEJh2NIyHjG9eYgoepqiDCPizjj/Y4Dycw6XCcwzOORY7Ne4SqQgAk3BKNAIJTACOyX7UPy1T2eC9p - nlj/YnTFu1W3BNI8kaMyDmgBDKKqgmkOIKnsJWdS2TkdRRqSrDN9vXIk5rW8z41NUTdNz1R/V5hDmuQB - nT55vuwb3LOvoaqCCJe36kWdOrrYmsWzVTlGF/oVwG2eGXRL+s+0AaV53akqBIAAwjPTB5QgAASAABAA - IAAEgAAAASAABAAIAAEgAEAACAABAAJAAAgAEAACQACAABAAAgAEgAAQACAABIAAAAEgAAQAbebNTwxb - TLLhiE65dIYmGa707c+7vNVaANWKNCS92tzl1jHGQgCp9QIYUuo/CAAuK/bXUg27Lmhb/HTDFjc1Qgvg - Jl3gD+gMbDVDk/o7Jy15xzl13TkdRRoybb1yTl6jHKN8fgXwwymZqk9OtuqT2zL3+nLUg7Nyza3FEQB8 - 98RlGUbiFLnq97bHJj+nT9J8nTKdSp3tl0i1PS71gH1Y5gVzUxDSJBnKaqekiGFuFTXC4zfdR3pUj1Gp - 6uoJXnWrJ0v1nZeLAOC743v/85Rhi3Y/qE/MWTon27elUzLxG+v/Z7bo1mPukReXonqNS1P36NHCoGIE - AJ2MQ9/r22PcD+mTbTX70nXdvfJ6vJ1qSgABQOfxRpII4AcNQ/4LFFvXzvfGppmvG0MA0DkMTbLpE+ul - 9g/7yWXdMz82Rd2cnIUAoHOwRSdfrU+sTIorOCLzAr30KGDAAh8CgM4QQMpt+sRaR3EFjwBkheD+/BwE - AJ0xAZhyb8NSHgUWJJOBkcM96q6Z2aHcLIQALlvzT0zK/Tq7KK7gibw8486smQgAEEC4CuCOTAQACAAB - IABAAAgAAQACQAAIABAAAkAAgAAQAAKAriIAaWAxopP1V53v7BjJynijMUnNklx/fASAAOC7F4CtSe96 - j2Fp6m9Gz1B3TPSpW8blqN5vZShXnFs5mn1vR44ToT/vpney1SPT5qnfpC9SL+QsU/+bt0K95Fuunpvx - mfqHlCJ17/v56qrh3ovH7cgx/QUBIICwF4BcZZ2xbnXdqEz1T6kl6qMVm9Vn1XvU+t0H1aZ9R9SGPYfU - 57sOqNlbdqpxizeof/WWqh+Nmam6D0s1f1bSluPcNDbbLPLcshr15deHVM3h42r/idPq6Omz6viZOnVM - 5/DJM2r30ZOqfP83aon+t4zVxx00eY7qkZDW5lFB5Jse89/74/E56tZmuXpkOgJAAOErACmmG/SVXgpy - ec1es/jOX7igrDh3/oJZpBUHj6opq7aoX7qL1Q8Ss8xRw6WO8/23s9QrhatU2d7D6lTdOdUW6vRx9x8/ - pdxfVKh+H882j2cL8L/vzvfyTKEdOnlaHWyWP81arYyhSQgAAYSfAOQev9/Hs1Texhp1uo0F2YgI40U9 - dG9NAFKod+kizPqyut3HaYqMCv5z5hLVPT71khKQeYQ++jZCRjP+iJv/BQJAAOEnAJl866uLf0XNvg4V - 4/bDx9QjU+eahWZ1Bb79XZ8q2rZbXVCdx95jp9Tv9Kilmx7e2y4hgHtaEUAsAkAA4SYAKcrr385UM76q - brUo5Vbg7PnzlrcE8ufTVperHsNSL+6F1/zKL8fx6Xv9C51Z/Q3IHMG/pJVecgSAABAAAmiWX6eVqJNn - 6/wWhUzKZX5ZpRJLv1QJC9ep0Yu+UilrtqmF22pVpb73l/vxxgL8edICy6u/XJ1HFK1Xp84GNuz/5vRZ - tfPIcbVDH//QyTMB/cza2oPqJ3qEYUMACAABBBa5d/5k5Ra/BSGz/r9wF6krZIa/cS1ef5VluL9OzFKD - p8wxl+q86yrNz+iZkGZZeAMnz1Fb9f16a8ik4he1B9RwLQpZXXh8+nz1U51fpRar12evVgu21mpRnWt1 - lPLuko3mEiUCQAAIIID18F4j0tXS7Xv9FtPLBSsvThD6u3UwpaC/yvLZtaMyLY8Rpa/+4z8rU+daGfvL - hODkVVvMOQIpYCnEi01A+tfyb5AVCpmpl3t+K7YdOKru/6DA73IkAkAACKBZcfYemaG2+Lky1507by6x - WQ3pW3TwWaz/y89LUa/ffciyaM/oYyWt2Wo2GLW2ri9/J7cSv89fad4iWIlEZvNtCAABIIAABKCLbuuB - b/wOx5/OWNQp7b0yxyBFbkXBph1mc44tOjDZ9BrhVZ61FZafJ0uZV2uxNf88BIAAEEDzfeqHe1VxxW6/ - BSGF8vPkhWabbqAdfv4672Ti0App6JH7fRnqB7xyoQtZ5ib2Hfd/K7Bhz2F9G1DYYvSCABAAAmh+MsZ5 - 1JsL1qoLFvfnu46cMP/+5rE5F4fhbRlhiGAKN++0FMCy7XvN7kFbGz5X/g0/HDPD7Cfwh6wayLMDzYsZ - ASAABOCnmKQopCW3tdZbaRKS1t1bx+XUv/cuQAFco28x5IrsD1FO2rrK9r1gQ8dq9UJWJv9jxpIuLYCL - m4KGZqYNXJSPAIJBADZzFOBWf5y1Wh2zmFhrOlknD+O8qkUgy4AOixWCpp8tV/c9FrP2Z/XnjSxe3+pn - WIpLF2rMvC8s5xZkebKr3gJExLvVjdMz1d3Z2erumaGX2zNn5twwJeMaqjOIWoH/amS6+nD55lYn6xqR - B4BW7Nhn9uDLMqLVbYEIQEYMVo08sqb/SsGq9glAF/Pz2UstVwP+b87nXXYVwB5bL4HIBE/IpZt8HeHZ - 1P2ttHuoziARQGOxylr+xyu2mA/1BPTwjy4+mY2/d1KB33t4+cw73vWZj/NaiUSe+2/Ps/hSzP+WsUgd - OHHa72dL16L0E9i6ogBC/M1HzjfdFVEjU++nOoNIAI0FKyMBudeXZ/PPB9i0v1KPBv4+ZWGLpwDl834y - wVoAIprns9svAOn9t1oJGFG0zly9QACXP854d0XkW2kIINgE0DgpKFfOOyfmqZFF61XVwaMB3RbIY7my - gUhzAcguP/KsvT9OnK0zm3raewvwTOZiy8+OnremhZAQwOUSQOqObqMyHqI6g1AAjYXb2HV324RcFb9g - rdmjf/oSIlhes8/s+mucfGt8AnDHkeOWXXuxZtde+wTw29xl5s5B/hCxdNVJwDAQwK6IUZl9qc4gFUBz - GciVVLbQkn6A1h7oke5BWU1wxbr/rNV41Y79lg/vTFlV3q4mIynUYQvXqrrz5/2K5en0RV12GfDbJIde - ZKs3LYBuCCA0BND01kBE8LNPF5h7Alohz/tLe3Hj7sFXJqSp9HWVlt8/f2ut+RShrY1Skvv7Tz/f6vcz - 5THix6bNa9Fd2GUEEOdRjsRC5ZhQpBzjQy/OcfM+c432/S3VGUICaHprIFfX/cdPW7ThHjL7+huv6lKo - siTX2tN7/T+Z3eZWYBmRLKvZ6/cz5elG2eTT6KLPAtjjvcr58SrlSqtUrtSKEMy2JKdncw+qM2j6ANr2 - /bI1t9WwXrYFu3ls9sXik6J7VF+Nj5w6Y9lcFK+H8t2azdi3Gv3Zz2QstvzMaau3mi3Otq7aByAC+ORz - 5UqvVi5vVeglvWqaK6uGTsBgEEBEXP022U3v2wP5mdLKry1XA2Tm/6IA9Fd5p8Dc8l2Wo4DN+47UjwIC - mAto3FFYtiX3x2EtheekDfiNrvs4cMgLwFs13ZW5HQF0dQFIsctmHgu21Zr7/8sOP/J7c0OOxjf0mG8D - +vbNQI3D7y26aFt7uKdpMTv0z0pr7gmLbcek22BR1dfq7vfyLB84ajy+jD7k6UKrHYVlZCKCsLVjRyBp - LTZe/7TJ24kukXY+IYkAoMsIQB7Wqf3mhDkjLw078oTdUH3P/tj0eeaSnizjyZuBouJTzU5BeSFHTtl2 - c8bfHyKS5pN6jaMAmfCzai2SP1+9c7/6TXqpWcBmgQ1Nupgr9PFlpx/5/OMWnYrSWvw7LRpHO/cEnLCk - TN2iRy8/Hp8bUH7Q8DwEAkAAQS2AnX7W6Y+cOmtusimz+rLbr7QIS+FXHzpmOZSX4fdT3lL/bcH6Cv5k - 8kLLnoBvH+U9rfI37VBx89eq/85eavb7yzZgk1eWq417D7fanZizYXv9CkQ7BSAbkMoIRp58DCRj9EhE - eiUQAAIIOQG0FSlL2Ry0cQnQ/9yB22zQsZq88/esgCSQjuQl1XvVfR8UtDqheSkBtBURVSQCQADBLoBd - +hagoxRV7Dbf+NPafbEcT3YYlmahPa1s7NkWZEQgty2yf+GlVjM6WwC+jTUIAAEEtwDk4Z91HSgIuUJn - rK8yiz/QY0rRyDZgi6v2XHyvQHuQuYsPlm9Wt4zLDrh/AAEgAATQbFgue++/t3SjKttz2Jxdl+27rYbd - 8sfy9zKjL/v0/zZ3ubp2VEabZ8Tl+6VZRzYXkT0JpadfJhYDfWvQ10dPmhN+MjkY8BuJGwSwrvagOXLo - aHLLtiMABBD8jUBSGDLTf8u4HPVE0gIVPXeN2Uwzr7zW3PhDtvSWGXpZLkxdW2l29kmrbeOuQO1dDpPj - Ohp2DZIXh/yhYKWaurrc7BmQR4y/+vqQ+cahCxYz/iItGcEEenyZm5BjvTb7czVxyUY14bOOpEz918yl - li8hQQAIIKhagRvX2eWrnNRyZZP7dRGDLOvJV/m9LAdK01Dj93fWcwYSZ2z9cbs3HFeu7j/7dL5lz4G8 - VnxE8XrzewPtInQ0jHpk9r6jaVfxIwDoigLoqnFoKUhnn9XOP7KiILcRUfGedm0s8hcJAgAE0LZ3C0ib - 7imL7j95VZjsTyiysCEABIAAQityeyD7CsjeAWctNiWRBqVfeYoZASAABBCam03WP4Mwp3yXZTegPIos - rco2BIAAEEBojgQe+miWWehWyF4A8swAAkAACCBE80t3sblEWHXwWItUHDyq3l+2yexw7KxVCgSAABBA - F4osv0kPwo/emdkiN+rcMHqGuVxpYwSAABBACM8LWIRJQASAAAgCQAAIgCAABBDCOGKS79Mn1U4KK5gE - kK6ckxEAdM4I4C6dLRRWsCRZ2YdlKOfUtbpQEAB0WADuG/WJtYjCCpLIC1aGZylXUlmoFj8CuMy3AFH6 - xJpIcQXRA06JhcqVVoEAoOM4Y5IMe2zKk0wEBkniUuvfChS6xY8ALvttwNBpUY6YlAn6BKujyLr41f+d - Ocrl2YoAoBMFEJ9m2GJTrtcnWKrOaQqtCw/9k8tCvfgRwGXnqTdEAIY9Oul6R0xyvD7Zyim4LlT4su4/ - fqEu/o3hUPwI4C82EohOMhyvJ0fok+4+fUvwqv6aobNY/3qNzhdW0d+zJjLBXXNlYuqFK0enKvJteiam - qoh4/1t/OYZlKMfIbP95K1c5xsxWzvcWKee0dfLG3FBe9kMAXWZVYGiyFkGyEfFGisMem9zTHuPupYu8 - d6uJTul9f0HOq/3m++p0FKlPf50HZ+eq3hPSWgog1q2ck5Yol7tc39P7y9b6opdXgKeHTeEjgK6ALSHL - cEbrW4KYZOkTMHSRXzKDS/NeGFySd1ZHkYaU5qn+C3zqmole/wL4cHm4FTYCCFUGlSAAf0EACAABIAAE - gAAQAAJAAAgAASAABIAAEAACQAAIAAEgAIIAEAACIAgAASAAggAQAAJAAAgAASAABAAIAAEgAEAACAAB - AAJAAGGQqa6s7VFUFQJAAOGZid2ydzioqiBicInvWX3Cn6DoEUAHU6fzmhYARRVkI4DH9Qm/k6JHAB3M - dpe3eggVFWQ8XOy7Xp/wCyh6BNCBnNNJjvQwARh03LXaq28D8p7ROUjhI4B2ZoO++t+rBUBBBRvL/v2s - MaQ4N0qf9H/SqaX4EUAbr/xrnenVjxtTK2xUU5AypNhnDCjNjxhUkv+IPvk/0pmtMz+c02++b2HvCWmb - HLEpuxxxnj2OeO8xu5n0Y1oAtS5vZYU++cM15TordN6N8FbeaczcZXOkVlFIwcrARblG/5ICQwvA6LO6 - 0HisqCByYFFOz4FF2WGZQcU5Pe/OntGrx9ueByIT3E9Ejsj4hXN0wbOOxMJnHaN1Pl75pD7xB4drdOH3 - 0wK8yZh4wK5/b+gRAEUUKvw6J9t4tKjA0AII22gBGH1yZxg93/YYWgBG5Ih0w5mYbzgSC3QKDS0AQ078 - cI0WgE6lYZ++i4IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIVv4fheb220fhoCoAAAAASUVORK5CYII= + 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..3ba878d0 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -0,0 +1,24 @@ +using System.Windows.Forms; + +// 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); + Application.Run(form); + var dsn = form.Builder.ToString(); + return dsn.Length; + } + } +} \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj index d0795533..2aac5afc 100644 --- a/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj +++ b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj @@ -62,17 +62,12 @@ - - - - - - + Form @@ -80,31 +75,24 @@ DSNEditorForm.cs + + True + True + Resources.resx + DSNEditorForm.cs ResXFileCodeGenerator Resources.Designer.cs - Designer - - True - Resources.resx - True - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - + + + diff --git a/dsneditor/EsOdbcDsnEditor/ODBC.ico b/dsneditor/EsOdbcDsnEditor/ODBC.ico new file mode 100644 index 0000000000000000000000000000000000000000..a3c48809eeb0fe75a706c7be0e02ca9f62db843c GIT binary patch literal 21238 zcmeI4ONbOl7{_ZJ-ST%)wkd2p+r%F)BMbNFI`l z2f?G_LBSkU5E0_zpjkm88clqlE>YA&d?G69?&SCH=~~L!n(5w|o}DSyhTm3oJ-+(> zUv>A+bWgQ0ZDxX*J=>7ZHv6U-^RY3ex3{$4wb+iN*RmZxZcI!E z=>pgU2jLn#gCY#VLpTq+VJ*yoiBR*VzAzbnSXHb}jxZOmNK zo$vsvjaR8|h<;aL19U>-rZymd7sFYQjY{^@mcPJ;<|Pl2HlHJLoyJ}PHz2M3H0`A0 z2q>l_e6VGty^!_Tu77*6KSkrYoAyD+>=|&|XPJM5?e&m@;Z1p%1Un$h@myQ?u)QuG zXLxL7C=>IcA8vu-p9MFua~%%DGI&e3b%Jt@uQ{370A^rwDnvT%n5~e0xv1O3d_npH z6rit0)K`2DtKkD^gNjmB2RYaU#SD3gtv}!^&=_7H;<{<1$05t{t*wjLou_epo66yR z_y=sCtkW&=I3rw$%V&}P+raVnW4l9Rm2WD8W{=9=Hv! zA<38Lw7CS^U>+!*sm3gCq<%3SqzhmZ9E5A|4CKooJcRS0IIjhrOB12oFOA7T+hZ6Y5ABdIvYj-huTY|gRODmqe&0#z)pme7Xos9^$tL79hQ=xu zu*ulEPtgkn(EW^P>n$bjUrZVow=U~E-xKs5r5p4;Z5NyXeTR7h?)Rxz?B9iRun#uF zQs}JLCs(U4U$6c0{`jnWJFDOT=su3CbF}$4^gj(7AzwaLnth*Uw=9i~G;Q#&3-q1) zDU4QUn)bTqaSWD2TN&w%)t9f-Gn#-OkhBKFw}f75#cPY3+oV>N(x#u2x^(r=NZcp_!j+>W#NP6ReOn zpJunLTtEHV;R@92L$mtpy#Lzja<%&Mww+DsyES0#_wjv^)uPm=*)4P1(pS&-blx;; z?&=v=&zfhsdTP-B3pO4Hgq}kmg*7mtT>EJC`}&7o<{s<5eCYa;{P$^g%aZzsUhiu4 z+4wb ze}$hv!h55MgR9k-$KJPr=(jQc9e<~!je$?ITZXM)1JkcK{Fb5qBLV7H3|6O!fjif} zJa#Sxs{gNy^#2#AejNk)$$HhzH|K5{HU z-@n8$Scr`@YjaxhJdjVXpr+qHNxwGwe+R9qFO6Tij@9)0w^+Z{S?v!z1~vcwF^+-O z7HZvIns~Qb8(jx#{{5@;Yr}@t`dtVt2GU;l@1IK%h+;4uds%=rw0b09)Gjy ztN-i;p*N)z{q`!)Lu4 zqZ`)4E;s>thsYCfW1(x0A0yOE?ZJy6rG;o;??cem6Q_M8b0#($Ilx%STQn@q z<)z#xrPd9b=7-%X)py4GY18ul)~-!s`RUyr)@gOyI1I<75DChUh~Gd|ZXM4L$KB3@ XofkV#kpaz{oku&bj8yVBn)m+!5Ndo^ literal 0 HcmV?d00001 diff --git a/dsneditor/EsOdbcDsnEditor/Properties/Resources.resx b/dsneditor/EsOdbcDsnEditor/Properties/Resources.resx index af7dbebb..1af7de15 100644 --- a/dsneditor/EsOdbcDsnEditor/Properties/Resources.resx +++ b/dsneditor/EsOdbcDsnEditor/Properties/Resources.resx @@ -46,7 +46,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Serialization.Formatters.Binary.BinaryFormatter + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -60,6 +60,7 @@ : and then encoded with base64 encoding. --> + @@ -68,9 +69,10 @@ - + + @@ -85,9 +87,10 @@ - + + @@ -109,9 +112,9 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.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/Properties/Settings.Designer.cs b/dsneditor/EsOdbcDsnEditor/Properties/Settings.Designer.cs deleted file mode 100644 index 6f71f0b6..00000000 --- a/dsneditor/EsOdbcDsnEditor/Properties/Settings.Designer.cs +++ /dev/null @@ -1,26 +0,0 @@ -//------------------------------------------------------------------------------ -// -// 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 { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.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/EsOdbcDsnEditor/SQL_Icon.ico b/dsneditor/EsOdbcDsnEditor/SQL_Icon.ico deleted file mode 100644 index ea70cbec4c51dfa2cc375c43b988d724839c7c24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5696 zcmcIocRZX=w|{I1Aqf(_MM8*Z!7p0EE+LW-y?4=x-og?~LW1ZC7OVFfJvxzuC`Ihl*Z=_i&Qk?B?N<|-Q6x-kjgT7#!mu=lSDI6RQaciJ3n7YGGjKo~Tp##J;wX)`?0G#LL0 zs^kJa(+3$)&{&b9Znh!t&D=?<=#cJPh$7r6@;Lo7_x`|M%WNZN1~YBTL>$7uFVTE> zDB=?(@|oMLlQHk7MCiv}QD@a;dnS>ep|ts84|`lcq+3ez^xj`ct(Y2^2`%?72D7BS#oUE$ar_k9=POUw~KHBn4pQ5+fyWr9;4Grt}342cV$>$oG)TpFzm*ugwL{T#dl74K>Bl9V=CCYq%^MH=#y*>7!dp% z&_iFBjn7g75HV_8gT;QDI_-ZQ>&1 z%K2YK2=J%>-(-KN0TN>NH?Kcj0ML2%hv&bs>wgXy^5;jS=VSP%iNFSO3G%ZM7Z`A8 zPrAZ>2%^QqLkHs{NP5q{&^_0gOR!3AMX2cR`=c`@=GNBNnhgzG0fT>ysjBE{bjS#j zWh^kkv)L?{W0sjET1TZSs)cpv6%?)>HlaUw;AMVYve&vMNYcidoPt6y-dc%` zS6B|P2;-97w5~5-BIRL^pq~VEe0_g&VhRHBnOR ziY##@Vgb$W+{-mh{gKZ#B%b$c2kIp~N?s&E$ojTyuufH#XBGuhB-Wu75z-Cg$=|DV zq#iTk=-Zsqm_3zo{naG7zSD(wiPHKiHbnmMKjij*)1=dKpW0<3mCE_|s`Z|sJj`X- z&Hl!w5kp`k{4eh2iK(SgkstCv;Xa5-6Ah*Sg!m7}G_N!Y*%_U9b}&fXm#3QJF`1)Y zz5Me2qj=l{UP0eZDX;1CIf^WdDpKA;qVL~fvoI2j-!|( zzw+peiq2>ZdH5p_Ue5Xm7?8UBymIaOdhP)={-^b{7+73!IyE%}$nf~A$ZJ-rR-zKl zFL`SZ)Qr7j-Uhk>f+1-j-A=e~hDvT^(STLR+}Q~m@xLM@N$xHknjB;d-0?xN9Wv-L zI4W~W;4L6zw5O)heJb5}r)(~)>EQyh1WsInkS~;TQ7Zr2+Ju$e0_@F2vf}sk5SR_7 zpWUyrMV|J>83+0k?PIQy5mx#X;}>mVk9K+7ukNFQEj0SER50UWeMc|VNGdtNMJeif zCK!|FQ#rylv|KowBdjUBD4<11sgrMBG%8C=*@5HUeZ1v`!5^cG=4T>0H@iem$w)Y1 zpBZ7s)`$vcujvAneYL{d=xq9w5!(^yFm9TBBriZ|I8^4{@Ji#=0+f+=kz0gBLEwHu z(TzxWVFR`D{XpHpgoUZU#3u!Irz%{_suUj5WpG+y; zefvzSj*wGB-VEbv*o)r+fanC`R>|3G=>v3zYrMs`2$T8!^oS)921 zW3jXMDFm|f0rLWI|MlXCK*he6tMm&89i78mS}VyRHeO^crzYulCu)sM)DfLCmifaN zY(Z`c$IPVAO&HXW79wy?%>(dtug@a2#afD-Ywj%R&TSm;CDf7bt_(L27a>s(yH34`6jeczCWN$X)>Y9aP8dyE9RSA-j8vAvP(_8SJmmq%l^%2Po}V zWAOpZX!0iaOe%wAo&)y7K>p7b{T2bN>fU^cOi4c_f{B`jT47y2TYNr<2_m!Fh# zC+Buu&8U(6wYbq~qt2z_)^tQv5x$IH6B>nyS4Fx`!D+k?3t(X0`tAPUIi%es*dEAt4?+KUb@K^XV;P|AXO z7tUOeD<K3?6b8GZ3g*4iQ>A6VLZE!u7f+)7@lniK}sPU{RW zF=2tjvn#>?{Zs(HMLYM#cu*iwXL%JqNhJ9tu8MlhNf-l8U!8XoGr?cCH+Pnhzyw=_wt#1YfRs2 zUW&>N#9h0kZ(7yAfpbjtck~CjcE&Cwm_RFR#!T^~+mln)%SgF1nhMX6r)~FW+REN* zzyk`Aob(V6=B`iLwF7alVWS+Yv0HKm&t1Pa5K2Z~jYOR$P-9dKbY{wUxcG)GsK$yK z_Et{Am)5X~Cf>~3licr2t%@ZCVa^nse~}JgM|uxe!Mc&ZDFfjk0+-P z>-8yN798kUdQ6vD8mSqF$E$&7(T8G7BELKH`W%0mj9Zh^tY}1ilV3_w&tY45B#d<1 zll6!^I29aWmIvASIdbZaop!1jRnK#X;&H6yXw8C3?y-p>=qVUJy{v1_94k7G zdyHBfTFvSQjW0BJAMe~p@*4W`_eN4o)lY*NuEOttD&peR=Tx zcYGF9+bl9VRoW6|OjrJHcRIOmypH7*)o&$Gd(vxPT$e0}tSy@$=L8)sdMrIF*A;Tn zS-sutkj!5ks@i5w15q$O6-PbCOkOyK4|bOmP10(akOg4%eDm9;c1;qpVSV_h;b;&j ziOOzDL%;U+VW)*)$V$$YqsTJ6q-z&do6-I0Pxb61o?awRDtc@egy%`2%^iV)JMhj1u-%4=_UwN?mc3M54 z|7;xVyBwD$u<$b_DC@UYpQ@biO(j*7x5tm!8@J+?8$Fn2eu!Q68*58 zAPn((bd4x$N9RhuDqQ7cc{PRdyxt9I8>x?p^M_h9ngpkI9OaqSu6$1#%;mTrPX5jT(5ODK+nQrAr&lBz7Ar<4j0$T(SJx+xx~`CsO?#6D5*yG;Z| z3MrCmdOl?oTeaJz0m4+!2x;4XOD&^0xHWEY7EU zvEQMnm$xk`7mWZ9SU&sJC2k&3d|>Qf4uQthM^GoY=+=Gcx%wp#IQPN>FZG3>nASSa z?7Nd5SMkv66l+JIIi7U$t}ZsU@BxX-*;`noOcYfjwW?_7w&}^fEq?C>62t zrqQXRN43}lW#9KDGZS3cZYJ~DZ6G!Yz4({$6m}e^9}z4c{Ir>&qu*;?&bR@ zT1#X+3>PTv7AN+nf!IZna^UO|3|4i!Oo82A0Cc|pP}o!Usi;@b_uAHv&}MsBG-Fo# zbHjC`M2GSZ4P&@K>r?8l(KB$=EeZ70J-^U;V|&X#O%(l9`fr~?h~+Pb9^h5JHen_cONv>YeqSmEoEObsE4^AJ}5u5370ql`^i2 zN;lM&PfydJfB_8p(pv{;;Jzh4aU=V6K{YI-`;%@Ekg*_X8Fv}_vO8Xd^y1Usn(MQa zbPeC50U^9o+km}BRMN*wIMU+mu7;x#0z#^L2^9`xTqmPRw;6w-RW`<}Uxc)$ETy78 zjqj0fKB8^2UMZAN077M?*Zh=J+?!-k-F#n!w2vycz^^lf&LZd4PblVu#jm|r6=f^u zRa%YTk_}MaE;>ujtkNg=MIsq>f6Z3$h_;(}SieAr@GIrt-F5Qg$t!yQieMBd6>5E_ zv+E_nmtRlnN3SmOkb!Td+H3d6l)CA`Bf!K)$K0-xL9UW^=W%!kN6OR?$HG=_M3YZ( znCeidOW97LgW z@e?NyNHk)Ags8E9idp>k%D={4{wDYzffkFH!m^T04Q+Z5aZe8qo38YcEGXi9;Tnc9 z2>6o= zjl$j|Ve*`+nJ9L-u$%Tv>Ird@-ZpPqaI?i^5IHi|p0m>uksL{hXX)6$!NS}lS_o_T zi6dfdB$`!=|E0E8jA+id?d3at(M;NLg!i(?;6Zq)7&vvvrNzYpbICr1t%9$S%`HtP z>J>($5stMq1Zsp_tV3jvJMJ$@^Lx%jZtKq2_fA=R29u}+8?BdvVhBdmy*FC(|#3qPNtFHx;~|*5Y=@Q;^Em|74_|_Eqk_?tm$!% z><;-mN6-1gAyCuLq*rZafiA1GfFj-96XGHNRdqqz0ElAJj)W<|-Ihsa_JGL{c)a-D zvjd*wB6*FDZeyPDKzHlt>-wjHRWctsqKEfg%MfMGR0Y zMX|NUav2CHQ;bKdtPJ(~Rc&&v9BpE>R_$5!*xVdmp!G|2oYC7I9P`eoU}{(|{o z)R21_d=D^y0SsUO0~o*n1~7mD3}65Q7{CAqFn|FJU;qOczyJm?fB_6(00S7n00uCC z0SsUO0~o*n1~7mD3}65Q7{CAqFn|FJU;qOczyJm?fB_6(00S7n00uCC0SsUO0~o*n z1~7mD3}65Q7{CAqFn|FJU;qOczyJm?fB_6(00S7n00uCC0SsUO0~o*n1~7mD3}65Q z7{CAqFn|FJU;qOczyJm?fB_6(00S7n00uCC0SsUO0~olW2F8pWu9R0_QQq-mYHMwk zVHozPud7qezD&*k({oB1I{1bj<0y>q{l@?XFn|FJU;qQ33Ihx0y`ZMXMn$3_H8dTO zqvCbt7tL0F;U86UHpTv0G#XJwd6}k8obajmo{)v_WuE!@hXD*=00S7nzzs35Y5h98 zPwJ|BPnm~?Y3IH{y^j5dA5>jKj)K9U7R;G_LyncqNM^(U1~7mD3}65QiEZFtt5zx+ z4eMmloL)0&Z&}sqr-niy&H2MKTJg^mRXUxDMj{IM{mMOdR5PbcCc9E=VE_XdzyJm? zfB_8j+raeSJZXYXG^gEkc8Fg3IcNB>PHOrBv5yzHw8|z)U z3Wvj*GI2Z^mAVN77{CAqFn|FJU?3&~lYae}ea-2i++W6(PpypF(psdYA0O?NLV9}FYl32mpBiqtfVBStb8s0J-i41 zFED@s3}65Q7{I{IZQ%Hk!)88RF`#_)u?gher$}r_$HQ00uCC0SsUO0~olffrHum&GY+d1Ink?HK;y+jXa@%ol`q} zO{y+Q*NKehwR`1I9Zj1mfBSir6c=gi!w+5cc&K-7?s|v$WBxFJ0SsUO0~m=3vp-Etiuo3d;90_N!G3fe7Gbk&Tyf88T?J{>c1IQ0_s5(Y4U0SsUO0~o-7 zoej@__~cddsg+r)cipYdHm4e!b9Jt2okC%sD$PFW%o;;qExeFRt!&ygZY?sD-EPR| zaa~x$f7$x|uJux9a$b?Ex*CgE!~g~`fB_6(00S7X4R~5w)OccUpZV0vs8wm-HP=i9 zRN7(asVYOa^pZ=v@~QRsCG+|1Erx=D7VTL*vPU1)x<=L4TBEnuuE8T7F@OOKU;qOc zzyJnp1BVZOAisNmO!>6#z&Hh?hQ3|$zdq@udGt!ZU;c@qaHn4f-X7O2qn^#*pys9~ z{cLm+9`T3)3}65Q7{CAqFkl;)GjoP{jz21Q)=&G)sQ$VG+SJ^?Y}%Ds=erF>%zdYh zrA@QfO*v0*RYxG8S<|Lq5sMhW00uCC0SsUO1KkW9I*_BV-z``Ak9*0ex2Yy$xS9%H zRMgC|_msz%PpzCfx!O?3?7v#Q)Qa*lO`h;Xx8oS-S}$JDdO-|e00S7n00uCS7zQ4B zaHM^o(MZsv#{6W}?)!%h!*83lep0i&*8c}( z@%K-y9J|2WhpNospl?bK9|IV`00uCC0SsUuCIk6-xypUVrbz zFaNVInfdfwMMX@5FUg}Vbi&hc-1)???(Z#v@3s99$3wbJPnYxk+Iu2+4< z`(g_N7{CAqFp!uA7S2yrC=^nCMMwuzy~;^3;aye7eQFXX)kZp8ERynVJi| z)49*ij43B$D=}>`ugoh3Fn|FJU;qR08kjn9f~ zouy_bHG8er{{HDZ()7^%_g(X2i8p`qI%+ozU;qOczyJm?kXQyL{pv9}3JdM*dD?F% zrNPh)^Blf)9)Gd92i0M(p@^AFTRk4V_VQoJrPN6nzyJm?fB_6(00aLQ19N81P}ZId zxod0f>j11?X?t6neZSOYDgTgl?^J4&#Hvl0Pv#Q?7{CAqFn|FJU;qOczyJm?fB_6( z00S7n00uCC0SsUO0~o*n1~7mD3}65Q7{CAqFn|FJU;qOczyJm?fB_6(00S7n00uCC z0SsUO0~o*n1~7mD3}65Q7{CAqFn|FJU;qOczyJm?fB_6(00S7n00uCC0SsUO0~o*n l1~7mD3}65Q7{CAqFn|FJU;qOczyJm?fB_6(;6@sd{tLyV)O-K{ literal 0 HcmV?d00001 From 468a84f30b2fe776aea5d83dc767647776bd9a5a Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Thu, 4 Oct 2018 21:20:49 +1000 Subject: [PATCH 09/35] Fix ESODBC_DSN_EXISTS_ERROR error handling --- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 89 +++++++++++++++------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index 3bb4cc8b..2ea796dc 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -16,6 +16,8 @@ namespace EsOdbcDsnEditor public partial class DsnEditorForm : Form { + private const int ESODBC_DSN_EXISTS_ERROR = -1; + private DriverCallbackDelegate testConnection; private DriverCallbackDelegate saveDsn; @@ -32,7 +34,6 @@ public DsnEditorForm( AcceptButton = saveButton; CancelButton = cancelButton; - // Save the delegates testConnection = connectionTest; saveDsn = dsnSave; @@ -40,36 +41,36 @@ public DsnEditorForm( // Otherwise it's a DSN editing, so it's going to be a "Save". saveButton.Text = onConnect ? "Connect" : "Save"; - // Parse the connection string using the builder + // Parse DSN Builder.ConnectionString = dsn; - // Basic Panel - textName.Text = Builder.ContainsKey("dsn") ? Builder["dsn"].ToString() : string.Empty; - textDescription.Text = Builder.ContainsKey("description") ? Builder["description"].ToString() : string.Empty; - textUsername.Text = Builder.ContainsKey("uid") ? Builder["uid"].ToString() : string.Empty; - textPassword.Text = Builder.ContainsKey("pwd") ? Builder["pwd"].ToString() : string.Empty; - textHostname.Text = Builder.ContainsKey("server") ? Builder["server"].ToString() : "localhost"; - numericUpDownPort.Text = Builder.ContainsKey("port") ? Builder["port"].ToString() : "9200"; - - // Security Panel + // Basic Panel + textName.Text = Builder.ContainsKey("dsn") ? StripBraces(Builder["dsn"].ToString()) : string.Empty; + textDescription.Text = Builder.ContainsKey("description") ? StripBraces(Builder["description"].ToString()) : string.Empty; + textUsername.Text = Builder.ContainsKey("uid") ? StripBraces(Builder["uid"].ToString()) : string.Empty; + textPassword.Text = Builder.ContainsKey("pwd") ? StripBraces(Builder["pwd"].ToString()) : string.Empty; + textHostname.Text = Builder.ContainsKey("server") ? StripBraces(Builder["server"].ToString()) : string.Empty; + numericUpDownPort.Text = Builder.ContainsKey("port") ? StripBraces(Builder["port"].ToString()) : string.Empty; + + // Security Panel + radioEnabledNoValidation.Checked = true; // Default setting + textCertificatePath.Text = Builder.ContainsKey("capath") ? StripBraces(Builder["capath"].ToString()) : string.Empty; + if (Builder.ContainsKey("secure")) { - var val = Convert.ToInt32(Builder["secure"]); - 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; + 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; + } } } - else - { - radioEnabledNoValidation.Checked = true; - } - - textCertificatePath.Text = Builder.ContainsKey("capath") ? Builder["capath"].ToString() : string.Empty; } /// @@ -77,6 +78,11 @@ public DsnEditorForm( /// 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 allowOverwrites) { var errorMessage = string.Empty; @@ -84,14 +90,29 @@ private void SaveButton_Click(object sender, EventArgs e) if (!dsnResult) return; var dsn = Builder.ToString(); + var flag = allowOverwrites ? 1u : 0; - int result = saveDsn(dsn, ref errorMessage, 0); - if (result >= 0) + int result = saveDsn(dsn, ref errorMessage, flag); + if (result >= 0 || (allowOverwrites + && result == ESODBC_DSN_EXISTS_ERROR)) { Close(); return; } + // Specific handling for prompting the user if overwriting + if (allowOverwrites == 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"; @@ -153,7 +174,7 @@ private void CertificatePathButton_Click(object sender, EventArgs e) private bool RebuildAndValidateDsn() { - // Basic Panel + // Basic Panel if (!string.IsNullOrEmpty(textName.Text)) Builder["dsn"] = textName.Text; if (!string.IsNullOrEmpty(textDescription.Text)) Builder["description"] = textDescription.Text; if (!string.IsNullOrEmpty(textUsername.Text)) Builder["uid"] = textUsername.Text; @@ -161,7 +182,7 @@ private bool RebuildAndValidateDsn() if (!string.IsNullOrEmpty(textHostname.Text)) Builder["server"] = textHostname.Text; if (!string.IsNullOrEmpty(numericUpDownPort.Text)) Builder["port"] = numericUpDownPort.Text; - // Security Panel + // Security Panel if (radioButtonDisabled.Checked) Builder["secure"] = 0; if (radioEnabledNoValidation.Checked) Builder["secure"] = 1; if (radioEnabledNoHostname.Checked) Builder["secure"] = 2; @@ -179,6 +200,7 @@ private bool RebuildAndValidateDsn() private bool ValidateCertificateFile(string file) { + // Simple validation if (File.Exists(file) && File.ReadAllBytes(file).Length > 0) { return true; @@ -187,5 +209,14 @@ private bool ValidateCertificateFile(string file) MessageBox.Show("Certificate file invalid", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } + + private static string StripBraces(string input) + { + if (input.StartsWith("{") && input.EndsWith("}")) + { + return input.Substring(1, input.Length - 2); + } + return input; + } } } \ No newline at end of file From be4f4a8932129f0573cc36ec51b046a27705b360 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 4 Oct 2018 21:11:05 +0200 Subject: [PATCH 10/35] b/f: conn str writer returned wrong chars count In count mode (output string is NULL), the function is supposed to return the min size of a buffer that could accomodate the connection string. This commit fixes it's calculation. A unittest was added for it, too. --- driver/dsn.c | 53 ++++++++++++++++++++++++++---------------------- test/test_dsn.cc | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/driver/dsn.c b/driver/dsn.c index 2bbb75e0..de08ed02 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -661,30 +661,34 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, wchar_t *format; struct { wstr_st *val; - char *kw; + wstr_st *kw; } *iter, map[] = { - {&attrs->driver, ESODBC_DSN_DRIVER}, - {&attrs->description, ESODBC_DSN_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_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); + for (iter = &map[0], pos = 0; iter->val; iter ++) { if (iter->val->cnt) { braces = needs_braces(iter->val) ? 2 : 0; @@ -699,12 +703,12 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, continue; } if (braces) { - format = WPFCP_DESC "={" WPFWP_LDESC "};"; + format = WPFWP_LDESC "={" WPFWP_LDESC "};"; } else { - format = WPFCP_DESC "=" WPFWP_LDESC ";"; + format = WPFWP_LDESC "=" WPFWP_LDESC ";"; } n = swprintf(szConnStrOut + pos, cchConnStrOutMax - pos, - format, iter->kw, LWSTR(iter->val)); + format, LWSTR(iter->kw), LWSTR(iter->val)); if (n < 0) { ERRN("failed to outprint connection string (space " "left: %d; needed: %d).", cchConnStrOutMax - pos, @@ -716,7 +720,8 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, } else { /* simply increment the counter, since the untruncated length * needs to be returned to the app */ - pos += iter->val->cnt + braces; + pos += iter->kw->cnt + /*`=`*/1 + + iter->val->cnt + braces + /*`;`*/1; } } } diff --git a/test/test_dsn.cc b/test/test_dsn.cc index a435113a..52341102 100644 --- a/test/test_dsn.cc +++ b/test/test_dsn.cc @@ -93,6 +93,44 @@ TEST_F(Dsn, parse_write_connection_string) { 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, sizeof(dst)/sizeof(*dst)); + ASSERT_TRUE(0 < written); + counted = write_connection_string(&attrs, NULL, 0); + ASSERT_EQ(written, counted); +} + } // test namespace From 980b34ad608ed4c2c78463963596dff435a88519 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Sat, 6 Oct 2018 11:50:34 +0200 Subject: [PATCH 11/35] refactor DSN validation - place all functionality in one function Also: - fix registry writing for update and new DSN addition. --- driver/connect.c | 64 +----- driver/dsn.c | 209 ++++++++++++++---- driver/dsn.h | 10 +- driver/setup.c | 101 ++++----- dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h | 16 +- 5 files changed, 243 insertions(+), 157 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index ddcf2647..e72eeaac 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -773,6 +773,7 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) if (state == SQL_STATE_HY000) { RET_HDIAG(dbc, state, "invalid configuration parameters", 0); } + cleanup_dbc(dbc); /* release allocated resources before the failure */ RET_HDIAGS(dbc, state); } @@ -1559,60 +1560,14 @@ static BOOL load_es_types(esodbc_dbc_st *dbc) static int receive_dsn_cb(void *arg, const wchar_t *dsn_str, wchar_t *err_out, size_t eo_max, unsigned int flags) { - esodbc_dsn_attrs_st *attrs = (esodbc_dsn_attrs_st *)arg; - esodbc_dbc_st dbc; - SQLRETURN res; - int ret; - - TRACE; assert(arg); - - if (! dsn_str) { - ERR("invalid NULL DSN string received."); - return ESODBC_DSN_ISNULL_ERROR; - } else { - DBG("received DSN string: `" LWPD "`.", dsn_str); - } - -#ifdef ESODBC_DSN_API_WITH_00_LIST - if (! parse_00_list(attrs, (SQLWCHAR *)dsn_str)) { -#else - if (! parse_connection_string(attrs, (SQLWCHAR *)dsn_str, - (SQLSMALLINT)wcslen(dsn_str))) { -#endif /* ESODBC_DSN_API_WITH_00_LIST */ - ERR("failed to parse received DSN string."); - return ESODBC_DSN_INVALID_ERROR; - } - /* - * validate the DSN set - */ - if (! (attrs->dsn.cnt & attrs->server.cnt)) { - ERR("DSN name (" LWPDL ") and server address (" LWPDL ") cannot be" - " empty.", LWSTR(&attrs->dsn), LWSTR(&attrs->server)); - return ESODBC_DSN_INVALID_ERROR; - } else { - /* fill in whatever's missing */ - assign_dsn_defaults(attrs); - } - - /* try configure and 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. */ - init_dbc(&dbc, NULL); - res = do_connect(&dbc, attrs); - if (! SQL_SUCCEEDED(res)) { - res = 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(res)); - ret = ESODBC_DSN_GENERIC_ERROR; - } else { - ret = 0; - } - - cleanup_dbc(&dbc); - return ret; + /* 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 @@ -1743,10 +1698,11 @@ SQLRETURN EsSQLDriverConnectW 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 */ diff --git a/driver/dsn.c b/driver/dsn.c index de08ed02..5e21a25c 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -591,63 +591,116 @@ BOOL load_system_dsn(esodbc_dsn_attrs_st *attrs, SQLWCHAR *list00) 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_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; - } - } else { - assert(0 < system_dsn_exists(&attrs->dsn)); - } + /* check that the esodbc_dsn_attrs_st stays in sync with the above */ + assert(sizeof(map)/sizeof(*iter) /* {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(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(iter->new)); + return FALSE; + } + INFO("DSN `" LWPDL "` attribute " LWPDL " set to `" LWPDL "`%s.", + LWSTR(&new_attrs->dsn), LWSTR(iter->kw), LWSTR(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(iter->new)); + return FALSE; + } + INFO("DSN `" LWPDL "` attribute " LWPDL " set to `" LWPDL "`.", + LWSTR(&new_attrs->dsn), LWSTR(iter->kw), LWSTR(iter->new)); } - DBG("key `" LWPDL "` with value `" LWPDL "` written to " SUBKEY_ODBC - ".", LWSTR(iter->kw), LWSTR(iter->val)); } return TRUE; } @@ -891,6 +944,74 @@ BOOL read_system_info(esodbc_dsn_attrs_st *attrs) #error "unsupported platform" /* TODO */ #endif /* defined(_WIN32) || defined (WIN32) */ +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 ret; + esodbc_dbc_st dbc; + + if (! dsn_str) { + ERR("invalid NULL DSN string received."); + return ESODBC_DSN_ISNULL_ERROR; + } else { +#ifdef ESODBC_DSN_API_WITH_00_LIST + /* this won't be "complete" if using 00-list */ + DBG("received DSN string starting with: `" LWPD "`.", dsn_str); +#else /* ESODBC_DSN_API_WITH_00_LIST */ + DBG("received DSN string: `" LWPD "`.", dsn_str); +#endif /* ESODBC_DSN_API_WITH_00_LIST */ + } + +#ifdef ESODBC_DSN_API_WITH_00_LIST + if (! parse_00_list(attrs, (SQLWCHAR *)dsn_str)) { +#else + if (! parse_connection_string(attrs, (SQLWCHAR *)dsn_str, SQL_NTS)) { +#endif /* ESODBC_DSN_API_WITH_00_LIST */ + ERR("failed to parse received DSN string."); + return ESODBC_DSN_INVALID_ERROR; + } + + /* + * validate the DSN set + */ + if (! (attrs->dsn.cnt && attrs->server.cnt)) { + ERR("DSN name (" LWPDL ") and server address (" LWPDL ") cannot be" + " empty strings.", LWSTR(&attrs->dsn), LWSTR(&attrs->server)); + return ESODBC_DSN_INVALID_ERROR; + } else { + /* fill in whatever's missing */ + assign_dsn_defaults(attrs); + } + + init_dbc(&dbc, NULL); + if (try_connect) { + ret = do_connect(&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 connection failed: " LWPD ".", err_out); + ret = ESODBC_DSN_GENERIC_ERROR; + } else { + ret = ESODBC_DSN_NO_ERROR; // 0 + } + } else { + init_dbc(&dbc, NULL); + ret = config_dbc(&dbc, attrs); + if (! SQL_SUCCEEDED(ret)) { + ERR("test DBC configuration failed."); + ret = ESODBC_DSN_INVALID_ERROR; + } else { + ret = ESODBC_DSN_NO_ERROR; // 0 + } + } + cleanup_dbc(&dbc); + return ret; + +} + static int test_connect(void *arg, const wchar_t *dsn_str, wchar_t *err_out, size_t eo_max, unsigned int _) { diff --git a/driver/dsn.h b/driver/dsn.h index b2c7ce84..bfc439e2 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -69,10 +69,12 @@ 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 *new, esodbc_dsn_attrs_st *old); BOOL TEST_API parse_connection_string(esodbc_dsn_attrs_st *attrs, SQLWCHAR *szConnStrIn, SQLSMALLINT cchConnStrIn); @@ -80,11 +82,15 @@ 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); /* Uncomment to enable 00-list format (vs. connection string, - * `;`/`|`-separated) at the interface with the GUI API */ + * `;`/`|`-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/setup.c b/driver/setup.c index 0a74bec2..e43089cf 100644 --- a/driver/setup.c +++ b/driver/setup.c @@ -123,80 +123,84 @@ static int save_dsn_cb(void *arg, const wchar_t *dsn_str, size_t cnt; int res; esodbc_dsn_attrs_st attrs; - esodbc_dbc_st dbc; - BOOL create_new, remove_old = FALSE; + BOOL remove_old; esodbc_dsn_attrs_st *old_attrs = (esodbc_dsn_attrs_st *)arg; wstr_st old_dsn = old_attrs->dsn; - if (! dsn_str) { - ERR("invalid NULL DSN string received."); - return ESODBC_DSN_ISNULL_ERROR; - } else { - DBG("received DSN string: `" LWPD "`.", dsn_str); - } - init_dsn_attrs(&attrs); -#ifdef ESODBC_DSN_API_WITH_00_LIST - if (! parse_00_list(&attrs, (SQLWCHAR *)dsn_str)) { -#else - if (! parse_connection_string(&attrs, (SQLWCHAR *)dsn_str, - (SQLSMALLINT)wcslen(dsn_str))) { -#endif /* ESODBC_DSN_API_WITH_00_LIST */ - ERR("failed to parse received DSN string."); - return ESODBC_DSN_INVALID_ERROR; - } - /* - * validate the DSN set - */ - if (! (attrs.dsn.cnt & attrs.server.cnt)) { - ERR("DSN name (" LWPDL ") and server address (" LWPDL ") cannot be" - " empty.", LWSTR(&attrs.dsn), LWSTR(&attrs.server)); - return ESODBC_DSN_INVALID_ERROR; - } else { - /* fill in whatever's missing */ - assign_dsn_defaults(&attrs); + res = validate_dsn(&attrs, dsn_str, err_out, eo_max, /*connect?*/FALSE); + if (res < 0) { + return res; } - init_dbc(&dbc, NULL); - if (! SQL_SUCCEEDED(config_dbc(&dbc, &attrs))) { - ERR("test DBC configuration failed."); - return ESODBC_DSN_INVALID_ERROR; - } + /* 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: `" LWPDL "`, new DSN: `" LWPDL "`.", + DBG("old DSN name: `" LWPDL "`, new DSN name: `" 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 */ + 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); - goto err; - } else if (res) { + 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)); } } - /* if an old DSN exists, delete it */ + /* 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; - create_new = TRUE; + /* a new entry is created, force writing all new values */ + old_attrs = NULL; } else { - create_new = FALSE; + remove_old = FALSE; } - /* create or update the DSN */ - if (! write_system_dsn(&attrs, create_new)) { + /* 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); - goto err; + return ESODBC_DSN_GENERIC_ERROR; } - /* only remove old if new is succesfully created (even though the - * documentation says otherwise). */ + /* only remove old if new is succesfully created */ if (remove_old) { assert(old_dsn.cnt); if (! SQLRemoveDSNFromIniW(old_dsn.str)) { @@ -209,9 +213,6 @@ static int save_dsn_cb(void *arg, const wchar_t *dsn_str, } return 0; -err: - SQLPostInstallerError(ODBC_ERROR_REQUEST_FAILED, NULL); - return ESODBC_DSN_GENERIC_ERROR; } @@ -239,7 +240,7 @@ BOOL SQL_API ConfigDSNW( }; /* If there's a DSN in reveived attributes, load the config from the - * registry. Otherwise, populate a new config with defaults. */ + * registry. */ if (! load_system_dsn(&attrs, (SQLWCHAR *)lpszAttributes)) { ERR("failed to load system DSN for driver ` " LWPD " ` and " "attributes `" LWPDL "`.", LWSTR(&driver), lpszAttributes); diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h index 8c073d08..8251145a 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h @@ -7,15 +7,17 @@ * is desired by user. */ #define ESODBC_DSN_OVERWRITE_FLAG (1<<0) -#define ESODBC_DSN_NO_ERROR 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 +#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 (connection/00-list) couldn't be parsed. */ -#define ESODBC_DSN_INVALID_ERROR -3 +#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 +#define ESODBC_DSN_GENERIC_ERROR -127 /* * Callback into the driver definition. @@ -65,4 +67,4 @@ __declspec(dllimport) #endif /* _WINDLL */ int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, driver_callback_ft cbConnectionTest, void *argConnectionTest, - driver_callback_ft cbSaveDsn, void *argSaveDsn); \ No newline at end of file + driver_callback_ft cbSaveDsn, void *argSaveDsn); From 548c3035eca2ee8351fcc6fd81ef5b2337b17999 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Tue, 9 Oct 2018 16:11:47 +0100 Subject: [PATCH 12/35] further DSN validation and code deduplication - bubble up more error messages from DSN validation through DBC configuration; - connection testing and DSN saving callbacks now use the DSN validation function to do most of their work; Also: - add []-framing to IPv6 addresses in URLs - make connection string printing more robust in case destination buffer is too small for the result. --- driver/connect.c | 103 ++++++++--- driver/convert.c | 2 +- driver/dsn.c | 163 ++++++------------ driver/dsn.h | 2 +- driver/error.c | 4 +- driver/error.h | 4 +- driver/handles.h | 2 +- driver/queries.c | 5 +- driver/util.c | 4 +- driver/util.h | 4 +- dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h | 2 +- test/test_dsn.cc | 6 +- 12 files changed, 157 insertions(+), 144 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index e72eeaac..0a90342a 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -481,9 +481,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 +510,42 @@ 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(&HDRH(dbc)->diag, SQL_STATE_08S01, + curl_easy_strerror(dbc->curl_err), dbc->curl_err); + } else { + ERRH(dbc, "libcurl failure message: " LWPD ".", buff); + post_diagnostic(&HDRH(dbc)->diag, SQL_STATE_08S01, buff, + dbc->curl_err); + } +} + /* * Sends a POST request with the given JSON object body. */ @@ -553,9 +588,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 +619,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 */ } @@ -615,8 +647,8 @@ static SQLRETURN test_connect(esodbc_dbc_st *dbc) */ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) { - esodbc_state_et state = SQL_STATE_HY000; - int cnt; + SQLRETURN ret; + int cnt, ipv6; SQLBIGINT secure; long long timeout, max_body_size, max_fetch_size; SQLWCHAR buff_url[ESODBC_MAX_URL_LEN]; @@ -627,11 +659,14 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) if (! str2bigint(&attrs->secure, /*wide?*/TRUE, &secure)) { ERRH(dbc, "failed to read secure param `" LWPDL "`.", LWSTR(&attrs->secure)); + ret = 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); + ret = SET_HDIAG(dbc, SQL_STATE_HY000, "invalid security setting", 0); goto err; } else { dbc->secure = (long)secure; @@ -642,6 +677,8 @@ SQLRETURN config_dbc(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)); + ret = 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 +687,30 @@ SQLRETURN config_dbc(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) { ERRNH(dbc, "failed to print URL out of server: `" LWPDL "` [%zd], " "port: `" LWPDL "` [%zd].", LWSTR(&attrs->server), LWSTR(&attrs->port)); + ret = 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)); + ret = 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 +722,16 @@ SQLRETURN config_dbc(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)); + ret = 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); + ret = SET_HDIAG(dbc, SQL_STATE_HY000, "password UTF8 " + "conversion failed", 0); goto err; } } @@ -696,6 +747,8 @@ SQLRETURN config_dbc(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); + ret = SET_HDIAG(dbc, SQL_STATE_HY000, "timeout setting number " + "conversion failure", 0); goto err; } if (timeout < 0) { @@ -710,13 +763,17 @@ SQLRETURN config_dbc(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); + ret = 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); + ret = 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 +785,17 @@ SQLRETURN config_dbc(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); + ret = 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); + ret = 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,17 +825,17 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) } else { ERRH(dbc, "unknown packing encoding '" LWPDL "'.", LWSTR(&attrs->packing)); + ret = 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 parameters", 0); - } - cleanup_dbc(dbc); /* release allocated resources before the failure */ - RET_HDIAGS(dbc, state); + /* release allocated resources before the failure; not the diag, tho */ + cleanup_dbc(dbc); + RET_STATE(dbc->hdr.diag.state); } diff --git a/driver/convert.c b/driver/convert.c index a3d4da02..ef91809b 100644 --- a/driver/convert.c +++ b/driver/convert.c @@ -2599,7 +2599,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/dsn.c b/driver/dsn.c index 5e21a25c..25307afa 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -667,8 +667,8 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs, }; /* check that the esodbc_dsn_attrs_st stays in sync with the above */ - assert(sizeof(map)/sizeof(*iter) /* {NULL,NULL, NULL} terminator */-1 - /*Driver,DSN,SAVEFILE,FILEDSN*/-4 == ESODBC_DSN_ATTRS_COUNT); + 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->old) { @@ -741,46 +741,38 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, /* 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; + /* 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; + } } - 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 (space " - "left: %d; needed: %d).", cchConnStrOutMax - pos, - iter->val->cnt); - return -1; - } else { - pos += n; - } - } else { - /* simply increment the counter, since the untruncated length - * needs to be returned to the app */ - pos += iter->kw->cnt + /*`=`*/1 + - iter->val->cnt + braces + /*`;`*/1; } + /* 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; } } - DBG("Output connection string: `" LWPD "`; out len: %d.", - szConnStrOut, pos); + DBG("new connection string: `" LWPD "`; out len: %zu.", szConnStrOut, pos); assert(pos < LONG_MAX); return (long)pos; } @@ -945,117 +937,74 @@ BOOL read_system_info(esodbc_dsn_attrs_st *attrs) #endif /* defined(_WIN32) || defined (WIN32) */ int validate_dsn(esodbc_dsn_attrs_st *attrs, const wchar_t *dsn_str, - wchar_t *err_out, size_t eo_max, BOOL try_connect) + wchar_t *err_out, size_t eo_max, BOOL on_connect) { int ret; esodbc_dbc_st dbc; if (! dsn_str) { ERR("invalid NULL DSN string received."); + /* internal error, no user-relevant message */ return ESODBC_DSN_ISNULL_ERROR; - } else { -#ifdef ESODBC_DSN_API_WITH_00_LIST - /* this won't be "complete" if using 00-list */ - DBG("received DSN string starting with: `" LWPD "`.", dsn_str); -#else /* ESODBC_DSN_API_WITH_00_LIST */ - DBG("received DSN string: `" LWPD "`.", dsn_str); -#endif /* ESODBC_DSN_API_WITH_00_LIST */ } - #ifdef ESODBC_DSN_API_WITH_00_LIST + /* this won't be "complete" if using 00-list */ + DBG("received DSN string starting with: `" LWPD "`.", dsn_str); if (! parse_00_list(attrs, (SQLWCHAR *)dsn_str)) { #else + DBG("received DSN string: `" LWPD "`.", dsn_str); 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; } /* - * validate the DSN set + * check on the minimum DSN set requirements */ - if (! (attrs->dsn.cnt && attrs->server.cnt)) { - ERR("DSN name (" LWPDL ") and server address (" LWPDL ") cannot be" - " empty strings.", LWSTR(&attrs->dsn), LWSTR(&attrs->server)); + 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; - } else { - /* fill in whatever's missing */ - assign_dsn_defaults(attrs); } + 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; + } + + /* fill in whatever's missing */ + assign_dsn_defaults(attrs); init_dbc(&dbc, NULL); - if (try_connect) { - ret = do_connect(&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 connection failed: " LWPD ".", err_out); - ret = ESODBC_DSN_GENERIC_ERROR; - } else { - ret = ESODBC_DSN_NO_ERROR; // 0 - } + 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 { - init_dbc(&dbc, NULL); - ret = config_dbc(&dbc, attrs); - if (! SQL_SUCCEEDED(ret)) { - ERR("test DBC configuration failed."); - ret = ESODBC_DSN_INVALID_ERROR; - } else { - ret = ESODBC_DSN_NO_ERROR; // 0 - } + ret = ESODBC_DSN_NO_ERROR; // 0 } + cleanup_dbc(&dbc); return ret; - } static int test_connect(void *arg, const wchar_t *dsn_str, wchar_t *err_out, size_t eo_max, unsigned int _) { esodbc_dsn_attrs_st attrs; - esodbc_dbc_st dbc; - SQLRETURN res; - int ret; assert(! arg); /* change santinel */ - if (! dsn_str) { - ERR("invalid NULL DSN string received."); - return ESODBC_DSN_ISNULL_ERROR; - } else { - DBG("received DSN string: `" LWPD "`.", dsn_str); - } - init_dsn_attrs(&attrs); -#ifdef ESODBC_DSN_API_WITH_00_LIST - if (! parse_00_list(&attrs, (SQLWCHAR *)dsn_str)) { -#else - if (! parse_connection_string(&attrs, (SQLWCHAR *)dsn_str, - (SQLSMALLINT)wcslen(dsn_str))) { -#endif /* ESODBC_DSN_API_WITH_00_LIST */ - ERR("failed to parse received DSN string."); - return ESODBC_DSN_INVALID_ERROR; - } - /* fill in whatever's missing */ - assign_dsn_defaults(&attrs); - - init_dbc(&dbc, NULL); - res = do_connect(&dbc, &attrs); - if (! SQL_SUCCEEDED(res)) { - res = 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(res)); - ret = ESODBC_DSN_GENERIC_ERROR; - } else { - ret = 0; - } - - cleanup_dbc(&dbc); - return ret; + return validate_dsn(&attrs, dsn_str, err_out, eo_max, /*on conn*/TRUE); } /* diff --git a/driver/dsn.h b/driver/dsn.h index bfc439e2..2a397ed8 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -74,7 +74,7 @@ long TEST_API write_00_list(esodbc_dsn_attrs_st *attrs, 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 *new, esodbc_dsn_attrs_st *old); +BOOL write_system_dsn(esodbc_dsn_attrs_st *crr, esodbc_dsn_attrs_st *old); BOOL TEST_API parse_connection_string(esodbc_dsn_attrs_st *attrs, SQLWCHAR *szConnStrIn, SQLSMALLINT cchConnStrIn); diff --git a/driver/error.c b/driver/error.c index 0e1f3834..61e62b99 100644 --- a/driver/error.c +++ b/driver/error.c @@ -18,7 +18,7 @@ void init_diagnostic(esodbc_diag_st *dest) /* 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) + const SQLWCHAR *text, SQLINTEGER code) { size_t pos, tcnt, ebufsz; @@ -53,7 +53,7 @@ SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, } SQLRETURN post_c_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, - SQLCHAR *text, SQLINTEGER code) + const SQLCHAR *text, SQLINTEGER code) { SQLWCHAR wtext[sizeof(dest->text)/sizeof(*dest->text)], *ptr; if (text) { diff --git a/driver/error.h b/driver/error.h index b13a0b01..5b7f85a1 100644 --- a/driver/error.h +++ b/driver/error.h @@ -444,9 +444,9 @@ typedef struct { void init_diagnostic(esodbc_diag_st *dest); SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, - SQLWCHAR *text, SQLINTEGER code); + const SQLWCHAR *text, SQLINTEGER code); SQLRETURN post_c_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, - SQLCHAR *text, SQLINTEGER code); + const SQLCHAR *text, SQLINTEGER code); /* post state into the diagnostic and return state's return code */ #define RET_DIAG(_d/*iag dest*/, _s/*tate*/, _t/*ext*/, _c/*ode*/) \ return post_diagnostic(_d, _s, _t, _c) diff --git a/driver/handles.h b/driver/handles.h index ca70d1f7..126497ef 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -477,7 +477,7 @@ SQLRETURN EsSQLSetDescRec( #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(&HDRH(_hp)->diag, _s, MK_WPTR(_t), _c) /* return the code associated with the given state (and debug-log) */ #define RET_STATE(_s) \ diff --git a/driver/queries.c b/driver/queries.c index d2fc84ee..79e1ebf2 100644 --- a/driver/queries.c +++ b/driver/queries.c @@ -359,8 +359,9 @@ static BOOL attach_sql_error(SQLHANDLE hnd, cstr_st *body) 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.*/ + /* swprintf will always append the 0-term, but fail if formated string + * would overrun the buffer size (in an equivocal way: overrun encoding + * error) => find out the limit first. */ n = swprintf(NULL, 0, MK_WPTR("%.*s: %.*s"), (int)tlen, wtype, (int)rlen, wreason); if (0 < n) { diff --git a/driver/util.c b/driver/util.c index 2e6b094a..52653b3d 100644 --- a/driver/util.c +++ b/driver/util.c @@ -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; diff --git a/driver/util.h b/driver/util.h index e08f2efb..a5534147 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. */ diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h index 8251145a..88daa893 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h @@ -5,7 +5,7 @@ /* 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_OVERWRITE_FLAG (1<<0) #define ESODBC_DSN_NO_ERROR 0 /* Code returned by callback to signal that given DSN name already exists. */ diff --git a/test/test_dsn.cc b/test/test_dsn.cc index 52341102..f9903b0a 100644 --- a/test/test_dsn.cc +++ b/test/test_dsn.cc @@ -87,7 +87,8 @@ TEST_F(Dsn, parse_write_connection_string) { init_dsn_attrs(&attrs); ASSERT_TRUE(parse_connection_string(&attrs, src.str, (SQLSMALLINT)src.cnt)); - written = write_connection_string(&attrs, dst, sizeof(dst)/sizeof(*dst)); + written = write_connection_string(&attrs, dst, + (SQLSMALLINT)sizeof(dst)/sizeof(*dst)); ASSERT_TRUE(0 < written); ASSERT_TRUE(memcmp(src.str, dst, written) == 0); @@ -125,7 +126,8 @@ TEST_F(Dsn, write_connection_string_null_str_out) { init_dsn_attrs(&attrs); ASSERT_TRUE(parse_connection_string(&attrs, src.str, (SQLSMALLINT)src.cnt)); - written = write_connection_string(&attrs, dst, sizeof(dst)/sizeof(*dst)); + 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); From baee1b6770cec0d1dda33901ed9b68204be8c971 Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Wed, 10 Oct 2018 11:24:41 +0100 Subject: [PATCH 13/35] Add editor launcher (for rapid development!). Disable name and description on edit - fixes #28 --- .gitignore | 3 + dsneditor/EsOdbcDsn.sln | 16 +- .../EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 156 ++++++++++-------- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 45 ++++- dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs | 1 + .../EsOdbcDsnEditorLauncher.csproj | 79 +++++++++ .../Launcher.Designer.cs | 73 ++++++++ dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs | 33 ++++ .../EsOdbcDsnEditorLauncher/Launcher.resx | 120 ++++++++++++++ dsneditor/EsOdbcDsnEditorLauncher/Program.cs | 19 +++ .../Properties/AssemblyInfo.cs | 36 ++++ .../Properties/Resources.Designer.cs | 63 +++++++ .../Properties/Resources.resx | 117 +++++++++++++ .../Properties/Settings.Designer.cs | 26 +++ .../Properties/Settings.settings | 7 + dsneditor/EsOdbcDsnEditorLauncher/app.config | 3 + 16 files changed, 717 insertions(+), 80 deletions(-) create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/EsOdbcDsnEditorLauncher.csproj create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Launcher.resx create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Program.cs create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.Designer.cs create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Properties/Resources.resx create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.Designer.cs create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/Properties/Settings.settings create mode 100644 dsneditor/EsOdbcDsnEditorLauncher/app.config diff --git a/.gitignore b/.gitignore index 49ba3cdd..c67bd85b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ driver/*.swp cscope.out .vs +dsneditor/EsOdbcDsnEditor/Debug/ +dsneditor/EsOdbcDsnEditorLauncher/bin/ +dsneditor/EsOdbcDsnEditorLauncher/obj/ diff --git a/dsneditor/EsOdbcDsn.sln b/dsneditor/EsOdbcDsn.sln index d5b79774..66f2340e 100644 --- a/dsneditor/EsOdbcDsn.sln +++ b/dsneditor/EsOdbcDsn.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27703.2047 +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 @@ -39,6 +41,18 @@ Global {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 diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs index 39cf44c0..d9bc81d0 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -68,10 +68,10 @@ private void InitializeComponent() // // saveButton // - this.saveButton.Location = new System.Drawing.Point(457, 541); - this.saveButton.Margin = new System.Windows.Forms.Padding(4); + 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(100, 28); + this.saveButton.Size = new System.Drawing.Size(112, 35); this.saveButton.TabIndex = 17; this.saveButton.Text = "Save"; this.saveButton.UseVisualStyleBackColor = true; @@ -79,10 +79,10 @@ private void InitializeComponent() // // cancelButton // - this.cancelButton.Location = new System.Drawing.Point(560, 541); - this.cancelButton.Margin = new System.Windows.Forms.Padding(4); + 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(100, 28); + this.cancelButton.Size = new System.Drawing.Size(112, 35); this.cancelButton.TabIndex = 18; this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; @@ -90,10 +90,10 @@ private void InitializeComponent() // // testButton // - this.testButton.Location = new System.Drawing.Point(17, 541); - this.testButton.Margin = new System.Windows.Forms.Padding(4); + 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(156, 28); + this.testButton.Size = new System.Drawing.Size(176, 35); this.testButton.TabIndex = 16; this.testButton.Text = "Test Connection"; this.testButton.UseVisualStyleBackColor = true; @@ -107,16 +107,16 @@ private void InitializeComponent() 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(680, 58); + 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(524, 206); - this.certificatePathButton.Margin = new System.Windows.Forms.Padding(4); + 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(100, 28); + this.certificatePathButton.Size = new System.Drawing.Size(112, 35); this.certificatePathButton.TabIndex = 15; this.certificatePathButton.Text = "Browse..."; this.certificatePathButton.UseVisualStyleBackColor = true; @@ -124,18 +124,18 @@ private void InitializeComponent() // // textCertificatePath // - this.textCertificatePath.Location = new System.Drawing.Point(124, 209); - this.textCertificatePath.Margin = new System.Windows.Forms.Padding(4); + 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(392, 22); + this.textCertificatePath.Size = new System.Drawing.Size(440, 26); this.textCertificatePath.TabIndex = 14; // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(16, 212); + this.label1.Location = new System.Drawing.Point(18, 265); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(101, 17); + this.label1.Size = new System.Drawing.Size(114, 20); this.label1.TabIndex = 11; this.label1.Text = "Certificate File:"; // @@ -146,9 +146,11 @@ private void InitializeComponent() this.groupBox1.Controls.Add(this.radioEnabledNoHostname); this.groupBox1.Controls.Add(this.radioEnabledNoValidation); this.groupBox1.Controls.Add(this.radioButtonDisabled); - this.groupBox1.Location = new System.Drawing.Point(16, 16); + this.groupBox1.Location = new System.Drawing.Point(18, 20); + this.groupBox1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(608, 176); + this.groupBox1.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.groupBox1.Size = new System.Drawing.Size(684, 220); this.groupBox1.TabIndex = 10; this.groupBox1.TabStop = false; this.groupBox1.Text = "Secure Sockets Layer (SSL):"; @@ -156,20 +158,22 @@ private void InitializeComponent() // radioEnabledFull // this.radioEnabledFull.AutoSize = true; - this.radioEnabledFull.Location = new System.Drawing.Point(16, 139); + 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(304, 21); + 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.Text = "Enabled. Certificate identity chain validated."; this.radioEnabledFull.UseVisualStyleBackColor = true; // // radioEnabledHostname // this.radioEnabledHostname.AutoSize = true; - this.radioEnabledHostname.Location = new System.Drawing.Point(16, 112); + 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(362, 21); + 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."; @@ -178,9 +182,10 @@ private void InitializeComponent() // radioEnabledNoHostname // this.radioEnabledNoHostname.AutoSize = true; - this.radioEnabledNoHostname.Location = new System.Drawing.Point(16, 85); + 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(386, 21); + 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."; @@ -189,9 +194,10 @@ private void InitializeComponent() // radioEnabledNoValidation // this.radioEnabledNoValidation.AutoSize = true; - this.radioEnabledNoValidation.Location = new System.Drawing.Point(16, 58); + 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(241, 21); + this.radioEnabledNoValidation.Size = new System.Drawing.Size(271, 24); this.radioEnabledNoValidation.TabIndex = 10; this.radioEnabledNoValidation.TabStop = true; this.radioEnabledNoValidation.Text = "Enabled. Certificate not validated."; @@ -200,9 +206,10 @@ private void InitializeComponent() // radioButtonDisabled // this.radioButtonDisabled.AutoSize = true; - this.radioButtonDisabled.Location = new System.Drawing.Point(16, 31); + 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(299, 21); + this.radioButtonDisabled.Size = new System.Drawing.Size(336, 24); this.radioButtonDisabled.TabIndex = 9; this.radioButtonDisabled.TabStop = true; this.radioButtonDisabled.Text = "Disabled. All communications unencrypted."; @@ -210,78 +217,81 @@ private void InitializeComponent() // // numericUpDownPort // - this.numericUpDownPort.Location = new System.Drawing.Point(112, 246); + 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(104, 22); + 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(112, 324); - this.textPassword.Margin = new System.Windows.Forms.Padding(4); + 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.Size = new System.Drawing.Size(229, 22); + 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(16, 327); + this.labelPassword.Location = new System.Drawing.Point(18, 409); this.labelPassword.Name = "labelPassword"; - this.labelPassword.Size = new System.Drawing.Size(73, 17); + 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(112, 284); - this.textUsername.Margin = new System.Windows.Forms.Padding(4); + 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(229, 22); + 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(16, 287); + this.labelUsername.Location = new System.Drawing.Point(18, 359); this.labelUsername.Name = "labelUsername"; - this.labelUsername.Size = new System.Drawing.Size(77, 17); + 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(112, 208); - this.textHostname.Margin = new System.Windows.Forms.Padding(4); + 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(506, 22); + 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(16, 248); + this.labelPort.Location = new System.Drawing.Point(18, 310); this.labelPort.Name = "labelPort"; - this.labelPort.Size = new System.Drawing.Size(38, 17); + 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(16, 211); + this.labelHostname.Location = new System.Drawing.Point(18, 264); this.labelHostname.Name = "labelHostname"; - this.labelHostname.Size = new System.Drawing.Size(76, 17); + this.labelHostname.Size = new System.Drawing.Size(87, 20); this.labelHostname.TabIndex = 0; this.labelHostname.Text = "Hostname:"; // @@ -293,10 +303,11 @@ private void InitializeComponent() // this.tabConfiguration.Controls.Add(this.tabBasic); this.tabConfiguration.Controls.Add(this.tabPage2); - this.tabConfiguration.Location = new System.Drawing.Point(16, 74); + 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(648, 460); + this.tabConfiguration.Size = new System.Drawing.Size(729, 575); this.tabConfiguration.TabIndex = 8; // // tabBasic @@ -313,46 +324,48 @@ private void InitializeComponent() this.tabBasic.Controls.Add(this.numericUpDownPort); this.tabBasic.Controls.Add(this.labelPassword); this.tabBasic.Controls.Add(this.textPassword); - this.tabBasic.Location = new System.Drawing.Point(4, 25); + this.tabBasic.Location = new System.Drawing.Point(4, 29); + this.tabBasic.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.tabBasic.Name = "tabBasic"; - this.tabBasic.Padding = new System.Windows.Forms.Padding(3); - this.tabBasic.Size = new System.Drawing.Size(640, 431); + this.tabBasic.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.tabBasic.Size = new System.Drawing.Size(721, 542); this.tabBasic.TabIndex = 0; this.tabBasic.Text = "Basic"; this.tabBasic.UseVisualStyleBackColor = true; // // textDescription // - this.textDescription.Location = new System.Drawing.Point(112, 56); - this.textDescription.Margin = new System.Windows.Forms.Padding(4); + 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(506, 132); + this.textDescription.Size = new System.Drawing.Size(569, 164); this.textDescription.TabIndex = 3; // // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(16, 59); + this.label2.Location = new System.Drawing.Point(18, 74); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(83, 17); + this.label2.Size = new System.Drawing.Size(93, 20); this.label2.TabIndex = 10; this.label2.Text = "Description:"; // // textName // - this.textName.Location = new System.Drawing.Point(112, 16); - this.textName.Margin = new System.Windows.Forms.Padding(4); + 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(506, 22); + 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(16, 21); + this.labelName.Location = new System.Drawing.Point(18, 26); this.labelName.Name = "labelName"; - this.labelName.Size = new System.Drawing.Size(49, 17); + this.labelName.Size = new System.Drawing.Size(55, 20); this.labelName.TabIndex = 8; this.labelName.Text = "Name:"; // @@ -362,19 +375,20 @@ private void InitializeComponent() this.tabPage2.Controls.Add(this.groupBox1); this.tabPage2.Controls.Add(this.textCertificatePath); this.tabPage2.Controls.Add(this.label1); - this.tabPage2.Location = new System.Drawing.Point(4, 25); + this.tabPage2.Location = new System.Drawing.Point(4, 29); + this.tabPage2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.tabPage2.Name = "tabPage2"; - this.tabPage2.Padding = new System.Windows.Forms.Padding(3); - this.tabPage2.Size = new System.Drawing.Size(640, 431); + this.tabPage2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.tabPage2.Size = new System.Drawing.Size(721, 542); this.tabPage2.TabIndex = 1; this.tabPage2.Text = "Security"; this.tabPage2.UseVisualStyleBackColor = true; // // DsnEditorForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(682, 588); + this.ClientSize = new System.Drawing.Size(767, 735); this.Controls.Add(this.tabConfiguration); this.Controls.Add(this.header); this.Controls.Add(this.testButton); @@ -382,7 +396,7 @@ private void InitializeComponent() 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); + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "DsnEditorForm"; diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index 2ea796dc..adac8437 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -21,6 +21,8 @@ public partial class DsnEditorForm : Form private DriverCallbackDelegate testConnection; private DriverCallbackDelegate saveDsn; + private readonly bool isConnecting; + public OdbcConnectionStringBuilder Builder { get; set; } = new OdbcConnectionStringBuilder(); public DsnEditorForm( @@ -29,14 +31,25 @@ public DsnEditorForm( DriverCallbackDelegate connectionTest, DriverCallbackDelegate dsnSave) { + this.isConnecting = onConnect; + InitializeComponent(); AcceptButton = saveButton; CancelButton = cancelButton; - testConnection = connectionTest; saveDsn = dsnSave; + // If connecting then disable some user inputs + if (isConnecting) + { + textName.ReadOnly = true; + textName.Enabled = false; + + textDescription.ReadOnly = true; + textDescription.Enabled = false; + } + // If this is a call serving a connect request, call the button "Connect". // Otherwise it's a DSN editing, so it's going to be a "Save". saveButton.Text = onConnect ? "Connect" : "Save"; @@ -175,12 +188,12 @@ private void CertificatePathButton_Click(object sender, EventArgs e) private bool RebuildAndValidateDsn() { // Basic Panel - if (!string.IsNullOrEmpty(textName.Text)) Builder["dsn"] = textName.Text; - if (!string.IsNullOrEmpty(textDescription.Text)) Builder["description"] = textDescription.Text; - if (!string.IsNullOrEmpty(textUsername.Text)) Builder["uid"] = textUsername.Text; - if (!string.IsNullOrEmpty(textPassword.Text)) Builder["pwd"] = textPassword.Text; - if (!string.IsNullOrEmpty(textHostname.Text)) Builder["server"] = textHostname.Text; - if (!string.IsNullOrEmpty(numericUpDownPort.Text)) Builder["port"] = numericUpDownPort.Text; + 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 if (radioButtonDisabled.Checked) Builder["secure"] = 0; @@ -189,9 +202,10 @@ private bool RebuildAndValidateDsn() if (radioEnabledHostname.Checked) Builder["secure"] = 3; if (radioEnabledFull.Checked) Builder["secure"] = 4; + Builder["capath"] = textCertificatePath.Text; + if (!string.IsNullOrEmpty(textCertificatePath.Text)) { - Builder["capath"] = textCertificatePath.Text; return ValidateCertificateFile(textCertificatePath.Text); } @@ -218,5 +232,20 @@ private static string StripBraces(string input) } return input; } + + private void textName_TextChanged(object sender, EventArgs e) + { + + } + + private void textHostname_TextChanged(object sender, EventArgs e) + { + + } + + private void numericUpDownPort_ValueChanged(object sender, EventArgs e) + { + + } } } \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs index 3ba878d0..737f2f29 100644 --- a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -1,4 +1,5 @@ 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 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..4ccaf85d --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs @@ -0,0 +1,73 @@ +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..b0dd1a70 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs @@ -0,0 +1,33 @@ +using System; +using System.Windows.Forms; + +namespace EsOdbcDsnEditorLauncher +{ + public partial class Launcher : Form + { + public Launcher() + { + InitializeComponent(); + } + + private void LaunchButton_Click(object sender, EventArgs e) + { + bool onConnect = true; + string dsn = "driver={IBM DB2 ODBC DRIVER};Database=SampleDB;hostname=SampleServerName;protocol=TCPIP;uid=Admin;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) + { + textLog.Text += "Connection String:" + connectionString + Environment.NewLine; + return 0; + } + private int SaveDsn(string connectionString, ref string errorMessage, uint flags) + { + textLog.Text += "Connection String:" + connectionString + Environment.NewLine; + errorMessage = "ESODBC_DSN_EXISTS_ERROR"; + return -1; + } + } +} 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..da6061e0 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Program.cs @@ -0,0 +1,19 @@ +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..287a0da7 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("EsOdbcDsnEditorLauncher")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("EsOdbcDsnEditorLauncher")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[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 @@ + + + From 1bd7bd7d22ff8c9bee92e84a9801878e6fe15df2 Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Wed, 10 Oct 2018 15:52:46 +0100 Subject: [PATCH 14/35] Set state of Save / Test buttons based on values being present in hostname / name. Partially fixes #29 Don't clear the connection string Builder on form close (fixes connection bug) --- .../EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 1 - dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 21 ++++++++++++------- dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs | 10 +++++---- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs index d9bc81d0..1dddba69 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -402,7 +402,6 @@ private void InitializeComponent() this.Name = "DsnEditorForm"; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; this.Text = "Elasticsearch ODBC DSN Configuration"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DsnEditorForm_FormClosing); ((System.ComponentModel.ISupportInitialize)(this.header)).EndInit(); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index adac8437..2074b01f 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -84,6 +84,9 @@ public DsnEditorForm( } } } + + // Set initial state of action buttons. + EnableDisableActionButtons(); } /// @@ -164,14 +167,8 @@ private void TestConnectionButton_Click(object sender, EventArgs e) } } - private void DsnEditorForm_FormClosing(object sender, FormClosingEventArgs e) - { - Builder.Clear(); - } - private void CancelButton_Click(object sender, EventArgs e) { - Builder.Clear(); Close(); } @@ -230,22 +227,30 @@ private static string StripBraces(string input) { return input.Substring(1, input.Length - 2); } + return input; } private void textName_TextChanged(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 textHostname_TextChanged(object sender, EventArgs e) { - + EnableDisableActionButtons(); } private void numericUpDownPort_ValueChanged(object sender, EventArgs e) { - + EnableDisableActionButtons(); } } } \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs index b0dd1a70..5a033cc6 100644 --- a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs @@ -20,14 +20,16 @@ private void LaunchButton_Click(object sender, EventArgs e) private int ConnectTest(string connectionString, ref string errorMessage, uint flags) { - textLog.Text += "Connection String:" + connectionString + Environment.NewLine; + textLog.Text += "CONNECT. Connection String:" + connectionString + Environment.NewLine; return 0; } + private int SaveDsn(string connectionString, ref string errorMessage, uint flags) { - textLog.Text += "Connection String:" + connectionString + Environment.NewLine; - errorMessage = "ESODBC_DSN_EXISTS_ERROR"; - return -1; + textLog.Text += "SAVE. Connection String:" + connectionString + Environment.NewLine; + //errorMessage = "ESODBC_DSN_EXISTS_ERROR"; + //return -1; + return 0; } } } From e2f5b5117e0bdd12561bd94d9fb9aae4a7be151b Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Wed, 10 Oct 2018 18:44:16 +0100 Subject: [PATCH 15/35] Add logging information panel. Fixes #31. Mask password box. Refactor StripBraces() method. Added [STAThread] attribute to the factory method. --- .../EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 267 ++++++++++++------ dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 150 ++++++---- dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx | 3 + dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs | 1 + .../EsOdbcDsnEditor/EsOdbcDsnEditor.csproj | 3 +- dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs | 19 ++ 6 files changed, 295 insertions(+), 148 deletions(-) create mode 100644 dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs index 1dddba69..d99a8ee0 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -35,8 +35,8 @@ private void InitializeComponent() this.header = new System.Windows.Forms.PictureBox(); this.certificatePathButton = new System.Windows.Forms.Button(); this.textCertificatePath = new System.Windows.Forms.TextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); + 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(); @@ -52,18 +52,26 @@ private void InitializeComponent() this.labelHostname = new System.Windows.Forms.Label(); this.certificateFileDialog = new System.Windows.Forms.OpenFileDialog(); this.tabConfiguration = new System.Windows.Forms.TabControl(); - this.tabBasic = new System.Windows.Forms.TabPage(); + this.pageBasic = new System.Windows.Forms.TabPage(); this.textDescription = new System.Windows.Forms.TextBox(); - this.label2 = new System.Windows.Forms.Label(); + this.labelDescription = new System.Windows.Forms.Label(); this.textName = new System.Windows.Forms.TextBox(); this.labelName = new System.Windows.Forms.Label(); - this.tabPage2 = new System.Windows.Forms.TabPage(); + this.pageSecurity = new System.Windows.Forms.TabPage(); + this.pageLogging = new System.Windows.Forms.TabPage(); + 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.groupBox1.SuspendLayout(); + this.groupSSL.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).BeginInit(); this.tabConfiguration.SuspendLayout(); - this.tabBasic.SuspendLayout(); - this.tabPage2.SuspendLayout(); + this.pageBasic.SuspendLayout(); + this.pageSecurity.SuspendLayout(); + this.pageLogging.SuspendLayout(); this.SuspendLayout(); // // saveButton @@ -130,30 +138,30 @@ private void InitializeComponent() this.textCertificatePath.Size = new System.Drawing.Size(440, 26); this.textCertificatePath.TabIndex = 14; // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(18, 265); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(114, 20); - this.label1.TabIndex = 11; - this.label1.Text = "Certificate File:"; - // - // groupBox1 - // - this.groupBox1.Controls.Add(this.radioEnabledFull); - this.groupBox1.Controls.Add(this.radioEnabledHostname); - this.groupBox1.Controls.Add(this.radioEnabledNoHostname); - this.groupBox1.Controls.Add(this.radioEnabledNoValidation); - this.groupBox1.Controls.Add(this.radioButtonDisabled); - this.groupBox1.Location = new System.Drawing.Point(18, 20); - this.groupBox1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.groupBox1.Size = new System.Drawing.Size(684, 220); - this.groupBox1.TabIndex = 10; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Secure Sockets Layer (SSL):"; + // 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 // @@ -232,13 +240,14 @@ private void InitializeComponent() 0, 0, 0}); - this.numericUpDownPort.ValueChanged += new System.EventHandler(this.numericUpDownPort_ValueChanged); + 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; // @@ -275,7 +284,7 @@ private void InitializeComponent() 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); + this.textHostname.TextChanged += new System.EventHandler(this.TextHostname_TextChanged); // // labelPort // @@ -301,8 +310,9 @@ private void InitializeComponent() // // tabConfiguration // - this.tabConfiguration.Controls.Add(this.tabBasic); - this.tabConfiguration.Controls.Add(this.tabPage2); + 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"; @@ -310,28 +320,28 @@ private void InitializeComponent() this.tabConfiguration.Size = new System.Drawing.Size(729, 575); this.tabConfiguration.TabIndex = 8; // - // tabBasic - // - this.tabBasic.Controls.Add(this.textDescription); - this.tabBasic.Controls.Add(this.label2); - this.tabBasic.Controls.Add(this.textName); - this.tabBasic.Controls.Add(this.labelName); - this.tabBasic.Controls.Add(this.textHostname); - this.tabBasic.Controls.Add(this.labelHostname); - this.tabBasic.Controls.Add(this.labelPort); - this.tabBasic.Controls.Add(this.labelUsername); - this.tabBasic.Controls.Add(this.textUsername); - this.tabBasic.Controls.Add(this.numericUpDownPort); - this.tabBasic.Controls.Add(this.labelPassword); - this.tabBasic.Controls.Add(this.textPassword); - this.tabBasic.Location = new System.Drawing.Point(4, 29); - this.tabBasic.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.tabBasic.Name = "tabBasic"; - this.tabBasic.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.tabBasic.Size = new System.Drawing.Size(721, 542); - this.tabBasic.TabIndex = 0; - this.tabBasic.Text = "Basic"; - this.tabBasic.UseVisualStyleBackColor = true; + // 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 // @@ -342,14 +352,14 @@ private void InitializeComponent() this.textDescription.Size = new System.Drawing.Size(569, 164); this.textDescription.TabIndex = 3; // - // label2 + // labelDescription // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(18, 74); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(93, 20); - this.label2.TabIndex = 10; - this.label2.Text = "Description:"; + 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 // @@ -358,7 +368,7 @@ private void InitializeComponent() 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); + this.textName.TextChanged += new System.EventHandler(this.TextName_TextChanged); // // labelName // @@ -369,20 +379,86 @@ private void InitializeComponent() this.labelName.TabIndex = 8; this.labelName.Text = "Name:"; // - // tabPage2 - // - this.tabPage2.Controls.Add(this.certificatePathButton); - this.tabPage2.Controls.Add(this.groupBox1); - this.tabPage2.Controls.Add(this.textCertificatePath); - this.tabPage2.Controls.Add(this.label1); - this.tabPage2.Location = new System.Drawing.Point(4, 29); - this.tabPage2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.tabPage2.Name = "tabPage2"; - this.tabPage2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.tabPage2.Size = new System.Drawing.Size(721, 542); - this.tabPage2.TabIndex = 1; - this.tabPage2.Text = "Security"; - this.tabPage2.UseVisualStyleBackColor = true; + // 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.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; + // + // 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, 72); + 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, 75); + 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, 18); + 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, 20); + 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, 26); + this.labelLogDirectory.Name = "labelLogDirectory"; + this.labelLogDirectory.Size = new System.Drawing.Size(107, 20); + this.labelLogDirectory.TabIndex = 16; + this.labelLogDirectory.Text = "Log Directory:"; // // DsnEditorForm // @@ -403,14 +479,16 @@ private void InitializeComponent() this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; this.Text = "Elasticsearch ODBC DSN Configuration"; ((System.ComponentModel.ISupportInitialize)(this.header)).EndInit(); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); + this.groupSSL.ResumeLayout(false); + this.groupSSL.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).EndInit(); this.tabConfiguration.ResumeLayout(false); - this.tabBasic.ResumeLayout(false); - this.tabBasic.PerformLayout(); - this.tabPage2.ResumeLayout(false); - this.tabPage2.PerformLayout(); + this.pageBasic.ResumeLayout(false); + this.pageBasic.PerformLayout(); + this.pageSecurity.ResumeLayout(false); + this.pageSecurity.PerformLayout(); + this.pageLogging.ResumeLayout(false); + this.pageLogging.PerformLayout(); this.ResumeLayout(false); } @@ -429,22 +507,29 @@ private void InitializeComponent() private System.Windows.Forms.Label labelUsername; private System.Windows.Forms.NumericUpDown numericUpDownPort; private System.Windows.Forms.RadioButton radioButtonDisabled; - private System.Windows.Forms.GroupBox groupBox1; + 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 label1; + private System.Windows.Forms.Label labelCertificatePath; private System.Windows.Forms.OpenFileDialog certificateFileDialog; private System.Windows.Forms.TabControl tabConfiguration; - private System.Windows.Forms.TabPage tabBasic; - private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.TabPage pageBasic; + private System.Windows.Forms.TabPage pageSecurity; private System.Windows.Forms.TextBox textDescription; - private System.Windows.Forms.Label label2; + 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; } } diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index 2074b01f..d8cfce95 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -31,44 +31,39 @@ public DsnEditorForm( DriverCallbackDelegate connectionTest, DriverCallbackDelegate dsnSave) { - this.isConnecting = onConnect; - InitializeComponent(); AcceptButton = saveButton; CancelButton = cancelButton; + + isConnecting = onConnect; testConnection = connectionTest; saveDsn = dsnSave; // If connecting then disable some user inputs if (isConnecting) { - textName.ReadOnly = true; - textName.Enabled = false; - - textDescription.ReadOnly = true; - textDescription.Enabled = false; + textName.ReadOnly = textDescription.ReadOnly = true; + textName.Enabled = textDescription.Enabled = false; } - // If this is a call serving a connect request, call the button "Connect". - // Otherwise it's a DSN editing, so it's going to be a "Save". + // If this is a call serving a connect request, call the button "Connect", otherwise it's a DSN editing, so it's going to be a "Save". saveButton.Text = onConnect ? "Connect" : "Save"; // Parse DSN Builder.ConnectionString = dsn; // Basic Panel - textName.Text = Builder.ContainsKey("dsn") ? StripBraces(Builder["dsn"].ToString()) : string.Empty; - textDescription.Text = Builder.ContainsKey("description") ? StripBraces(Builder["description"].ToString()) : string.Empty; - textUsername.Text = Builder.ContainsKey("uid") ? StripBraces(Builder["uid"].ToString()) : string.Empty; - textPassword.Text = Builder.ContainsKey("pwd") ? StripBraces(Builder["pwd"].ToString()) : string.Empty; - textHostname.Text = Builder.ContainsKey("server") ? StripBraces(Builder["server"].ToString()) : string.Empty; - numericUpDownPort.Text = Builder.ContainsKey("port") ? StripBraces(Builder["port"].ToString()) : string.Empty; + 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 - textCertificatePath.Text = Builder.ContainsKey("capath") ? StripBraces(Builder["capath"].ToString()) : string.Empty; - if (Builder.ContainsKey("secure")) { var result = int.TryParse(Builder["secure"].ToString(), out int val); @@ -85,6 +80,20 @@ public DsnEditorForm( } } + // Logging Panel + textLogDirectoryPath.Text = Builder.ContainsKey("tracefile") ? Builder["tracefile"].ToString().StripBraces() : string.Empty; + comboLogLevel.Text = "DEBUG"; // 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; + } + } + // Set initial state of action buttons. EnableDisableActionButtons(); } @@ -98,7 +107,7 @@ private void SaveButton_Click(object sender, EventArgs e) SaveDsn(false); } - private void SaveDsn(bool allowOverwrites) + private void SaveDsn(bool forceOverwrite) { var errorMessage = string.Empty; @@ -106,18 +115,18 @@ private void SaveDsn(bool allowOverwrites) if (!dsnResult) return; var dsn = Builder.ToString(); - var flag = allowOverwrites ? 1u : 0; + var flag = forceOverwrite ? 1u : 0; int result = saveDsn(dsn, ref errorMessage, flag); - if (result >= 0 || (allowOverwrites + if (result >= 0 || (forceOverwrite && result == ESODBC_DSN_EXISTS_ERROR)) { Close(); return; } - // Specific handling for prompting the user if overwriting - if (allowOverwrites == false + // 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); @@ -167,21 +176,6 @@ private void TestConnectionButton_Click(object sender, EventArgs e) } } - private void CancelButton_Click(object sender, EventArgs e) - { - Close(); - } - - private void CertificatePathButton_Click(object sender, EventArgs e) - { - var result = certificateFileDialog.ShowDialog(); - if (result == DialogResult.OK) - { - string file = certificateFileDialog.FileName; - ValidateCertificateFile(file); - } - } - private bool RebuildAndValidateDsn() { // Basic Panel @@ -193,26 +187,75 @@ private bool RebuildAndValidateDsn() 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; - Builder["capath"] = textCertificatePath.Text; + // Logging Panel + Builder["tracefile"] = textLogDirectoryPath.Text; + Builder["tracelevel"] = comboLogLevel.Text; + + // Validations + var certificateFileOK = true; + var logDirectoryOK = true; if (!string.IsNullOrEmpty(textCertificatePath.Text)) { - return ValidateCertificateFile(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; } - return true; + MessageBox.Show("Log directory invalid", "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) { - // Simple validation - if (File.Exists(file) && File.ReadAllBytes(file).Length > 0) + if (string.IsNullOrEmpty(file) + || (File.Exists(file) && File.ReadAllBytes(file).Length > 0)) { return true; } @@ -221,17 +264,17 @@ private bool ValidateCertificateFile(string file) return false; } - private static string StripBraces(string input) + private void TextName_TextChanged(object sender, EventArgs e) { - if (input.StartsWith("{") && input.EndsWith("}")) - { - return input.Substring(1, input.Length - 2); - } + EnableDisableActionButtons(); + } - return input; + private void TextHostname_TextChanged(object sender, EventArgs e) + { + EnableDisableActionButtons(); } - private void textName_TextChanged(object sender, EventArgs e) + private void NumericUpDownPort_ValueChanged(object sender, EventArgs e) { EnableDisableActionButtons(); } @@ -243,14 +286,9 @@ private void EnableDisableActionButtons() testButton.Enabled = string.IsNullOrEmpty(textHostname.Text) == false; } - private void textHostname_TextChanged(object sender, EventArgs e) - { - EnableDisableActionButtons(); - } - - private void numericUpDownPort_ValueChanged(object sender, EventArgs e) + private void CancelButton_Click(object sender, EventArgs e) { - EnableDisableActionButtons(); + Close(); } } } \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx index 21dffa8c..2a42dcab 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.resx @@ -2756,6 +2756,9 @@ 17, 17 + + 239, 17 + AAABAAIAICAAAAEAIACoEAAAJgAAAEBAAAABACAAKEIAAM4QAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQ diff --git a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs index 737f2f29..6aadf86c 100644 --- a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -9,6 +9,7 @@ namespace EsOdbcDsnEditor { public static class DsnEditorFactory { + [STAThread] public static int DsnEditor( bool onConnect, string dsnIn, diff --git a/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj index 2aac5afc..32fd350f 100644 --- a/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj +++ b/dsneditor/EsOdbcDsnEditor/EsOdbcDsnEditor.csproj @@ -74,6 +74,7 @@ DSNEditorForm.cs + True @@ -95,4 +96,4 @@ - + \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs b/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs new file mode 100644 index 00000000..ccb50bf2 --- /dev/null +++ b/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs @@ -0,0 +1,19 @@ +// 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; + } + } +} \ No newline at end of file From 8c5575de5c921d2d1585365150a222c7eb095844 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 11 Oct 2018 17:21:02 +0100 Subject: [PATCH 16/35] change one file's encoding - now UTF8 - remove BOM --- .../EsOdbcDsnBinding/EsOdbcDsnBinding.cpp | Bin 11470 -> 5555 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp index 12f1266736ab53640d07f88b87b9ba18adca9bb1..021c5d6310b8aedef7329c5dfaefc63ac41310d7 100644 GIT binary patch literal 5555 zcmcIoYjfMU75yyzio43RrX1O_)AkFKxMRz5;;Cb$wma_3?pnhKB(b4D4ggwKQ~&p# z3xL$a@=VihcP48J1nwK>p38opVkM$)6d5{xcSK2%S97V`?KhcB@+uYd;XadTvCxOJ zleI-{R3@kEE0NYhnay2~Ns=pGTAl0+^(c*#b1lyx7cvfKop!swPowj*!Jei{mP$;r zCy`Q$zfw@9=}*V+Xuscn<5$MFf4v%iIsN4WiIAIvX(*~^1*()RaS8hrHHgYxUHw%`rdA};-bi@kb^=> zMCd(MZf)M_A=UCy;s=avwNjNm3N=omJddN~+jwFqiTxocy%XA=eJrw+_9Hdje)XD& z+vriCP*`tsc5RJSmrxxU5oVHgUT!!W?6m8mk~Y~rax#_jV@P_PnwaB?N&)XeC^ zM>^^vv)Iw<_mM!9yq*$X(|wQF zWd9V!B-mK(Qoml`-+a;YCE=_VsRS(|O_6lY^cRtmz^fkRu#w}ySwd*O$znkT zXPX1Sg>yt75MZ4Pxn;sy>D)nptNOZtIIZkI*Mv1tLkqw@f+2tiT+awGr+8_VSHN&r zMlg8D)n2E8DozH2ahfFn6VwRKSc#~VRD?9orZYn^49Ij6sT4aHj&v~>)Dcf55JaRM zZ-i5_9x)_nnn>1$U298(}gXRdsS5QO1>h}IOu;A@}0aV*)*+Y7^P_oDOu|cHuz^w|6Lt$g_ zzYT?l-2ZzFydLX(^ZcKAZOek&B9{PP0vyvuRS9}5ia`ln0*!9M#g8JkXjVoF903iR zZ)1u(GR;5~>@Q;cp2zm)=pugvi!`Z;b~^K9=|-PG~ISxY~Cr0c(3UD2O6@1}d( zrI0!uhi^{wb{$*L-E_<3$xZoccn!5w1?&)+pu|hOFkZE1p2P zP3vp6A#U7SzuENxaEl3fL)hxZI5t)@h*~<=@+7jh<^ir+d)8G2qxc`d;0Mxz`+twt zO*~ao{lL**ahR2`TB{4STCS+d-ROK2QZf_CH`m|LADFyE6UKa5RMT09f<2bIM^V0r zmi7iJu%|@^<^!lL(d^JC=8@q#tqIse?X|Z;EU$4{U4fv6`NzS@ZJB2#*z4_ee?I!v z6^iYYK42S14^D=lJNHl;bT~Mb0zvO|>)y%bAqyKe`-Y-NMA)`QkQ5eZjkCng0>ej= zRi1+b*`TzZN#t7iz_74SzMaRQ=RA-27z%K#DPCnpLuz74xrLuN(kMP|P~4s4D)Y3c zNq&t;Wn^Zqd$OAA0^}EG5Oin&jrHxFVa_aIc^0ckWmvWi-(_H0uto3-%^CxrwL_#{ zs5f+pSo;7j!w}vKp9Huj4%VxIBXb}pxJZ~j%o!T$EgqNy)>6Q6y_a~xo=cS8(`XXx z{qoCReZKCkn`v8qg`=-Xx~*cMJ8TJ#K%6#Izop|Yy>-cIZA$bt^p-k@X`XxFg8AT9 zKe-_h6Wk~aFMtDKIDA4o(7~n%YgD)ENIr_y%oJ#v<1NFdM3hXZg3g755??t@Flets zZ?_8Dxe1&04m))~9BZ$6yR;{4VKXUPfoDymp!`buoqmUTy-w0ogRHT z8;|}%|Kh)wgVFWy=4N!`B{2tdW7m$`>Eh|h*Sfp*g`0)#1le?RH=PGf=iGV`hUa$V z9r%grT;yU3PK>#jpLTS-9}}?AxORxdwQx1J+l+wmphv%Nl=_bcVYq|Zx&T@wtLFI| zq}K;U99ala<-kvy(?>6uXe7D z3uk$vjsE)D z07_m0$pd*HVHja&Jc|6-w}fpBvFJ@}IN}n@GyD(1NDXP|sh$V|y)ns_d9<`E6$WNI zc3GtH5jgmRLrq@uC!2jBHv&|PCuNA@$$ITV931mOJGolov9KRmEguCRYRCun)SBLL IP`T3o4YvmzL;wH) literal 11470 zcmds-ZBJXt702hTEA=}#u3DUA6SCX(4V!L7z}byTLJCw=+U_DA4UMsp4GA>euip0m zo8#k|xz|rbjkc;R;QHQk=REz-!<^yY|5^>Jp&5?CFr0?N@J`o<;ab1H3}1z3`g$xYPG*U0;T; z<6HN6V=b(O9ld)d%bse*Yw;S0&!?@lr!{W1#;3i#6RnN}=!j&mBng@gCDWBG#?^a0 zNAuB`3m90WaUBmG`SL<*54565%R1PR*nmS*-q@F4Hg$C%q<@esCmHQDU)1@OxNC-w z6Tdcumq+niLmWWh+oC-dJz)#94*wBF(T~W2{|6S4WbPtkTr)zM_iXL=94! zy`kFGH_s8x@T2r4R`I$$NuGiFB+|auDnohXIO^IHCv<3#KNsEe@CW^V zCf*+1V3m*6U8t;5jC3`S9=(XUzOM1yx#YXj2RowYktn!tQjnvlJA-Hsp4%$a%i{^9 zt%LPWC-H4EaEH7w)apo?@Ye`GMH@M?a(ON1kV{g0nFpwCL&-o!As~MmyoC`$v2Zb z&FG`I@r^z4&sW|U%RZXn@e&*O6c3&0gC&q0eekUL)$?*&5)LBrF7yV~6@(;&H)HH1 zj3*6Cd*<(yQL!JsmmTiYUHl*kKG2l-(dYbgII(li&zv}G@5U*4OkeB$rf2!B1-59k<0JoOjYVpc}l z6I#(Aucw|@JUe*=H-+&$gCsrC9edbbg6L;W>qD&K9*%KOPP-dZLqoU5I^K!o*|MYi zRspK2Bh}UMP8NHkvJ-y5dJZ#N&A;nvd*y<~F+}zNM$^TYAf~z11bU zO@GgNPx^|1bi_~SjAsXhH4OWnxxJS&PlE*Ou6S;B0}EScmUHrslb&;WCzB*8yT-DM zYZsQIdzey-txwLJ-;XWoQ;{81&hJX0nx%QdXPHx{XFQNmb_8jE(hwIg zlYH~PgOajq{{O&5sZ&Bi{r-R3_n^SzweEkf9LALjKU6j+C)%Er%t0K``%zJB=i`-v zOq%z1%rJ)fv3$duUQfxwtkaFzncR||5f#?YUn<6o6SQROUhF8Xr=~ll?hYsVfZ;s< zq&+XSp`#V%&5YXOHhWKtkE(s!BT3-<*?si^ZrA(l9yi1@?VQUT2+w?9woJD86dCHM zt8*=Brtib3AXHoSP-s=^X0@??KUFa7=JvF#ZttYK`?AP`*x|kjf6+c|OW*&fpV<{R zf0upst1tGVt-qLVkq)KLlGNq>`Er)Dxn$*&sw~!3 zS7SVN(@ackvO$NLVmWnZXS#n8Bb;ne&j~$Y>`;iYE4yhmW znF%|L(l&1Ox((gNd4F4GQEGtA7LT=V)lNU^9yVR9VOzQPDEwWtR1@CA-dKtZPNth> z(RDp%_QN|C6SL+()1<5FT%vs#>)6LEbNiHJFP;}{$9%!`1Gza_N&7_Gq1f+{Bx7D+ zI|+7sHM z95?jQ><)Vo!_}#v%lEA)@|ghVb4;TxUsW?2z5xN82k;iQ2Omhxj?2$&ES7C2jNBIW zRMmX9&IAknp~JrT+jXDLpMH*h4eD~Q>-xNrBsP<_H4XV@JDU?bo>?XFy%yT>6nMf0 z#KMInvR)9cqf;IeUD3Og<~Q5R)0N^8w+OmdWu5NJb#cq{I~BqF(}`z)Hfc4URob>N zSb87KTkWz_n`@os*iLv?(jfs6%p9&N4cOnB$Z!3E-GlNvo{W$YZ3Z z3KO2ZD}V>Ni=1KJC9}QMY9@!pqeX*tFLjJGSYV#Ll9Wc<(tA(!W*sYKM`0SI zvkR(l;*{dH#4g@hCTmuahvh*$o!ZN2v&(x}zeIgq53)x({U{~AD`JL>ZF!Vx&J?@u zE8@te_#X>h9cSs3cv@BZoaYv&aZgoFdFp;HpTCgRZ>49F+ByvM=W0t>anE?R#U^w* zIf7e}`n7W0fwIz{^yWYHW8Yt?uijQq(NXudqpO`7`pjn@H&qWUs1CJ+omkb%%U6f3zd&Ealg0hZt`== zFn?FKoUG>6JTKnICheGA$<(0qH^SEZxeYc?bIW-)EF&iAQtJG3N?%Ii@;VM1LROyd zHf5Qb}(rHp+y@lOd2Ad`2bREb5DJo4PKb=hXPnCac z8c0oQDZ5Y!5A?xX{Fe;>8O0U>?V@o$#{nq!#13oXswp?qLbruPFE?J zIDWl4xmB0xY?usGI~(t6+)LG-=I8BaT)GF6e0pEwa${?YO|CvyS5vi)?}~kbb}Bs5 zJJZ{PO!5@FBqGFVm{Cyknd#UWc*^WmJUj`%t@N*n)_V8ZtJ?JFR5IAKGndWJ=F@5= r4xec6=E(Al4O)$-&0Ic=QoDaa>V From 542bffffa771a27954a959735899518ed51627b5 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 11 Oct 2018 17:33:03 +0100 Subject: [PATCH 17/35] configure COM as STA - this fixes the file chooser issue; - DSN form now rendered with .ShowDialog() to prevent calling app allow the user to re-invoke the bridge (which would fail, it running as an STA) fixes #25 --- .../EsOdbcDsnBinding/EsOdbcDsnBinding.cpp | 24 ++++++++++++++++--- dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs | 3 +-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp index 021c5d63..922170bb 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp @@ -112,7 +112,7 @@ namespace EsOdbcDsnBinding { // no resources available to load assembly = nullptr; } else { - // Get the briding assembley, get its location and based on that build the loading assembly path. + // 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('\\'); @@ -154,11 +154,29 @@ namespace EsOdbcDsnBinding { }; } -extern "C" __declspec(dllexport) int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, +#ifdef __cplusplus +extern "C" +#endif /* __cpluplus */ +#ifdef _WINDLL +__declspec(dllexport) +#else /* _WINDLL */ +__declspec(dllimport) +#endif /* _WINDLL */ +int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, driver_callback_ft cbConnectionTest, void *argConnectionTest, driver_callback_ft cbSaveDsn, void *argSaveDsn) { try { + // (re)set the threading model; "Multiple calls to CoInitializeEx by the same + // thread are allowed as long as they pass the same concurrency flag". + switch (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY)) { + case S_OK: + case S_FALSE: + break; + default: + throw gcnew Exception("setting threading model failed."); + } + _hwnd = hwnd; EsOdbcDsnBinding::EsOdbcDsnBinding binding(onConnect, dsnInW, cbConnectionTest, argConnectionTest, @@ -167,7 +185,7 @@ extern "C" __declspec(dllexport) int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wc } catch (Exception^ e) { if (hwnd) { pin_ptr wch = PtrToStringChars(e->Message); - if (! MessageBox(hwnd, wch, L"Loading Exeception", MB_OK | MB_ICONERROR)) { + if (! MessageBox(hwnd, wch, L"Loading Exception", MB_OK | MB_ICONERROR)) { // failed to display failure error return -3; } diff --git a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs index 6aadf86c..666efcbc 100644 --- a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -9,7 +9,6 @@ namespace EsOdbcDsnEditor { public static class DsnEditorFactory { - [STAThread] public static int DsnEditor( bool onConnect, string dsnIn, @@ -18,7 +17,7 @@ public static int DsnEditor( { Application.EnableVisualStyles(); DsnEditorForm form = new DsnEditorForm(onConnect, dsnIn, delegConnectionTest, delegSaveDsn); - Application.Run(form); + form.ShowDialog(); // instead of Application.Run(form); var dsn = form.Builder.ToString(); return dsn.Length; } From e8ee1af0974862051124ed07d48f6bf99eaa49be Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Sat, 13 Oct 2018 09:35:47 +0100 Subject: [PATCH 18/35] Fixes #27 --- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index d8cfce95..23f18fab 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -288,6 +288,8 @@ private void EnableDisableActionButtons() 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(); } } From ca5f91dc4c624c676b8d3d17904acd3bb7fcccc1 Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Sat, 13 Oct 2018 11:10:47 +0100 Subject: [PATCH 19/35] Add TraceEnabled handling to UI. #31 --- .../EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 26 +++++++++++++++---- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 26 +++++++++++++++++++ dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs | 2 +- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs index d99a8ee0..21aa2155 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -65,6 +65,7 @@ private void InitializeComponent() this.textLogDirectoryPath = new System.Windows.Forms.TextBox(); this.labelLogDirectory = new System.Windows.Forms.Label(); this.folderLogDirectoryDialog = new System.Windows.Forms.FolderBrowserDialog(); + this.checkLoggingEnabled = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.header)).BeginInit(); this.groupSSL.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).BeginInit(); @@ -396,6 +397,7 @@ private void InitializeComponent() // // 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); @@ -418,7 +420,7 @@ private void InitializeComponent() "INFO", "WARN", "ERROR"}); - this.comboLogLevel.Location = new System.Drawing.Point(139, 72); + 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; @@ -426,7 +428,7 @@ private void InitializeComponent() // labelLogLevel // this.labelLogLevel.AutoSize = true; - this.labelLogLevel.Location = new System.Drawing.Point(18, 75); + 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; @@ -434,7 +436,7 @@ private void InitializeComponent() // // logDirectoryPathButton // - this.logDirectoryPathButton.Location = new System.Drawing.Point(592, 18); + 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); @@ -445,7 +447,7 @@ private void InitializeComponent() // // textLogDirectoryPath // - this.textLogDirectoryPath.Location = new System.Drawing.Point(139, 20); + 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); @@ -454,12 +456,25 @@ private void InitializeComponent() // labelLogDirectory // this.labelLogDirectory.AutoSize = true; - this.labelLogDirectory.Location = new System.Drawing.Point(18, 26); + 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:"; // + // 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); + // // DsnEditorForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); @@ -530,6 +545,7 @@ private void InitializeComponent() 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 index 23f18fab..950f97af 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -83,6 +83,7 @@ public DsnEditorForm( // 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()) @@ -93,6 +94,18 @@ public DsnEditorForm( 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; + case 1: checkLoggingEnabled.Checked = true; break; + } + } + } // Set initial state of action buttons. EnableDisableActionButtons(); @@ -197,6 +210,7 @@ private bool RebuildAndValidateDsn() // Logging Panel Builder["tracefile"] = textLogDirectoryPath.Text; Builder["tracelevel"] = comboLogLevel.Text; + Builder["traceenabled"] = checkLoggingEnabled.Checked ? "1" : "0"; // Validations var certificateFileOK = true; @@ -292,5 +306,17 @@ private void CancelButton_Click(object sender, EventArgs e) Builder.Clear(); Close(); } + + private void CheckLoggingEnabled_CheckedChanged(object sender, EventArgs e) + { + EnableDisableLoggingControls(); + } + + private void EnableDisableLoggingControls() + { + textLogDirectoryPath.Enabled = checkLoggingEnabled.Checked; + comboLogLevel.Enabled = checkLoggingEnabled.Checked; + logDirectoryPathButton.Enabled = checkLoggingEnabled.Checked; + } } } \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs index 666efcbc..2e864514 100644 --- a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -17,7 +17,7 @@ public static int DsnEditor( { Application.EnableVisualStyles(); DsnEditorForm form = new DsnEditorForm(onConnect, dsnIn, delegConnectionTest, delegSaveDsn); - form.ShowDialog(); // instead of Application.Run(form); + form.ShowDialog(); // Instead of Application.Run(form); var dsn = form.Builder.ToString(); return dsn.Length; } From 48dca53f3069c9e0b9b39e5c9d2e72c429e679b8 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Mon, 15 Oct 2018 10:21:52 +0200 Subject: [PATCH 20/35] create thread if neutral/multi-threaded appart. - in case STA init'ing fails, create a new thread for the form; - also, make the window handler a static member of the binding class. closes #25 --- .../EsOdbcDsnBinding/EsOdbcDsnBinding.cpp | 59 +++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp index 922170bb..73d7cf0d 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp @@ -14,10 +14,10 @@ using namespace System; using namespace System::Reflection; using namespace System::Text; using namespace System::Runtime::InteropServices; +using namespace System::Threading; using namespace EsOdbcDsnEditor; -static HWND _hwnd; namespace EsOdbcDsnBinding { @@ -28,12 +28,15 @@ namespace 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, @@ -81,10 +84,14 @@ namespace EsOdbcDsnBinding { /* * The constructor merely saves the input parameters and registers an assembly resolve handler */ - public: EsOdbcDsnBinding(bool onConnect, wchar_t *dsnInW, + 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) : ""; @@ -98,6 +105,11 @@ namespace EsOdbcDsnBinding { AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler(resolveEventHandler); } + public: ~EsOdbcDsnBinding() + { + hwnd = NULL; + } + /* * Handler called if loading an assembly fails. */ @@ -129,9 +141,9 @@ namespace EsOdbcDsnBinding { return assembly; } catch (Exception ^e) { - if (_hwnd) { + if (hwnd) { pin_ptr wch = PtrToStringChars(e->Message); - MessageBox(_hwnd, wch, L"Loading Exeception", MB_OK | MB_ICONERROR); + MessageBox(hwnd, wch, L"Loading Exception", MB_OK | MB_ICONERROR); } assembly = nullptr; } @@ -140,6 +152,30 @@ namespace EsOdbcDsnBinding { } 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; @@ -147,7 +183,7 @@ namespace EsOdbcDsnBinding { delegConnectionTest = gcnew DriverCallbackDelegate(this, &EsOdbcDsnBinding::proxyConnectionTest); delegSaveDsn = gcnew DriverCallbackDelegate(this, &EsOdbcDsnBinding::proxySaveDsn); - return DsnEditorFactory::DsnEditor(onConnect, dsnInStr, delegConnectionTest, delegSaveDsn); + dsnOutLen = DsnEditorFactory::DsnEditor(onConnect, dsnInStr, delegConnectionTest, delegSaveDsn); } @@ -167,18 +203,7 @@ int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, driver_callback_ft cbSaveDsn, void *argSaveDsn) { try { - // (re)set the threading model; "Multiple calls to CoInitializeEx by the same - // thread are allowed as long as they pass the same concurrency flag". - switch (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY)) { - case S_OK: - case S_FALSE: - break; - default: - throw gcnew Exception("setting threading model failed."); - } - - _hwnd = hwnd; - EsOdbcDsnBinding::EsOdbcDsnBinding binding(onConnect, dsnInW, + EsOdbcDsnBinding::EsOdbcDsnBinding binding(hwnd, onConnect, dsnInW, cbConnectionTest, argConnectionTest, cbSaveDsn, argSaveDsn); return binding.EsOdbcDsnEditor(); From a38e80caa6314e42415995be0e48e2f79878d19b Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Mon, 15 Oct 2018 11:10:38 +0100 Subject: [PATCH 21/35] Swap cursor for a wait cursor on connection test, to give feedback to the user that the operation is underway and that they should wait. Fixes #30 --- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 3 +++ dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index 950f97af..cff17859 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -172,7 +172,10 @@ private void TestConnectionButton_Click(object sender, EventArgs e) 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) { diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs index 5a033cc6..697bf7d6 100644 --- a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Windows.Forms; namespace EsOdbcDsnEditorLauncher @@ -13,13 +14,14 @@ public Launcher() private void LaunchButton_Click(object sender, EventArgs e) { bool onConnect = true; - string dsn = "driver={IBM DB2 ODBC DRIVER};Database=SampleDB;hostname=SampleServerName;protocol=TCPIP;uid=Admin;pwd=pass!word1;secure=4"; + 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; } From 823001687d013223a8ab49bba44b75da6c03b094 Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Mon, 15 Oct 2018 11:38:29 +0100 Subject: [PATCH 22/35] Remove [X] button, to force user into using the cancel button --- .../EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 28 ++++++++-------- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 33 +++++++++++++------ dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs | 2 +- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs index 21aa2155..a91c3d59 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -59,13 +59,13 @@ private void InitializeComponent() 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(); - this.checkLoggingEnabled = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.header)).BeginInit(); this.groupSSL.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownPort)).BeginInit(); @@ -411,6 +411,19 @@ private void InitializeComponent() 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; @@ -462,19 +475,6 @@ private void InitializeComponent() this.labelLogDirectory.TabIndex = 16; this.labelLogDirectory.Text = "Log Directory:"; // - // 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); - // // DsnEditorForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index cff17859..2dc350f0 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -33,6 +33,7 @@ public DsnEditorForm( { InitializeComponent(); + // Wire up default button behaviours AcceptButton = saveButton; CancelButton = cancelButton; @@ -47,10 +48,10 @@ public DsnEditorForm( textName.Enabled = textDescription.Enabled = false; } - // If this is a call serving a connect request, call the button "Connect", otherwise it's a DSN editing, so it's going to be a "Save". + // 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 + // Parse DSN into the builder Builder.ConnectionString = dsn; // Basic Panel @@ -302,14 +303,7 @@ private void EnableDisableActionButtons() && string.IsNullOrEmpty(textHostname.Text) == false; testButton.Enabled = string.IsNullOrEmpty(textHostname.Text) == false; } - - 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(); - } - + private void CheckLoggingEnabled_CheckedChanged(object sender, EventArgs e) { EnableDisableLoggingControls(); @@ -321,5 +315,24 @@ private void EnableDisableLoggingControls() 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; + } + } } } \ No newline at end of file diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs index 697bf7d6..5494713f 100644 --- a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs @@ -13,7 +13,7 @@ public Launcher() private void LaunchButton_Click(object sender, EventArgs e) { - bool onConnect = true; + 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(); From 4a81307e545d21053f03cd278355079f572e43c3 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Mon, 15 Oct 2018 15:55:04 +0200 Subject: [PATCH 23/35] add a new conn str param, TraceEnabled - this will be used to enable/disable the per-dbc logging - the tick-box in the GUI is now left unchecked if the corresponding parameter is missing from the connection string. --- driver/defs.h | 2 ++ driver/dsn.c | 18 ++++++++++++++++-- driver/dsn.h | 4 +++- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 6 +++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/driver/defs.h b/driver/defs.h index ea2144be..52cb8e3b 100644 --- a/driver/defs.h +++ b/driver/defs.h @@ -144,6 +144,8 @@ #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" diff --git a/driver/dsn.c b/driver/dsn.c index 25307afa..43b6cc35 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -67,12 +67,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); @@ -387,6 +391,7 @@ long TEST_API write_00_list(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} @@ -655,6 +660,10 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs, &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 @@ -733,6 +742,7 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, {&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} @@ -811,8 +821,11 @@ void assign_dsn_defaults(esodbc_dsn_attrs_st *attrs) /* default: no trace file */ res |= assign_dsn_attr(attrs, - &MK_WSTR(ESODBC_DSN_TRACE_LEVEL), &MK_WSTR(ESODBC_DEF_TRACE_LEVEL), - /*overwrite?*/FALSE); + &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); } @@ -1032,6 +1045,7 @@ int prompt_user_config(HWND hwnd, BOOL on_conn, esodbc_dsn_attrs_st *attrs, 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); } diff --git a/driver/dsn.h b/driver/dsn.h index 2a397ed8..a985f61a 100644 --- a/driver/dsn.h +++ b/driver/dsn.h @@ -32,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" @@ -54,9 +55,10 @@ 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; diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index 2dc350f0..cb4a7628 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -103,10 +103,14 @@ public DsnEditorForm( switch (val) { case 0: checkLoggingEnabled.Checked = false; break; - case 1: checkLoggingEnabled.Checked = true; break; + default: checkLoggingEnabled.Checked = true; break; } } } + else + { + checkLoggingEnabled.Checked = false; + } // Set initial state of action buttons. EnableDisableActionButtons(); From 4a9aa1db926eefd29f0ba1cb1a2ef588f027b977 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Tue, 16 Oct 2018 15:45:37 +0200 Subject: [PATCH 24/35] b/f: use the correct macro for DSN editor exports - use DRIVER_BUILD macro, which signals a driver build --- dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp | 6 +++--- dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp index 73d7cf0d..b8a0b289 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp @@ -193,11 +193,11 @@ namespace EsOdbcDsnBinding { #ifdef __cplusplus extern "C" #endif /* __cpluplus */ -#ifdef _WINDLL +#ifndef DRIVER_BUILD __declspec(dllexport) -#else /* _WINDLL */ +#else /* DRIVER_BUILD */ __declspec(dllimport) -#endif /* _WINDLL */ +#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.h b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h index 88daa893..6214b123 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h @@ -60,11 +60,11 @@ typedef int(*driver_callback_ft)(void *arg, const wchar_t *connectionString, #ifdef __cplusplus extern "C" #endif /* __cpluplus */ -#ifdef _WINDLL +#ifndef DRIVER_BUILD __declspec(dllexport) -#else /* _WINDLL */ +#else /* DRIVER_BUILD */ __declspec(dllimport) -#endif /* _WINDLL */ +#endif /* DRIVER_BUILD */ int EsOdbcDsnEdit(HWND hwnd, BOOL onConnect, wchar_t *dsnInW, driver_callback_ft cbConnectionTest, void *argConnectionTest, driver_callback_ft cbSaveDsn, void *argSaveDsn); From 463bc1d5b51c4ff193dceb70f97db41fafdacfb4 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Wed, 17 Oct 2018 10:16:20 +0200 Subject: [PATCH 25/35] add missing license headers - add Elastic's license to all source files --- dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp | 6 +++++- dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h | 6 ++++++ dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs | 8 +++++++- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 10 ++++++++-- dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs | 10 ++++++++-- dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs | 10 ++++++++-- dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs | 8 +++++++- dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs | 8 +++++++- dsneditor/EsOdbcDsnEditorLauncher/Program.cs | 8 +++++++- 9 files changed, 63 insertions(+), 11 deletions(-) diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp index b8a0b289..908c96da 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp @@ -1,4 +1,8 @@ -// break at 120 columns +/* + * 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 diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h index 6214b123..5741400b 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.h @@ -1,3 +1,9 @@ +/* + * 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*) */ diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs index a91c3d59..ed568caf 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.Designer.cs @@ -1,4 +1,10 @@ -namespace EsOdbcDsnEditor +/* + * 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 { diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index cb4a7628..7d31ea91 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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; @@ -339,4 +345,4 @@ protected override CreateParams CreateParams } } } -} \ No newline at end of file +} diff --git a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs index 2e864514..975f7507 100644 --- a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -1,4 +1,10 @@ -using System.Windows.Forms; +/* + * 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: @@ -22,4 +28,4 @@ public static int DsnEditor( return dsn.Length; } } -} \ No newline at end of file +} diff --git a/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs b/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs index ccb50bf2..ce4c1190 100644 --- a/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs +++ b/dsneditor/EsOdbcDsnEditor/ExtensionMethods.cs @@ -1,4 +1,10 @@ -// uncomment to have the assembly loading to ask for (various) resources; various solutions: +/* + * 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)] @@ -16,4 +22,4 @@ public static string StripBraces(this string input) return input; } } -} \ No newline at end of file +} diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs index 4ccaf85d..f359a7db 100644 --- a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.Designer.cs @@ -1,4 +1,10 @@ -namespace EsOdbcDsnEditorLauncher +/* + * 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 { diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs index 5494713f..4bacfb02 100644 --- a/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs +++ b/dsneditor/EsOdbcDsnEditorLauncher/Launcher.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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; diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Program.cs b/dsneditor/EsOdbcDsnEditorLauncher/Program.cs index da6061e0..3f9b6f76 100644 --- a/dsneditor/EsOdbcDsnEditorLauncher/Program.cs +++ b/dsneditor/EsOdbcDsnEditorLauncher/Program.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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 From 8425ff27c2db09d26a2ffa111cc70285b6868f3b Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Wed, 17 Oct 2018 12:09:04 +0200 Subject: [PATCH 26/35] rename .cpp file to .cc to stay consistent - rename EsOdbcDsnBinding.cpp to EsOdbcDsnBinding.cc, to be consistent with the rest of the repo (.cc test files) --- .../{EsOdbcDsnBinding.cpp => EsOdbcDsnBinding.cc} | 0 dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj | 2 +- dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename dsneditor/EsOdbcDsnBinding/{EsOdbcDsnBinding.cpp => EsOdbcDsnBinding.cc} (100%) diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cc similarity index 100% rename from dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cpp rename to dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.cc diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj index a77f1942..45458646 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj @@ -179,7 +179,7 @@ - + diff --git a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters index 2f9e6e7f..c03e342e 100644 --- a/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters +++ b/dsneditor/EsOdbcDsnBinding/EsOdbcDsnBinding.vcxproj.filters @@ -15,7 +15,7 @@ - + Source Files @@ -24,4 +24,4 @@ Header Files - \ No newline at end of file + From a49c4b0511b35401612553803c36c27830014945 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Wed, 17 Oct 2018 21:22:57 +0200 Subject: [PATCH 27/35] add logging per connection With this commit it's now possible to have one log file per connection. There is now still onen type of logger, but two types of instaces (each instance writes in one dedicated log file): - per process: each process attaching to the driver DLL gets one instance. - per connection: each instantiated connection can get one instance (if its config parameters ask for one) The two types can coexist. The former is configured through an environment variable, the later through the DSN parameters. The handle logging macros (INFOH, DBGH etc.) will use the per-connection logger, if the respective handles are associated with a connection with separate logging enabled. If that's not enabled, the "global", per-process logger will be used. The non-handle logging macros (INFO, DBG etc.) can still only make use of the per-process logger. This commit also fixes the tracing (=logging of calls at driver input and output) format specifier for wide char strings with precision ('W') and adds new specifiers for SQL[U]LEN. --- driver/catalogue.c | 2 +- driver/connect.c | 152 ++++++++++++--- driver/convert.c | 2 +- driver/defs.h | 7 +- driver/handles.c | 22 +++ driver/handles.h | 25 +++ driver/info.c | 37 ++-- driver/log.c | 308 ++++++++++++++++++++----------- driver/log.h | 75 +++----- driver/odbc.c | 450 +++++++++++++++++++++++++-------------------- driver/queries.c | 3 +- driver/setup.c | 13 +- driver/tracing.h | 388 ++++++++++++++++++++------------------ driver/util.c | 2 +- 14 files changed, 887 insertions(+), 599 deletions(-) diff --git a/driver/catalogue.c b/driver/catalogue.c index 0ee1b542..f1fbb74a 100644 --- a/driver/catalogue.c +++ b/driver/catalogue.c @@ -500,7 +500,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 0a90342a..cf29614a 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; +/* this mutex protects a 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. */ +esodbc_mutex_lt filelog_cnt_mux = SRWLOCK_INIT; BOOL connect_init() { @@ -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."); @@ -642,12 +646,91 @@ static SQLRETURN test_connect(esodbc_dbc_st *dbc) RET_STATE(dbc->hdr.diag.state); } +static unsigned filelog_inc_counter() +{ + static unsigned counter = 0; + unsigned val; + + ESODBC_MUX_LOCK(&filelog_cnt_mux); + val = counter ++; + ESODBC_MUX_UNLOCK(&filelog_cnt_mux); + return val; +} + +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(), filelog_inc_counter()); + 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 '/': + case '\\': + case L'?': + case '*': + case ':': + 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 */ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) { - SQLRETURN ret; int cnt, ipv6; SQLBIGINT secure; long long timeout, max_body_size, max_fetch_size; @@ -656,17 +739,26 @@ SQLRETURN config_dbc(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)); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "security setting number " - "conversion failure", 0); + 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); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "invalid security setting", 0); + SET_HDIAG(dbc, SQL_STATE_HY000, "invalid security setting", 0); goto err; } else { dbc->secure = (long)secure; @@ -677,8 +769,8 @@ SQLRETURN config_dbc(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)); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "reading the CA file path " - "failed", 0); + SET_HDIAG(dbc, SQL_STATE_HY000, "reading the CA file path " + "failed", 0); goto err; } INFOH(dbc, "CA path: `%s`.", dbc->ca_path.str); @@ -697,20 +789,19 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) 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)); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "assembling server's URL failed", - 0); + 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)); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "server URL's UTF8 conversion " - "failed", 0); + SET_HDIAG(dbc, SQL_STATE_HY000, "server URL's UTF8 conversion " + "failed", 0); goto err; } INFOH(dbc, "connection URL: `%s`.", dbc->url.str); @@ -722,16 +813,16 @@ SQLRETURN config_dbc(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)); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "username UTF8 conversion " - "failed", 0); + 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 password [%zd] (-not shown-) to " "UTF8.", attrs->pwd.cnt); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "password UTF8 " - "conversion failed", 0); + SET_HDIAG(dbc, SQL_STATE_HY000, "password UTF8 " + "conversion failed", 0); goto err; } } @@ -747,8 +838,8 @@ SQLRETURN config_dbc(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); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "timeout setting number " - "conversion failure", 0); + SET_HDIAG(dbc, SQL_STATE_HY000, "timeout setting number " + "conversion failure", 0); goto err; } if (timeout < 0) { @@ -765,15 +856,15 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) (SQLBIGINT *)&max_body_size)) { ERRH(dbc, "failed to convert max body size `" LWPDL "` [%zu] to LL.", LWSTR(&attrs->max_body_size), attrs->max_body_size.cnt); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "max body size setting number " - "conversion failure", 0); + 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); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "invalid max body size setting " - "(negative)", 0); + 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; @@ -787,15 +878,15 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) (SQLBIGINT *)&max_fetch_size)) { ERRH(dbc, "failed to convert max fetch size `" LWPDL "` [%zu] to LL.", LWSTR(&attrs->max_fetch_size), attrs->max_fetch_size.cnt); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "max fetch size setting number " - "conversion failure", 0); + 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); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "invalid max fetch size setting " - "(negative)", 0); + SET_HDIAG(dbc, SQL_STATE_HY000, "invalid max fetch size setting " + "(negative)", 0); goto err; } else { dbc->fetch.max = (size_t)max_fetch_size; @@ -825,8 +916,7 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) } else { ERRH(dbc, "unknown packing encoding '" LWPDL "'.", LWSTR(&attrs->packing)); - ret = SET_HDIAG(dbc, SQL_STATE_HY000, "invalid packing encoding " - "setting", 0); + SET_HDIAG(dbc, SQL_STATE_HY000, "invalid packing encoding setting", 0); goto err; } INFOH(dbc, "pack JSON: %s.", dbc->pack_json ? "true" : "false"); @@ -895,8 +985,14 @@ 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; + } } SQLRETURN do_connect(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) diff --git a/driver/convert.c b/driver/convert.c index ef91809b..5117075b 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" diff --git a/driver/defs.h b/driver/defs.h index 52cb8e3b..d75f818f 100644 --- a/driver/defs.h +++ b/driver/defs.h @@ -17,7 +17,8 @@ #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" +#define ESODBC_LOG_MAX_RETRY 5 // FIXME: review@alpha /* TODO: should there be a max? */ @@ -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" /* diff --git a/driver/handles.c b/driver/handles.c index ab4ceb98..838e2a7d 100644 --- a/driver/handles.c +++ b/driver/handles.c @@ -53,6 +53,28 @@ 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); + } } /* diff --git a/driver/handles.h b/driver/handles.h index 126497ef..a47dddb6 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; /* @@ -496,6 +500,27 @@ SQLRETURN EsSQLSetDescRec( (rec)->indicator_ptr != NULL || \ (rec)->octet_length_ptr != NULL) +/* + * Logging with handle + */ + +#define LOGH(lvl, werrn, hnd, fmt, ...) \ + _LOG(HDRH(hnd)->log, lvl, werrn, "[" LWPDL "@0x%p] " fmt, \ + LWSTR(&HDRH(hnd)->typew), 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 /* __HANDLES_H__ */ diff --git a/driver/info.c b/driver/info.c index 8ee84fe8..66dfcb86 100644 --- a/driver/info.c +++ b/driver/info.c @@ -892,10 +892,7 @@ SQLRETURN EsSQLGetDiagFieldW( 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; } @@ -903,41 +900,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 */ 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..e48d3994 100644 --- a/driver/odbc.c +++ b/driver/odbc.c @@ -30,7 +30,7 @@ static BOOL driver_init() { if (log_init()) { - INFO("initializing driver %s.", ESODBC_DRIVER_VER); + INFO("initializing driver."); convert_init(); return connect_init(); } @@ -87,10 +87,11 @@ 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; } @@ -122,22 +123,25 @@ 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 ) { SQLRETURN ret; - TRACE8(_IN, "pppdpdpd", hdbc, hwnd, szConnStrIn, cchConnStrIn, + TRACE8(_IN, hdbc, "ppWhphpH", 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, "dppWhWhtH", ret, hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); return ret; } @@ -209,8 +213,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 +223,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 +235,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 +249,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 +271,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 +290,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 +345,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 +364,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 +392,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,14 +418,14 @@ 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; } @@ -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,13 +533,13 @@ 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; } @@ -586,11 +598,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,16 +621,17 @@ 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; } @@ -666,11 +679,11 @@ 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; } @@ -699,11 +712,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 +724,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 +738,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 +753,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 +785,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,11 +828,11 @@ 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; } @@ -853,36 +872,46 @@ SQLRETURN SQL_API SQLFetchScroll(SQLHSTMT 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,21 +920,21 @@ 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; } @@ -915,20 +944,22 @@ SQLRETURN SQL_API SQLGetDiagFieldW( 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,22 +969,25 @@ 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; } @@ -983,51 +1017,61 @@ SQLRETURN SQL_API SQLColumnPrivilegesW( 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 +1087,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 +1101,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,7 +1123,7 @@ 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); @@ -1119,18 +1166,21 @@ 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,7 +1188,7 @@ 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; @@ -1180,28 +1230,32 @@ SQLRETURN SQL_API SQLTablePrivilegesW 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 +1269,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,11 +1303,11 @@ 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; } @@ -1274,24 +1328,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 79e1ebf2..41ebaa03 100644 --- a/driver/queries.c +++ b/driver/queries.c @@ -1036,7 +1036,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 */ @@ -1176,6 +1176,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); } diff --git a/driver/setup.c b/driver/setup.c index e43089cf..71661aaf 100644 --- a/driver/setup.c +++ b/driver/setup.c @@ -89,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: @@ -111,8 +111,8 @@ 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 } @@ -227,7 +227,8 @@ BOOL SQL_API ConfigDSNW( int res; DWORD ierror = 0; - TRACE4(_IN, "phWW", hwndParent, fRequest, lpszDriver, lpszAttributes); + TRACE4(_IN, NULL, "phww", hwndParent, fRequest, lpszDriver, + lpszAttributes); init_dsn_attrs(&attrs); @@ -280,7 +281,7 @@ BOOL SQL_API ConfigDSNW( SQLPostInstallerError(ODBC_ERROR_REQUEST_FAILED, NULL); } - TRACE5(_OUT, "dphWW", ierror, hwndParent, fRequest, lpszDriver, + TRACE5(_OUT, NULL, "dphww", ierror, hwndParent, fRequest, lpszDriver, lpszAttributes); return ierror == 0; } diff --git a/driver/tracing.h b/driver/tracing.h index 0e3e2dac..29ebb67a 100644 --- a/driver/tracing.h +++ b/driver/tracing.h @@ -16,8 +16,10 @@ /*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; \ switch(type) { \ /* * numeric pointers @@ -66,48 +68,66 @@ _n = snprintf(_bf + _ps, _AVAIL, "@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(_bf + _ps, _AVAIL, "[%d] `" LWPDL "`", \ + _prec, _prec, _w); \ + } else { \ + _n = snprintf(_bf + _ps, _AVAIL, 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(_bf + _ps, _AVAIL, "[%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(_bf + _ps, _AVAIL, "[%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(_bf + _ps, _AVAIL, "%lld", \ + (long long)(intptr_t)val); \ + break; \ + case 'Z': /* long long unsigned (SQLULEN) */ \ + _n = snprintf(_bf + _ps, _AVAIL, "%llu", \ + (unsigned long long)(uintptr_t)val); \ + break; \ /* longs */ \ - case 'l': /* long signed */ \ + case 'l': /* long signed (SQLINTEGER) */ \ _n = snprintf(_bf + _ps, _AVAIL, "%ld", \ (long)(intptr_t)val); \ - break;\ - case 'L': /* long unsigned */ \ + break; \ + case 'L': /* long unsigned (SQLUINTEGER) */ \ _n = snprintf(_bf + _ps, _AVAIL, "%lu", \ (unsigned long)(uintptr_t)val); \ - break;\ + break; \ /* ints */ \ case 'd': /* int signed */ \ _n = snprintf(_bf + _ps, _AVAIL, "%d", \ (int)(intptr_t)val); \ - break;\ + break; \ case 'u': /* int unsigned */ \ _n = snprintf(_bf + _ps, _AVAIL, "%u", \ (unsigned)(uintptr_t)val); \ - break;\ - case 'h': /* short signed */ \ + break; \ + case 'h': /* short signed (SQLSMALLINT) */ \ _n = snprintf(_bf + _ps, _AVAIL, "%hd", \ (short)(intptr_t)val); \ - break;\ - case 'H': /* short unsigned */ \ + break; \ + case 'H': /* short unsigned (SQLUSMALLINT) */ \ _n = snprintf(_bf + _ps, _AVAIL, "%hu", \ (unsigned short)(uintptr_t)val); \ - break;\ + break; \ default: \ _n = snprintf(_bf+_ps, _AVAIL, "BUG! unknown type: %d",type); \ break; \ @@ -133,7 +153,7 @@ } \ } 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); \ @@ -141,7 +161,7 @@ _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 +170,213 @@ #define _OUT 1 #define _TRACE_OUT "EXIT: " -#define _TRACE_DECLARATION(end) \ - char _bf[1024]; /*"ought to be enough for anybody"*/\ +#define _TRACE_HEADER(inout, hnd) \ + char _bf[1024]; \ int _ps = 0; \ - _ps += snprintf(_bf + _ps, _AVAIL, end ? _TRACE_OUT : _TRACE_IN) + 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; \ + } \ + _ps += snprintf(_bf + _ps, _AVAIL, inout ? _TRACE_OUT : _TRACE_IN) -#define _TRACE_ENDING \ +#define _TRACE_FOOTER \ _ps += snprintf(_bf + _ps, _AVAIL, "."); \ - LOG(TRACE_LOG_LEVEL, "%s", _bf) + _esodbc_log(_log, TRACE_LOG_LEVEL, /*werr*/0, \ + __func__, __FILE__, __LINE__, "%s", _bf); -#define TRACE1(out, fmt, p0) \ +#define TRACE1(inout, hnd, fmt, p0) \ do { \ - _TRACE_DECLARATION(out); \ - _PRINT_PARAM(fmt[0], p0, 0); \ - _TRACE_ENDING; \ + _TRACE_HEADER(inout, hnd); \ + _PRINT_PARAM(fmt[0], p0, SQL_NTS, 0); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE2(out, fmt, p0, p1) \ +#define TRACE2(inout, hnd, fmt, p0, p1) \ 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, p1, 0); \ + _PRINT_PARAM(fmt[1], p1, SQL_NTS, 1); \ + _TRACE_FOOTER; \ } while(0) -#define TRACE3(out, fmt, p0, p1, p2) \ +#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); \ - _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 TRACE4(out, fmt, p0, p1, p2, p3) \ +#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); \ - _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 TRACE5(out, fmt, p0, p1, p2, p3, p4) \ +#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); \ - _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 TRACE6(out, fmt, p0, p1, p2, p3, p4, p5) \ +#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); \ - _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 TRACE7(out, fmt, p0, p1, p2, p3, p4, p5, p6) \ +#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); \ - _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 TRACE8(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7) \ +#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); \ - _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 TRACE9(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8) \ +#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); \ - _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 TRACE10(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) \ +#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); \ - _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 TRACE11(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) \ +#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); \ - _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(out, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) \ - 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; \ - } 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 52653b3d..a56d2b51 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" From 2b4faaa8ffa294bcc4a0dc0263367013ceae3f57 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 18 Oct 2018 16:39:59 +0200 Subject: [PATCH 28/35] b/f: fix trace buffer overrun checks - snprintf won't fail in case the formatted string doesn't fit the given buffer (unlike swprintf), so the "buffer too small" condition needs explicit checks. --- driver/tracing.h | 88 ++++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/driver/tracing.h b/driver/tracing.h index 29ebb67a..76d3c49b 100644 --- a/driver/tracing.h +++ b/driver/tracing.h @@ -12,7 +12,25 @@ #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 */ @@ -20,6 +38,9 @@ do { \ int _prec; \ wchar_t *_w; \ + if (_AVAIL(_ps) <= 0) { \ + break; \ + } \ switch(type) { \ /* * numeric pointers @@ -27,45 +48,45 @@ /* 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; \ /* TODO: support for (buffer*, max, written*) pattern */ \ @@ -74,19 +95,19 @@ _w = (wchar_t *)(uintptr_t)val; \ _prec = (int)(intptr_t)prec; \ _prec = (_prec == SQL_NTS) ? (int)wcslen(_w) : _prec; \ - _n = snprintf(_bf + _ps, _AVAIL, "[%d] `" LWPDL "`", \ - _prec, _prec, _w); \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), \ + "[%d] `" LWPDL "`", _prec, _prec, _w); \ } else { \ - _n = snprintf(_bf + _ps, _AVAIL, TS_NULL); \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), TS_NULL); \ } \ break; \ case 'w': /* NTS wchar_t* */ \ - _n = snprintf(_bf + _ps, _AVAIL, "[%zu] `" LWPD "`", \ + _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(_bf + _ps, _AVAIL, "[%zu] `" LCPD "`", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "[%zu] `" LCPD "`", \ val ? strlen((char *)(uintptr_t)val) : 0, \ val ? (char *)(uintptr_t)val : TS_NULL); \ break; \ @@ -95,45 +116,45 @@ */ \ /* long longs */ \ case 'z': /* long long signed (SQLLEN) */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%lld", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lld", \ (long long)(intptr_t)val); \ break; \ case 'Z': /* long long unsigned (SQLULEN) */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%llu", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%llu", \ (unsigned long long)(uintptr_t)val); \ break; \ /* longs */ \ case 'l': /* long signed (SQLINTEGER) */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%ld", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%ld", \ (long)(intptr_t)val); \ break; \ case 'L': /* long unsigned (SQLUINTEGER) */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%lu", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lu", \ (unsigned long)(uintptr_t)val); \ break; \ /* ints */ \ case 'd': /* int signed */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%d", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%d", \ (int)(intptr_t)val); \ 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 (SQLSMALLINT) */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hd", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hd", \ (short)(intptr_t)val); \ break; \ case 'H': /* short unsigned (SQLUSMALLINT) */ \ - _n = snprintf(_bf + _ps, _AVAIL, "%hu", \ + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hu", \ (unsigned short)(uintptr_t)val); \ 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*/ @@ -157,8 +178,8 @@ 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, prec); \ @@ -171,20 +192,21 @@ #define _TRACE_OUT "EXIT: " #define _TRACE_HEADER(inout, hnd) \ - char _bf[1024]; \ - int _ps = 0; \ + 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; \ } \ - _ps += snprintf(_bf + _ps, _AVAIL, inout ? _TRACE_OUT : _TRACE_IN) + _n = snprintf(_BUFF + _ps, _AVAIL(_ps), inout ? _TRACE_OUT : _TRACE_IN); \ + _CHECK_WRITE(_n, _ps); #define _TRACE_FOOTER \ - _ps += snprintf(_bf + _ps, _AVAIL, "."); \ _esodbc_log(_log, TRACE_LOG_LEVEL, /*werr*/0, \ - __func__, __FILE__, __LINE__, "%s", _bf); + __func__, __FILE__, __LINE__, "%s.", _BUFF); + #define TRACE1(inout, hnd, fmt, p0) \ do { \ From d4fabfe50079135800e4642a8b3de754a219e9b5 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 18 Oct 2018 16:54:57 +0200 Subject: [PATCH 29/35] fix: better source choice for error reporting - in some cases (like select fields count is larger than an index limit) the error reason given in "error"."root_cause" field is different and more relevant than the "error"."reason" field; => if available, choose that message in the first element in the "root_cause" array to post as failure diagnostic. --- driver/queries.c | 116 +++++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 38 deletions(-) diff --git a/driver/queries.c b/driver/queries.c index 41ebaa03..4562af9a 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" @@ -314,21 +315,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,48 +350,74 @@ 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 always append the 0-term, but fail if formated string - * would overrun the buffer size (in an equivocal way: overrun encoding - * error) => 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'; + /* 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; + } 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(&HDRH(hnd)->diag, SQL_STATE_HY000, wbuf, UJNumericInt(o_status)); From e665ec81b76073abff3e3f7c0e7afd4e9f5287a3 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 18 Oct 2018 18:42:11 +0200 Subject: [PATCH 30/35] fix: don't output the PWD in the logs - replace the password attribute value with a substitute - the places where these are part of a string are commented out for release builds. --- driver/connect.c | 2 ++ driver/defs.h | 1 + driver/dsn.c | 40 ++++++++++++++++++++++++++++++---------- driver/odbc.c | 11 +++++++++-- driver/setup.c | 13 ++++++++++--- driver/util.h | 5 ++--- 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index cf29614a..e0ceec40 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -1765,8 +1765,10 @@ SQLRETURN EsSQLDriverConnectW init_dsn_attrs(&attrs); if (szConnStrIn) { +#ifndef NDEBUG /* don't print the PWD */ DBGH(dbc, "Input connection string: '"LWPD"'[%d].", szConnStrIn, cchConnStrIn); +#endif /* NDEBUG */ /* parse conn str into attrs */ if (! parse_connection_string(&attrs, szConnStrIn, cchConnStrIn)) { ERRH(dbc, "failed to parse connection string `" LWPDL "`.", diff --git a/driver/defs.h b/driver/defs.h index d75f818f..e0e19e9e 100644 --- a/driver/defs.h +++ b/driver/defs.h @@ -149,6 +149,7 @@ #define ESODBC_DEF_TRACE_ENABLED "0" /* default tracing level */ #define ESODBC_DEF_TRACE_LEVEL "WARN" +#define ESODBC_PWD_VAL_SUBST "" /* * diff --git a/driver/dsn.c b/driver/dsn.c index 43b6cc35..07c45624 100644 --- a/driver/dsn.c +++ b/driver/dsn.c @@ -11,6 +11,7 @@ #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" @@ -30,6 +31,15 @@ void TEST_API 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 @@ -92,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)); @@ -318,7 +330,7 @@ BOOL TEST_API 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)); @@ -579,7 +591,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.", @@ -684,7 +696,7 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs, if (EQ_WSTR(iter->new, iter->old)) { DBG("DSN `" LWPDL "` attribute " LWPDL " maintained " "value `" LWPDL "`.", LWSTR(&new_attrs->dsn), - LWSTR(iter->kw), LWSTR(iter->new)); + LWSTR(iter->kw), LWSTR(mask_pwd(iter->kw, iter->new))); continue; } if (! SQLWritePrivateProfileStringW(new_attrs->dsn.str, @@ -694,21 +706,23 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs, iter->new->cnt ? iter->new->str : NULL, MK_WPTR(SUBKEY_ODBC))) { ERR("failed to write key `" LWPDL "` with value `" LWPDL "`.", - LWSTR(iter->kw), LWSTR(iter->new)); + 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(iter->new), + 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(iter->new)); + 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(iter->new)); + LWSTR(&new_attrs->dsn), LWSTR(iter->kw), + LWSTR(mask_pwd(iter->kw, iter->new))); } } return TRUE; @@ -782,7 +796,9 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs, } } +#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; } @@ -962,10 +978,14 @@ int validate_dsn(esodbc_dsn_attrs_st *attrs, const wchar_t *dsn_str, } #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."); diff --git a/driver/odbc.c b/driver/odbc.c index e48d3994..7701af29 100644 --- a/driver/odbc.c +++ b/driver/odbc.c @@ -134,14 +134,21 @@ SQLRETURN SQL_API SQLDriverConnectW 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, hdbc, "ppWhphpH", 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, hdbc, "dppWhWhtH", ret, hdbc, hwnd, szConnStrIn, cchConnStrIn, + TRACE9(_OUT, hdbc, fmt_out, ret, hdbc, hwnd, szConnStrIn, cchConnStrIn, szConnStrOut, cchConnStrOutMax, pcchConnStrOut, fDriverCompletion); return ret; } diff --git a/driver/setup.c b/driver/setup.c index 71661aaf..e65d3b18 100644 --- a/driver/setup.c +++ b/driver/setup.c @@ -227,9 +227,16 @@ BOOL SQL_API ConfigDSNW( int res; DWORD ierror = 0; - TRACE4(_IN, NULL, "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); /* assign the Driver name; this is not the value of the Driver key in the @@ -281,7 +288,7 @@ BOOL SQL_API ConfigDSNW( SQLPostInstallerError(ODBC_ERROR_REQUEST_FAILED, NULL); } - TRACE5(_OUT, NULL, "dphww", ierror, hwndParent, fRequest, lpszDriver, + TRACE5(_OUT, NULL, fmt_out, ierror, hwndParent, fRequest, lpszDriver, lpszAttributes); return ierror == 0; } diff --git a/driver/util.h b/driver/util.h index a5534147..d510ae3b 100644 --- a/driver/util.h +++ b/driver/util.h @@ -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. */ From b503e379d56e8920e1ff09200d8329980b78ba19 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 18 Oct 2018 19:28:00 +0200 Subject: [PATCH 31/35] fix: post proper code for unimplemented APIs - signal through the provided input handle when a API function is not implemented. --- driver/info.c | 2 +- driver/odbc.c | 112 ++++++++++---------------------------------------- 2 files changed, 23 insertions(+), 91 deletions(-) diff --git a/driver/info.c b/driver/info.c index 66dfcb86..7cbdfcf6 100644 --- a/driver/info.c +++ b/driver/info.c @@ -75,7 +75,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, diff --git a/driver/odbc.c b/driver/odbc.c index 7701af29..a573397f 100644 --- a/driver/odbc.c +++ b/driver/odbc.c @@ -16,14 +16,11 @@ //#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) @@ -95,7 +92,6 @@ SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, return ret; } -#if WITH_EMPTY /* * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/unicode-drivers : * """ @@ -114,10 +110,9 @@ SQLRETURN SQL_API SQLConnectW SQLSMALLINT cchAuthStr ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hdbc); } -#endif /* WITH_EMPTY */ SQLRETURN SQL_API SQLDriverConnectW ( @@ -153,7 +148,6 @@ SQLRETURN SQL_API SQLDriverConnectW return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLBrowseConnectW ( @@ -166,53 +160,9 @@ SQLRETURN SQL_API SQLBrowseConnectW SQLSMALLINT *pcchConnStrOut ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hdbc); } - - -/* - * - * 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; -} - -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, @@ -436,7 +386,6 @@ SQLRETURN SQL_API SQLSetDescFieldW return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLGetDescRecW( SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, @@ -459,7 +408,7 @@ SQLRETURN SQL_API SQLGetDescRecW( _Out_opt_ SQLSMALLINT *NullablePtr) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(DescriptorHandle); } @@ -475,7 +424,7 @@ SQLRETURN SQL_API SQLSetDescRec( _Inout_opt_ SQLLEN *StringLength, _Inout_opt_ SQLLEN *Indicator) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(DescriptorHandle); } /* @@ -493,9 +442,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 @@ -551,7 +499,6 @@ SQLRETURN SQL_API SQLBindParameter( return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLGetCursorNameW ( SQLHSTMT hstmt, @@ -561,7 +508,7 @@ SQLRETURN SQL_API SQLGetCursorNameW SQLSMALLINT *pcchCursor ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLSetCursorNameW @@ -571,7 +518,7 @@ SQLRETURN SQL_API SQLSetCursorNameW SQLSMALLINT cchCursor ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLSetScrollOptions( /* Use SQLSetStmtOptions */ @@ -580,9 +527,8 @@ SQLRETURN SQL_API SQLSetScrollOptions( /* Use SQLSetStmtOptions */ SQLLEN crowKeyset, SQLUSMALLINT crowRowset) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif // WITH_EMPTY /* * @@ -642,7 +588,6 @@ SQLRETURN SQL_API SQLExecDirectW return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLNativeSqlW ( SQLHDBC hdbc, @@ -653,7 +598,7 @@ SQLRETURN SQL_API SQLNativeSqlW SQLINTEGER *pcchSqlStr ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hdbc); } /* @@ -676,9 +621,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, @@ -694,20 +638,18 @@ SQLRETURN SQL_API SQLNumParams( 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 */ /* * @@ -843,7 +785,6 @@ SQLRETURN SQL_API SQLFetch(SQLHSTMT 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 @@ -875,9 +816,8 @@ 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, @@ -998,7 +938,6 @@ SQLRETURN SQL_API SQLGetDiagRecW return ret; } -#if WITH_EMPTY /* * * Obtaining information about the data source's system tables @@ -1018,9 +957,8 @@ SQLRETURN SQL_API SQLColumnPrivilegesW( SQLSMALLINT cchColumnName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif /* WITH_EMPTY */ SQLRETURN SQL_API SQLColumnsW ( @@ -1137,7 +1075,6 @@ SQLRETURN SQL_API SQLPrimaryKeysW return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLProcedureColumnsW ( SQLHSTMT hstmt, @@ -1151,7 +1088,7 @@ SQLRETURN SQL_API SQLProcedureColumnsW SQLSMALLINT cchColumnName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLProceduresW @@ -1165,9 +1102,8 @@ SQLRETURN SQL_API SQLProceduresW SQLSMALLINT cchProcName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif // WITH_EMPTY SQLRETURN SQL_API SQLSpecialColumnsW ( @@ -1201,7 +1137,6 @@ SQLRETURN SQL_API SQLSpecialColumnsW return ret; } -#if WITH_EMPTY SQLRETURN SQL_API SQLStatisticsW ( SQLHSTMT hstmt, @@ -1215,7 +1150,7 @@ SQLRETURN SQL_API SQLStatisticsW SQLUSMALLINT fAccuracy ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } SQLRETURN SQL_API SQLTablePrivilegesW @@ -1229,10 +1164,9 @@ SQLRETURN SQL_API SQLTablePrivilegesW SQLSMALLINT cchTableName ) { - RET_NOT_IMPLEMENTED; + RET_NOT_IMPLEMENTED(hstmt); } -#endif /* WITH_EMPTY */ SQLRETURN SQL_API SQLTablesW ( @@ -1318,13 +1252,11 @@ SQLRETURN SQL_API SQLCancelHandle(SQLSMALLINT HandleType, 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 */ /* From 36a61f0f1bccd6251942006a3afc5a33a738ffb8 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Thu, 18 Oct 2018 23:00:46 +0200 Subject: [PATCH 32/35] complete DBC attributes setting/getting - add the missing attribute handling for a connection handle; most are read-only attributes either marking an unsupported feature or informing the caller on how the driver/DSN work. --- driver/connect.c | 94 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index e0ceec40..a3418e97 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -1977,19 +1977,6 @@ SQLRETURN EsSQLSetConnectAttrW( //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."); @@ -2043,6 +2030,53 @@ 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; + + case SQL_ATTR_CONNECTION_TIMEOUT: + DBGH(dbc, "setting connection timeout: %lu", + (SQLUINTEGER)(uintptr_t)Value); + dbc->timeout = (SQLUINTEGER)(uintptr_t)Value; + break; + case SQL_ATTR_LOGIN_TIMEOUT: + WARNH(dbc, "no individual login timeout supported"); + if (dbc->timeout != (SQLUINTEGER)(uintptr_t)Value) { + RET_HDIAGS(dbc, SQL_STATE_01S02); + } + 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); @@ -2109,22 +2143,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_LOGIN_TIMEOUT: + WARNH(dbc, "no login timeout supported: use connection timeout."); case SQL_ATTR_CONNECTION_TIMEOUT: + DBGH(dbc, "getting 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 From d23edf5da248fd72a3235d80912cb66cb8ba7ab0 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 19 Oct 2018 00:29:46 +0200 Subject: [PATCH 33/35] extend the use of handle-logging for DBC operation - convert logging lines to handle-logging (INFOH() etc.) where is possible, so that those messages end up in per-connection log files (which can be provided for troubleshooting purposes). --- driver/connect.c | 25 ++++++++++++------------- driver/convert.c | 13 +++++++------ driver/error.c | 15 ++++++++------- driver/error.h | 12 +++--------- driver/handles.c | 20 ++++++++++---------- driver/handles.h | 20 ++++++++++---------- driver/queries.c | 9 ++++----- driver/util.c | 4 ++-- 8 files changed, 56 insertions(+), 62 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index a3418e97..352d4c29 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -418,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); @@ -541,12 +541,11 @@ static void dbc_curl_post_diag(esodbc_dbc_st *dbc) * 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(&HDRH(dbc)->diag, SQL_STATE_08S01, + 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(&HDRH(dbc)->diag, SQL_STATE_08S01, buff, - dbc->curl_err); + post_diagnostic(dbc, SQL_STATE_08S01, buff, dbc->curl_err); } } @@ -1344,8 +1343,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; @@ -1410,7 +1409,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 { @@ -1424,7 +1423,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); } @@ -1433,11 +1432,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), @@ -1451,8 +1450,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; } @@ -1679,7 +1678,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; } diff --git a/driver/convert.c b/driver/convert.c index 5117075b..2218ed2e 100644 --- a/driver/convert.c +++ b/driver/convert.c @@ -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 */ diff --git a/driver/error.c b/driver/error.c index 61e62b99..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, +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, +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 5b7f85a1..17ca6d64 100644 --- a/driver/error.h +++ b/driver/error.h @@ -443,18 +443,12 @@ typedef struct { void init_diagnostic(esodbc_diag_st *dest); -SQLRETURN post_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, +SQLRETURN post_diagnostic(SQLHANDLE hnd, esodbc_state_et state, const SQLWCHAR *text, SQLINTEGER code); -SQLRETURN post_c_diagnostic(esodbc_diag_st *dest, esodbc_state_et state, +SQLRETURN post_c_diagnostic(SQLHANDLE hnd, esodbc_state_et state, const SQLCHAR *text, SQLINTEGER code); -/* post state into the diagnostic and return state's return code */ -#define RET_DIAG(_d/*iag dest*/, _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_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 838e2a7d..4409d0c2 100644 --- a/driver/handles.c +++ b/driver/handles.c @@ -325,7 +325,7 @@ SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType, ERRN("OOM for handle type %hd, on input handle@0x%p.", HandleType, InputHandle); if (InputHandle) { - RET_DIAG(&HDRH(InputHandle)->diag, SQL_STATE_HY001, NULL, 0); + RET_HDIAGS(InputHandle, SQL_STATE_HY001); } else { RET_STATE(SQL_STATE_HY001); } @@ -1244,13 +1244,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); @@ -1340,10 +1341,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", @@ -1403,7 +1404,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; } @@ -1549,8 +1550,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 /* @@ -1566,7 +1566,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); @@ -2278,7 +2278,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." diff --git a/driver/handles.h b/driver/handles.h index a47dddb6..b4f4dcf7 100644 --- a/driver/handles.h +++ b/driver/handles.h @@ -471,17 +471,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(&HDRH(_hp)->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(&HDRH(_hp)->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(&HDRH(_hp)->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) \ @@ -504,15 +504,15 @@ SQLRETURN EsSQLSetDescRec( * Logging with handle */ -#define LOGH(lvl, werrn, hnd, fmt, ...) \ +#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(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 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 { \ diff --git a/driver/queries.c b/driver/queries.c index 4562af9a..006d1184 100644 --- a/driver/queries.c +++ b/driver/queries.c @@ -419,8 +419,7 @@ static BOOL attach_sql_error(SQLHANDLE hnd, cstr_st *body) assert(wbuf[cnt] == L'\0'); ERRH(hnd, "request failure reason: [%d] `" LWPD "`.", cnt, wbuf); - post_diagnostic(&HDRH(hnd)->diag, SQL_STATE_HY000, wbuf, - UJNumericInt(o_status)); + post_diagnostic(hnd, SQL_STATE_HY000, wbuf, UJNumericInt(o_status)); ret = TRUE; end: @@ -450,13 +449,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); } /* @@ -665,7 +664,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) \ diff --git a/driver/util.c b/driver/util.c index a56d2b51..96cc387e 100644 --- a/driver/util.c +++ b/driver/util.c @@ -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); } From 92c3e411c29d7a6fb130a7a32a7865654ad6d173 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 19 Oct 2018 10:47:38 +0200 Subject: [PATCH 34/35] more logging migrated to handle-logging - ERR -> ERRH --- driver/convert.c | 59 +++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/driver/convert.c b/driver/convert.c index 2218ed2e..b5c9787b 100644 --- a/driver/convert.c +++ b/driver/convert.c @@ -1360,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*/]; @@ -1379,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 */ @@ -1405,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); @@ -1420,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) */ @@ -1434,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) { @@ -1462,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; @@ -1481,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; @@ -1494,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; } @@ -1528,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) { @@ -1536,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; @@ -1958,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; @@ -1986,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: @@ -2003,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: @@ -2022,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; @@ -2094,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); @@ -2194,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); @@ -2537,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) { From 4d63ca10c4aaad09d53b73fa6d8268d689be4c54 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Tue, 23 Oct 2018 23:51:01 +0200 Subject: [PATCH 35/35] fixes: atomic inc for logger; consist. wide subst - use an atomic increment for log file stamping, instead of a mutex; - correct the mixing of narrow and wide chars when escaping URL chars to be used in logging file name. --- driver/connect.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/driver/connect.c b/driver/connect.c index 352d4c29..5407276a 100644 --- a/driver/connect.c +++ b/driver/connect.c @@ -96,10 +96,10 @@ typedef struct { * HTTP headers used for all requests (Content-Type, Accept). */ static struct curl_slist *http_headers = NULL; -/* this mutex protects a counter used to number DBC log files: +/* 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. */ -esodbc_mutex_lt filelog_cnt_mux = SRWLOCK_INIT; +volatile unsigned filelog_cnt = 0; BOOL connect_init() { @@ -645,17 +645,6 @@ static SQLRETURN test_connect(esodbc_dbc_st *dbc) RET_STATE(dbc->hdr.diag.state); } -static unsigned filelog_inc_counter() -{ - static unsigned counter = 0; - unsigned val; - - ESODBC_MUX_LOCK(&filelog_cnt_mux); - val = counter ++; - ESODBC_MUX_UNLOCK(&filelog_cnt_mux); - return val; -} - static BOOL config_dbc_logging(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) { int cnt, level; @@ -671,7 +660,7 @@ static BOOL config_dbc_logging(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) cnt = swprintf(ident.str, ident.cnt, WPFWP_LDESC "_" WPFWP_LDESC "_" "%d-%u", LWSTR(&attrs->server), LWSTR(&attrs->port), - GetCurrentProcessId(), filelog_inc_counter()); + 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); @@ -689,11 +678,11 @@ static BOOL config_dbc_logging(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs) case L'<': case L'>': case L'|': - case '/': - case '\\': + case L'/': + case L'\\': case L'?': - case '*': - case ':': + case L'*': + case L':': ident.str[cnt] = L'_'; } }