diff --git a/src/fileutil.c b/src/fileutil.c index a182a260..38fadd5d 100644 --- a/src/fileutil.c +++ b/src/fileutil.c @@ -15,6 +15,8 @@ */ #include +#include +#include /* for getpwnam_r() */ #include #include #include @@ -22,22 +24,84 @@ extern char* scenario_path; +int expand_user_path(const char* path, char* expanded_home_path /*The buffer*/, size_t buflen) +{ + if (path[0] != '~') { /* We have nothing to expand here */ + return 1; + } + + memset(expanded_home_path, '\0', buflen); + char* home_dir = NULL; + + if (path[1] == '\0' || path[1] == '/') { /* '~/path' case */ + home_dir = getenv("HOME"); + if (home_dir == NULL) { + home_dir = getenv("USERPROFILE"); + } else { + snprintf(expanded_home_path, buflen - 1, "%s%s", home_dir, path + 1); + } + } else { + const char* first_slash = strchr(path, '/'); /* substring starting from '/' */ + const size_t linux_username_limit = 32; /* As of now */ + char* username = NULL; + if ((first_slash != NULL) && ((first_slash - (path + 1)) <= linux_username_limit)) { /* '~someuser/blah' case */ + username = strndup(path + 1, first_slash - (path + 1)); + } else { /* '~someuser' case, there is no file, just username */ + return -1; + } + + struct passwd pwd; + struct passwd* result; + const size_t bufsize = sysconf(_SC_GETPW_R_SIZE_MAX) +1; + char* buffer = malloc(bufsize * sizeof(char)); + int retcode = getpwnam_r(username, &pwd, buffer, bufsize - 1, &result); + free(username); + free(buffer); + if (result == NULL) { + if (retcode != 0) { + errno = retcode; + } + WARNING_NO("Unable to resolve home path for [%s]\n", path); + return -1; + } else { + home_dir = result->pw_dir; + } + + if (home_dir != NULL) { + if (first_slash != NULL) { /* '~username/path' case */ + snprintf(expanded_home_path, buflen - 1, "%s%s", home_dir, first_slash); + } else { /* '~username' case should be eliminated above, but just in case it is modified in future*/ + return -1; + } + } + } + + return 1; +} + char* find_file(const char* filename) { - char *fullpath; - if (filename[0] == '/' || !*scenario_path) { - return strdup(filename); + char tmppath[MAX_PATH]; + tmppath[0] = '\0'; + const char* filepathptr = tmppath; + if ((expand_user_path(filename, tmppath, sizeof(tmppath)) == -1) || (tmppath[0] == '\0')) { /* we couldn't expand path, or buffer is still empty */ + filepathptr = filename; + } + + if (filepathptr[0] == '/' || !*scenario_path) { + return strdup(filepathptr); } - fullpath = malloc(MAX_PATH); - snprintf(fullpath, MAX_PATH, "%s%s", scenario_path, filename); + size_t len = strlen(scenario_path) + strlen(filepathptr) + 1; + char* fullpath = malloc(len); + snprintf(fullpath, len, "%s%s", scenario_path, filepathptr); if (access(fullpath, R_OK) < 0) { free(fullpath); WARNING("SIPp now prefers looking for pcap/rtpstream files next to the scenario. " "%s couldn't be found next to the scenario, falling back to " - "using the current working directory", filename); - return strdup(filename); + "using the current working directory", filepathptr); + return strdup(filepathptr); } return fullpath;