diff --git a/libmariadb/ma_default.c b/libmariadb/ma_default.c index 80efe1c9a..0d5530db6 100644 --- a/libmariadb/ma_default.c +++ b/libmariadb/ma_default.c @@ -24,9 +24,12 @@ #include #include #include +#include #ifdef _WIN32 #include +#include +#include #include "shlwapi.h" #define access _access @@ -34,6 +37,7 @@ static const char *ini_exts[]= {"ini", "cnf", 0}; #define R_OK 4 #else +#include #include static const char *ini_exts[]= {"cnf", 0}; #endif @@ -152,6 +156,42 @@ static my_bool is_group(char *ptr, const char **groups) return 0; } +static my_bool is_config_file(char *path) +{ + char *end; + if (access(path, R_OK)) { + return 0; + } + end = path + strlen(path); + for (int exts = 0; ini_exts[exts]; exts++) { + size_t ext_length = strlen(ini_exts[exts]); + char *ext_start = end - ext_length - 1; + + if ((ext_start >= path) && (*ext_start == '.')) { + #ifdef _WIN32 + if (!_stricmp(ext_start + 1, ini_exts[exts])) { + return 1; + } + #else + if (!strcmp(ext_start + 1, ini_exts[exts])) { + return 1; + } + #endif + } + } + return 0; +} + +static int compare_filenames(const void* a, const void* b) { + const char *name_a = *(const char **)a; + const char *name_b = *(const char **)b; + #ifdef _WIN32 + return _stricmp(name_a, name_b); + #else + return strcmp(name_a, name_b); + #endif +} + static my_bool _mariadb_read_options_from_file(MYSQL *mysql, const char *config_file, const char *group, @@ -200,8 +240,59 @@ static my_bool _mariadb_read_options_from_file(MYSQL *mysql, end= strchr(val, 0); for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */ *end= 0; - if (!strcmp(ptr, "includedir")) - _mariadb_read_options(mysql, (const char *)val, NULL, group, recursion + 1); + if (!strcmp(ptr, "includedir")) { + DYNAMIC_ARRAY filenames; + #ifdef _WIN32 + #define MAX_INCLUDE_PATH_LENGTH 4096 + MAX_PATH + HANDLE hFind = NULL; + WIN32_FIND_DATA fdFile; + TCHAR cIncDirFilePattern[MAX_INCLUDE_PATH_LENGTH]; + #else + #define MAX_INCLUDE_PATH_LENGTH 4096 + 256 + DIR *dir; + struct dirent *ent; + #endif + char inc_config_path[MAX_INCLUDE_PATH_LENGTH]; + ma_init_dynamic_array(&filenames, sizeof(char*), 10, 10); + #ifdef _WIN32 + for (int exts = 0; ini_exts[exts]; exts++) { + snprintf(cIncDirFilePattern, MAX_INCLUDE_PATH_LENGTH, "%s%c*.%s", val, FN_LIBCHAR, ini_exts[exts]); + if ((hFind = FindFirstFile((const char*)cIncDirFilePattern, &fdFile)) == INVALID_HANDLE_VALUE) { + continue; + } + do { + snprintf(inc_config_path, MAX_INCLUDE_PATH_LENGTH, "%s%c%s", val, FN_LIBCHAR, fdFile.cFileName); + if (!access(inc_config_path, R_OK)) { + char* filename = strdup(fdFile.cFileName); + ma_insert_dynamic(&filenames, (gptr)&filename); + } + } while (FindNextFile(hFind, &fdFile)); + FindClose(hFind); + } + #else + if (!(dir = opendir((const char *)val))) { + goto err; + } + while ((ent = readdir(dir))) { + snprintf(inc_config_path, MAX_INCLUDE_PATH_LENGTH, "%s%c%s", val, FN_LIBCHAR, ent->d_name); + if (is_config_file(inc_config_path)) { + /* _mariadb_read_options(mysql, NULL, (const char *)inc_config_path, group, recursion + 1); */ + char *filename = strdup(ent->d_name); + ma_insert_dynamic(&filenames, (gptr)&filename); + } + } + closedir(dir); + #endif + qsort(filenames.buffer, filenames.elements, filenames.size_of_element, compare_filenames); + for (uint fi = 0; fi < filenames.elements; fi++) { + char* filename; + ma_get_dynamic(&filenames, (void *)&filename, fi); + snprintf(inc_config_path, MAX_INCLUDE_PATH_LENGTH, "%s%c%s", val, FN_LIBCHAR, filename); + _mariadb_read_options(mysql, NULL, (const char *)inc_config_path, group, recursion + 1); + free(filename); + } + ma_delete_dynamic(&filenames); + } else if (!strcmp(ptr, "include")) _mariadb_read_options(mysql, NULL, (const char *)val, group, recursion + 1); continue; diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index 82930fe9f..3c63b20c3 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -26,6 +26,12 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "my_test.h" +#ifdef _WIN32 +#include +#else +#include +#include +#endif static int test_conc66(MYSQL *my) { @@ -1589,6 +1595,102 @@ static int test_conc327(MYSQL *unused __attribute__((unused))) } #endif +static int test_conc396(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + FILE *fp1, *fp2, *fp3; + char cnf_file1[FN_REFLEN + 10]; + char cnf_dir[FN_REFLEN + 10]; + char cnf_file2[FN_REFLEN + 30]; + char cnf_file3[FN_REFLEN + 30]; + char tmp_dir[FN_REFLEN + 1]; + const char *env = getenv("MYSQL_TMP_DIR"); + fp1 = fp2 = fp3 = NULL; + + + if (env) { + strncpy(tmp_dir, env, FN_REFLEN + 1); + } + else { +#ifdef _WIN32 + GetTempPath(FN_REFLEN + 1, tmp_dir); +#else + strncpy(tmp_dir, "/tmp", FN_REFLEN + 1); +#endif + } + + snprintf(cnf_file1, FN_REFLEN + 10, "%s%cfoo.cnf", tmp_dir, FN_LIBCHAR); + snprintf(cnf_dir, FN_REFLEN + 10, "%s%cconf.d", tmp_dir, FN_LIBCHAR); + snprintf(cnf_file2, FN_REFLEN + 30, "%s%cconfig_a.cnf", cnf_dir, FN_LIBCHAR); + snprintf(cnf_file3, FN_REFLEN + 30, "%s%cconfig_b.cnf", cnf_dir, FN_LIBCHAR); + + #ifdef _WIN32 + CreateDirectory(cnf_dir, NULL); + #else + mkdir(cnf_dir, 0777); + #endif + + mysql = mysql_init(NULL); + + fp1 = fopen(cnf_file1, "w"); + FAIL_IF(!fp1, "fopen"); + fprintf(fp1, "[client]\n!includedir %s\n", cnf_dir); + fclose(fp1); + + /* config_b is alphabetically later, so it should take precendence. */ + fp2 = fopen(cnf_file2, "w"); + FAIL_IF(!fp2, "fopen"); + fprintf(fp2, "[client]\ndefault-character-set = latin2\n"); + fclose(fp2); + + fp3 = fopen(cnf_file3, "w"); + FAIL_IF(!fp3, "fopen"); + fprintf(fp3, "[client]\ndefault-character-set = utf8mb3\n"); + fclose(fp3); + + mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, cnf_file1); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0, 1); + + remove(cnf_file1); + remove(cnf_file2); + remove(cnf_file3); + + FAIL_IF(strcmp(mysql_character_set_name(mysql), "utf8mb3"), "expected charset utf8mb3"); + + mysql_close(mysql); + mysql = mysql_init(NULL); + + fp1 = fopen(cnf_file1, "w"); + FAIL_IF(!fp1, "fopen"); + fprintf(fp1, "[client]\n!includedir %s\n", cnf_dir); + fclose(fp1); + + fp2 = fopen(cnf_file2, "w"); + FAIL_IF(!fp2, "fopen"); + fprintf(fp2, "[client]\ndefault-character-set = utf8mb3\n"); + fclose(fp2); + + fp3 = fopen(cnf_file3, "w"); + FAIL_IF(!fp3, "fopen"); + fprintf(fp3, "[client]\ndefault-character-set = latin2\n"); + fclose(fp3); + + mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, cnf_file1); + my_test_connect(mysql, hostname, username, password, + schema, port, socketname, 0, 1); + + remove(cnf_file1); + remove(cnf_file2); + remove(cnf_file3); + + FAIL_IF(strcmp(mysql_character_set_name(mysql), "latin2"), "expected charset latin2"); + + mysql_close(mysql); + + return OK; +} + static int test_conc332(MYSQL *unused __attribute__((unused))) { int rc; @@ -2045,7 +2147,7 @@ static int test_conn_str_1(MYSQL *my __attribute__((unused))) if (!(fp= fopen("./conc274.cnf", "w"))) return FAIL; - sprintf(conn_str, "connection=host=%s;user=%s;password=%s;port=%d;ssl_enforce=1;socket=%s", + sprintf(conn_str, "connection=host=%s;user=%s;password={%s};port=%d;ssl_enforce=1;socket=%s", hostname ? hostname : "localhost", username ? username : "", password ? password : "", ssl_port, socketname ? socketname : ""); @@ -2084,7 +2186,7 @@ static int test_conc365(MYSQL *my __attribute__((unused))) char tmp[1024]; snprintf(tmp, sizeof(tmp) - 1, - "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d;socket=%s;tls_fp=%s", + "host=127.0.0.1:3300,%s;user=%s;password={%s};port=%d;socket=%s;tls_fp=%s", hostname ? hostname : "localhost", username ? username : "", password ? password : "", port, socketname ? socketname : "", fingerprint[0] ? fingerprint : ""); @@ -2140,12 +2242,12 @@ static int test_conc365_reconnect(MYSQL *my) if (IS_SKYSQL(hostname)) { snprintf(tmp, sizeof(tmp) - 1, - "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d;socket=%s;ssl_enforce=1;tls_fp=%s", + "host=127.0.0.1:3300,%s;user=%s;password={%s};port=%d;socket=%s;ssl_enforce=1;tls_fp=%s", hostname ? hostname : "localhost", username ? username : "", password ? password : "", ssl_port, socketname ? socketname : "", fingerprint[0] ? fingerprint : ""); } else { snprintf(tmp, sizeof(tmp) - 1, - "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d;socket=%s;tls_fp=%s", + "host=127.0.0.1:3300,%s;user=%s;password={%s};port=%d;socket=%s;tls_fp=%s", hostname ? hostname : "localhost", username ? username : "", password ? password : "", port, socketname ? socketname : "", fingerprint[0] ? fingerprint :""); } @@ -2432,6 +2534,7 @@ struct my_tests_st my_tests[] = { {"test_conc327", test_conc327, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc317", test_conc317, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, #endif + {"test_conc396", test_conc396, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc315", test_conc315, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_expired_pw", test_expired_pw, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc276", test_conc276, TEST_CONNECTION_NONE, 0, NULL, NULL},