From 91bf4b36d32318614963e3d0c16907e200e66a5d Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 28 Sep 2018 00:09:19 +0200 Subject: [PATCH 01/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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 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 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 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 Date: Thu, 4 Oct 2018 12:26:29 +0200 Subject: [PATCH 07/28] 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/28] 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 + - 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 - 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 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 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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 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 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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 d93ffa2d5ff79ad0e18561143dc35e585cc55967 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Tue, 23 Oct 2018 11:39:34 +0200 Subject: [PATCH 27/28] detail one comment - add an explanation as to why the editor form is rendered with .ShowDialog() instead of Application.Run(). --- dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs index 975f7507..90eb44d5 100644 --- a/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs +++ b/dsneditor/EsOdbcDsnEditor/DsnEditorFactory.cs @@ -23,7 +23,8 @@ public static int DsnEditor( { Application.EnableVisualStyles(); DsnEditorForm form = new DsnEditorForm(onConnect, dsnIn, delegConnectionTest, delegSaveDsn); - form.ShowDialog(); // Instead of Application.Run(form); + // instead of Application.Run(form): prevent STA apps from launching a new editor within the same thread + form.ShowDialog(); var dsn = form.Builder.ToString(); return dsn.Length; } From aa709b84983577522a1fe3973ce2431fc78d468c Mon Sep 17 00:00:00 2001 From: Stuart Cam Date: Wed, 24 Oct 2018 13:58:02 +1100 Subject: [PATCH 28/28] Address PR comments --- dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs | 11 ++++++++--- dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs | 5 ++--- .../Properties/AssemblyInfo.cs | 11 +++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs index 7d31ea91..2033e262 100644 --- a/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs +++ b/dsneditor/EsOdbcDsnEditor/DSNEditorForm.cs @@ -263,7 +263,7 @@ private bool ValidateLogFolderPath(string path) return true; } - MessageBox.Show("Log directory invalid", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show("Log directory invalid, path does not exist", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } @@ -282,8 +282,13 @@ private void CertificatePathButton_Click(object sender, EventArgs e) private bool ValidateCertificateFile(string file) { - if (string.IsNullOrEmpty(file) - || (File.Exists(file) && File.ReadAllBytes(file).Length > 0)) + if (string.IsNullOrEmpty(file)) + { + return true; + } + + var info = new FileInfo(file); + if (info.Exists && info.Length > 0) { return true; } diff --git a/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs b/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs index 8f2e18a4..5bd7a64f 100644 --- a/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs +++ b/dsneditor/EsOdbcDsnEditor/Properties/AssemblyInfo.cs @@ -1,12 +1,11 @@ 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: AssemblyTitle("Elasticsearch DSN Editor")] +[assembly: AssemblyDescription("Elasticsearch DSN Editor for managing ODBC connection strings.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Elasticsearch B.V.")] [assembly: AssemblyProduct("Elasticsearch ODBC Driver")] diff --git a/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs b/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs index 287a0da7..62de8362 100644 --- a/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs +++ b/dsneditor/EsOdbcDsnEditorLauncher/Properties/AssemblyInfo.cs @@ -1,16 +1,15 @@ 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: AssemblyTitle("Elasticsearch DSN Editor Launcher")] +[assembly: AssemblyDescription("Elasticsearch DSN Editor Launcher.")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("EsOdbcDsnEditorLauncher")] -[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyCompany("Elasticsearch B.V.")] +[assembly: AssemblyProduct("Elasticsearch ODBC Driver")] +[assembly: AssemblyCopyright("Copyright Elasticsearch B.V. ©")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")]