diff options
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | src/common/compat.c | 17 | ||||
-rw-r--r-- | src/common/compat.h | 3 | ||||
-rw-r--r-- | src/common/util.c | 52 |
4 files changed, 61 insertions, 13 deletions
diff --git a/configure.in b/configure.in index 475c99739..935a1a8fc 100644 --- a/configure.in +++ b/configure.in @@ -143,7 +143,7 @@ dnl These headers are not essential AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h syslog.h sys/time.h sys/resource.h) -AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit setrlimit strlcat strlcpy strtoull) +AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit setrlimit strlcat strlcpy strtoull getpwnam) AC_CHECK_MEMBERS([struct timeval.tv_sec]) diff --git a/src/common/compat.c b/src/common/compat.c index ce99db1c1..395194b37 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -390,6 +390,23 @@ int switch_id(char *user, char *group) { return -1; } +#ifdef HAVE_PWD_H +/** Allocate and return a string containing the home directory for the + * user <b>username</b>. Only works on posix-like systems */ +char * +get_user_homedir(const char *username) +{ + struct passwd *pw; + tor_assert(username); + + if (!(pw = getpwnam(username))) { + log_fn(LOG_ERR,"User '%s' not found.", username); + return NULL; + } + return tor_strdup(pw->pw_dir); +} +#endif + /** Set *addr to the IP address (in dotted-quad notation) stored in c. * Return 1 on success, 0 if c is badly formatted. (Like inet_aton(c,addr), * but works on Windows and Solaris.) diff --git a/src/common/compat.h b/src/common/compat.h index a3b136ab6..32dc7e172 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -164,6 +164,9 @@ void set_uint32(char *cp, uint32_t v); int set_max_file_descriptors(unsigned int required_min); int switch_id(char *user, char *group); +#ifdef HAVE_PWD_H +char *get_user_homedir(const char *username); +#endif int spawn_func(int (*func)(void *), void *data); void spawn_exit(void); diff --git a/src/common/util.c b/src/common/util.c index fbb92c0a4..5d6b08cf9 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -958,22 +958,50 @@ parse_line_from_str(char *line, char **key_out, char **value_out) char *expand_filename(const char *filename) { tor_assert(filename); - /* XXXX Should eventually check for ~username/ */ - if (!strncmp(filename,"~/",2)) { + if (*filename == '~') { size_t len; - const char *home = getenv("HOME"); - char *result; - if (!home) { - log_fn(LOG_WARN, "Couldn't find $HOME environment variable while expanding %s", filename); - return NULL; + char *home, *result; + const char *rest; + + if (filename[1] == '/' || filename[1] == '\0') { + home = getenv("HOME"); + if (!home) { + log_fn(LOG_WARN, "Couldn't find $HOME environment variable while expanding %s", filename); + return NULL; + } + home = tor_strdup(home); + rest = strlen(filename)>=2?(filename+2):NULL; + } else { +#ifdef HAVE_PWD_H + char *username, *slash; + slash = strchr(filename, '/'); + if (slash) + username = tor_strndup(filename+1,slash-filename-1); + else + username = tor_strdup(filename+1); + if (!(home = get_user_homedir(username))) { + log_fn(LOG_WARN,"Couldn't get homedir for %s",username); + tor_free(username); + return NULL; + } + tor_free(username); + rest = slash ? (slash+1) : NULL; +#else + log_fn(LOG_WARN, "Couldn't expend homedir on system without pwd.h"); + return tor_strdup(filename); +#endif + } + tor_assert(home); + /* Remove trailing slash. */ + if (strlen(home)>1 && !strcmpend(home,"/")) { + home[strlen(home)-1] = '\0'; } - /* minus two characters for ~/, plus one for /, plus one for NUL. + /* Plus one for /, plus one for NUL. * Round up to 16 in case we can't do math. */ - len = strlen(home)+strlen(filename)+16; + len = strlen(home)+strlen(rest)+16; result = tor_malloc(len); - tor_snprintf(result,len,"%s%s%s",home, - (!strcmpend(home, "/")) ? "" : "/", - filename+2); + tor_snprintf(result,len,"%s/%s",home,rest?rest:""); + tor_free(home); return result; } else { return tor_strdup(filename); |