
We call path token expansion functions with a NULL context in CCAPI (lib/krb5/acache.c). We only need the context for errors.
726 lines
20 KiB
C
726 lines
20 KiB
C
|
|
/***********************************************************************
|
|
* Copyright (c) 2009-2020, Secure Endpoints Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
**********************************************************************/
|
|
|
|
#include "baselocl.h"
|
|
|
|
#include <stdarg.h>
|
|
|
|
typedef int PTYPE;
|
|
|
|
#ifdef _WIN32
|
|
#include <shlobj.h>
|
|
#include <sddl.h>
|
|
|
|
/*
|
|
* Expand a %{TEMP} token
|
|
*
|
|
* The %{TEMP} token expands to the temporary path for the current
|
|
* user as returned by GetTempPath().
|
|
*
|
|
* @note: Since the GetTempPath() function relies on the TMP or TEMP
|
|
* environment variables, this function will failover to the system
|
|
* temporary directory until the user profile is loaded. In addition,
|
|
* the returned path may or may not exist.
|
|
*/
|
|
static heim_error_code
|
|
expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
TCHAR tpath[MAX_PATH];
|
|
size_t len;
|
|
|
|
if (!GetTempPath(sizeof(tpath)/sizeof(tpath[0]), tpath)) {
|
|
heim_set_error_message(context, EINVAL,
|
|
"Failed to get temporary path (GLE=%d)",
|
|
GetLastError());
|
|
return EINVAL;
|
|
}
|
|
|
|
len = strlen(tpath);
|
|
|
|
if (len > 0 && tpath[len - 1] == '\\')
|
|
tpath[len - 1] = '\0';
|
|
|
|
*ret = strdup(tpath);
|
|
|
|
if (*ret == NULL)
|
|
return heim_enomem(context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
|
|
|
/*
|
|
* Expand a %{BINDIR} token
|
|
*
|
|
* This is also used to expand a few other tokens on Windows, since
|
|
* most of the executable binaries end up in the same directory. The
|
|
* "bin" directory is considered to be the directory in which the
|
|
* containing DLL is located.
|
|
*/
|
|
static heim_error_code
|
|
expand_bin_dir(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
TCHAR path[MAX_PATH];
|
|
TCHAR *lastSlash;
|
|
DWORD nc;
|
|
|
|
nc = GetModuleFileName((HINSTANCE)&__ImageBase, path,
|
|
sizeof(path)/sizeof(path[0]));
|
|
if (nc == 0 ||
|
|
nc == sizeof(path)/sizeof(path[0])) {
|
|
return EINVAL;
|
|
}
|
|
|
|
lastSlash = strrchr(path, '\\');
|
|
if (lastSlash != NULL) {
|
|
TCHAR *fslash = strrchr(lastSlash, '/');
|
|
|
|
if (fslash != NULL)
|
|
lastSlash = fslash;
|
|
|
|
*lastSlash = '\0';
|
|
}
|
|
|
|
if (postfix) {
|
|
if (strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
|
|
return EINVAL;
|
|
}
|
|
|
|
*ret = strdup(path);
|
|
if (*ret == NULL)
|
|
return heim_enomem(context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Expand a %{USERID} token
|
|
*
|
|
* The %{USERID} token expands to the string representation of the
|
|
* user's SID. The user account that will be used is the account
|
|
* corresponding to the current thread's security token. This means
|
|
* that:
|
|
*
|
|
* - If the current thread token has the anonymous impersonation
|
|
* level, the call will fail.
|
|
*
|
|
* - If the current thread is impersonating a token at
|
|
* SecurityIdentification level the call will fail.
|
|
*
|
|
*/
|
|
static heim_error_code
|
|
expand_userid(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
int rv = EINVAL;
|
|
HANDLE hThread = NULL;
|
|
HANDLE hToken = NULL;
|
|
PTOKEN_OWNER pOwner = NULL;
|
|
DWORD len = 0;
|
|
LPTSTR strSid = NULL;
|
|
|
|
hThread = GetCurrentThread();
|
|
|
|
if (!OpenThreadToken(hThread, TOKEN_QUERY,
|
|
FALSE, /* Open the thread token as the
|
|
current thread user. */
|
|
&hToken)) {
|
|
|
|
DWORD le = GetLastError();
|
|
|
|
if (le == ERROR_NO_TOKEN) {
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
|
|
le = 0;
|
|
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
|
|
le = GetLastError();
|
|
}
|
|
|
|
if (le != 0) {
|
|
heim_set_error_message(context, rv,
|
|
"Can't open thread token (GLE=%d)", le);
|
|
goto _exit;
|
|
}
|
|
}
|
|
|
|
if (!GetTokenInformation(hToken, TokenOwner, NULL, 0, &len)) {
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
heim_set_error_message(context, rv,
|
|
"Unexpected error reading token information (GLE=%d)",
|
|
GetLastError());
|
|
goto _exit;
|
|
}
|
|
|
|
if (len == 0) {
|
|
heim_set_error_message(context, rv,
|
|
"GetTokenInformation() returned truncated buffer");
|
|
goto _exit;
|
|
}
|
|
|
|
pOwner = malloc(len);
|
|
if (pOwner == NULL) {
|
|
heim_set_error_message(context, rv, "Out of memory");
|
|
goto _exit;
|
|
}
|
|
} else {
|
|
heim_set_error_message(context, rv, "GetTokenInformation() returned truncated buffer");
|
|
goto _exit;
|
|
}
|
|
|
|
if (!GetTokenInformation(hToken, TokenOwner, pOwner, len, &len)) {
|
|
heim_set_error_message(context, rv,
|
|
"GetTokenInformation() failed. GLE=%d",
|
|
GetLastError());
|
|
goto _exit;
|
|
}
|
|
|
|
if (!ConvertSidToStringSid(pOwner->Owner, &strSid)) {
|
|
heim_set_error_message(context, rv,
|
|
"Can't convert SID to string. GLE=%d",
|
|
GetLastError());
|
|
goto _exit;
|
|
}
|
|
|
|
*ret = strdup(strSid);
|
|
if (*ret == NULL)
|
|
heim_set_error_message(context, rv, "Out of memory");
|
|
|
|
rv = 0;
|
|
|
|
_exit:
|
|
if (hToken != NULL)
|
|
CloseHandle(hToken);
|
|
|
|
if (pOwner != NULL)
|
|
free (pOwner);
|
|
|
|
if (strSid != NULL)
|
|
LocalFree(strSid);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Expand a folder identified by a CSIDL
|
|
*/
|
|
|
|
static heim_error_code
|
|
expand_csidl(heim_context context, PTYPE folder, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
TCHAR path[MAX_PATH];
|
|
size_t len;
|
|
|
|
if (SHGetFolderPath(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path) != S_OK) {
|
|
heim_set_error_message(context, EINVAL, "Unable to determine folder path");
|
|
return EINVAL;
|
|
}
|
|
|
|
len = strlen(path);
|
|
|
|
if (len > 0 && path[len - 1] == '\\')
|
|
path[len - 1] = '\0';
|
|
|
|
if (postfix &&
|
|
strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
|
|
return heim_enomem(context);
|
|
|
|
*ret = strdup(path);
|
|
if (*ret == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
static heim_error_code
|
|
expand_path(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
*ret = strdup(postfix);
|
|
if (*ret == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
|
|
static heim_error_code
|
|
expand_temp_folder(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
const char *p = NULL;
|
|
|
|
p = secure_getenv("TEMP");
|
|
|
|
if (p)
|
|
*ret = strdup(p);
|
|
else
|
|
*ret = strdup("/tmp");
|
|
if (*ret == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
|
|
static heim_error_code
|
|
expand_userid(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **str)
|
|
{
|
|
int ret = asprintf(str, "%ld", (unsigned long)getuid());
|
|
if (ret < 0 || *str == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
|
|
static heim_error_code
|
|
expand_euid(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **str)
|
|
{
|
|
int ret = asprintf(str, "%ld", (unsigned long)geteuid());
|
|
if (ret < 0 || *str == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
#endif /* _WIN32 */
|
|
|
|
static heim_error_code
|
|
expand_home(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **str)
|
|
{
|
|
char homedir[MAX_PATH];
|
|
int ret;
|
|
|
|
if (roken_get_homedir(homedir, sizeof(homedir)))
|
|
ret = asprintf(str, "%s", homedir);
|
|
else
|
|
ret = asprintf(str, "/unknown");
|
|
if (ret < 0 || *str == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
|
|
static heim_error_code
|
|
expand_username(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **str)
|
|
{
|
|
char user[128];
|
|
const char *username = roken_get_username(user, sizeof(user));
|
|
|
|
if (username == NULL) {
|
|
heim_set_error_message(context, ENOTTY,
|
|
N_("unable to figure out current principal",
|
|
""));
|
|
return ENOTTY; /* XXX */
|
|
}
|
|
|
|
*str = strdup(username);
|
|
if (*str == NULL)
|
|
return heim_enomem(context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static heim_error_code
|
|
expand_loginname(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **str)
|
|
{
|
|
char user[128];
|
|
const char *username = roken_get_loginname(user, sizeof(user));
|
|
|
|
if (username == NULL) {
|
|
heim_set_error_message(context, ENOTTY,
|
|
N_("unable to figure out current principal",
|
|
""));
|
|
return ENOTTY; /* XXX */
|
|
}
|
|
|
|
*str = strdup(username);
|
|
if (*str == NULL)
|
|
return heim_enomem(context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static heim_error_code
|
|
expand_strftime(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
size_t len;
|
|
time_t t;
|
|
char buf[1024];
|
|
|
|
t = time(NULL);
|
|
len = strftime(buf, sizeof(buf), arg, localtime(&t));
|
|
if (len == 0 || len >= sizeof(buf))
|
|
return heim_enomem(context);
|
|
*ret = strdup(buf);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Expand an extra token
|
|
*/
|
|
|
|
static heim_error_code
|
|
expand_extra_token(heim_context context, const char *value, char **ret)
|
|
{
|
|
*ret = strdup(value);
|
|
if (*ret == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Expand a %{null} token
|
|
*
|
|
* The expansion of a %{null} token is always the empty string.
|
|
*/
|
|
|
|
static heim_error_code
|
|
expand_null(heim_context context, PTYPE param, const char *postfix,
|
|
const char *arg, char **ret)
|
|
{
|
|
*ret = strdup("");
|
|
if (*ret == NULL)
|
|
return heim_enomem(context);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const struct {
|
|
const char * tok;
|
|
int ftype;
|
|
#define FTYPE_CSIDL 0
|
|
#define FTYPE_SPECIAL 1
|
|
|
|
PTYPE param;
|
|
const char * postfix;
|
|
|
|
int (*exp_func)(heim_context, PTYPE, const char *, const char *, char **);
|
|
|
|
#define SPECIALP(f, P) FTYPE_SPECIAL, 0, P, f
|
|
#define SPECIAL(f) SPECIALP(f, NULL)
|
|
|
|
} tokens[] = {
|
|
#ifdef _WIN32
|
|
#define CSIDLP(C,P) FTYPE_CSIDL, C, P, expand_csidl
|
|
#define CSIDL(C) CSIDLP(C, NULL)
|
|
|
|
{"APPDATA", CSIDL(CSIDL_APPDATA)}, /* Roaming application data (for current user) */
|
|
{"COMMON_APPDATA", CSIDL(CSIDL_COMMON_APPDATA)}, /* Application data (all users) */
|
|
{"LOCAL_APPDATA", CSIDL(CSIDL_LOCAL_APPDATA)}, /* Local application data (for current user) */
|
|
{"SYSTEM", CSIDL(CSIDL_SYSTEM)}, /* Windows System folder (e.g. %WINDIR%\System32) */
|
|
{"WINDOWS", CSIDL(CSIDL_WINDOWS)}, /* Windows folder */
|
|
{"USERCONFIG", CSIDLP(CSIDL_APPDATA, "\\" PACKAGE)}, /* Per user Heimdal configuration file path */
|
|
{"COMMONCONFIG", CSIDLP(CSIDL_COMMON_APPDATA, "\\" PACKAGE)}, /* Common Heimdal configuration file path */
|
|
{"LIBDIR", SPECIAL(expand_bin_dir)},
|
|
{"BINDIR", SPECIAL(expand_bin_dir)},
|
|
{"LIBEXEC", SPECIAL(expand_bin_dir)},
|
|
{"SBINDIR", SPECIAL(expand_bin_dir)},
|
|
#else
|
|
{"LOCALSTATEDIR", FTYPE_SPECIAL, 0, LOCALSTATEDIR, expand_path},
|
|
{"LIBDIR", FTYPE_SPECIAL, 0, LIBDIR, expand_path},
|
|
{"BINDIR", FTYPE_SPECIAL, 0, BINDIR, expand_path},
|
|
{"LIBEXEC", FTYPE_SPECIAL, 0, LIBEXECDIR, expand_path},
|
|
{"SBINDIR", FTYPE_SPECIAL, 0, SBINDIR, expand_path},
|
|
{"USERCONFIG", SPECIAL(expand_home)}, /* same as %{HOME} on not-Windows */
|
|
{"euid", SPECIAL(expand_euid)},
|
|
{"ruid", SPECIAL(expand_userid)},
|
|
{"loginname", SPECIAL(expand_loginname)},
|
|
#endif
|
|
{"username", SPECIAL(expand_username)},
|
|
{"TEMP", SPECIAL(expand_temp_folder)},
|
|
{"USERID", SPECIAL(expand_userid)},
|
|
{"uid", SPECIAL(expand_userid)},
|
|
{"null", SPECIAL(expand_null)},
|
|
{"strftime", SPECIAL(expand_strftime)},
|
|
{"HOME", SPECIAL(expand_home)},
|
|
};
|
|
|
|
static heim_error_code
|
|
expand_token(heim_context context,
|
|
const char *token,
|
|
const char *token_end,
|
|
char **extra_tokens,
|
|
char **ret)
|
|
{
|
|
heim_error_code errcode;
|
|
size_t i;
|
|
char **p;
|
|
const char *colon;
|
|
|
|
*ret = NULL;
|
|
|
|
if (token[0] != '%' || token[1] != '{' || token_end[0] != '}' ||
|
|
token_end - token <= 2) {
|
|
heim_set_error_message(context, EINVAL,"Invalid token.");
|
|
return EINVAL;
|
|
}
|
|
|
|
for (p = extra_tokens; p && p[0]; p += 2) {
|
|
if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
|
|
return expand_extra_token(context, p[1], ret);
|
|
}
|
|
|
|
for (colon=token+2; colon < token_end; colon++)
|
|
if (*colon == ':')
|
|
break;
|
|
|
|
for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
|
|
if (!strncmp(token+2, tokens[i].tok, (colon - token) - 2)) {
|
|
char *arg = NULL;
|
|
|
|
errcode = 0;
|
|
if (*colon == ':') {
|
|
int asprintf_ret = asprintf(&arg, "%.*s",
|
|
(int)(token_end - colon - 1),
|
|
colon + 1);
|
|
if (asprintf_ret < 0 || !arg)
|
|
errcode = ENOMEM;
|
|
}
|
|
if (!errcode)
|
|
errcode = tokens[i].exp_func(context, tokens[i].param,
|
|
tokens[i].postfix, arg, ret);
|
|
free(arg);
|
|
return errcode;
|
|
}
|
|
|
|
heim_set_error_message(context, EINVAL, "Invalid token.");
|
|
return EINVAL;
|
|
}
|
|
|
|
/**
|
|
* Internal function to expand tokens in paths.
|
|
*
|
|
* Params:
|
|
*
|
|
* @context A heim_context
|
|
* @path_in The path to expand tokens from
|
|
* @filepath True if this is a filesystem path (converts slashes to
|
|
* backslashes on Windows)
|
|
* @ppath_out The expanded path
|
|
* @... Variable number of pairs of strings, the first of each
|
|
* being a token (e.g., "luser") and the second a string to
|
|
* replace it with. The list is terminated by a NULL.
|
|
*/
|
|
heim_error_code
|
|
heim_expand_path_tokens(heim_context context,
|
|
const char *path_in,
|
|
int filepath,
|
|
char **ppath_out,
|
|
...)
|
|
{
|
|
heim_error_code ret;
|
|
va_list ap;
|
|
|
|
va_start(ap, ppath_out);
|
|
ret = heim_expand_path_tokensv(context, path_in, filepath, ppath_out, ap);
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
free_extra_tokens(char **extra_tokens)
|
|
{
|
|
char **p;
|
|
|
|
for (p = extra_tokens; p && *p; p++)
|
|
free(*p);
|
|
free(extra_tokens);
|
|
}
|
|
|
|
/**
|
|
* Internal function to expand tokens in paths.
|
|
*
|
|
* Inputs:
|
|
*
|
|
* @context A heim_context
|
|
* @path_in The path to expand tokens from
|
|
* @filepath True if this is a filesystem path (converts slashes to
|
|
* backslashes on Windows)
|
|
* @ppath_out The expanded path
|
|
* @ap A NULL-terminated va_list of pairs of strings, the first of each
|
|
* being a token (e.g., "luser") and the second a string to replace
|
|
* it with.
|
|
*
|
|
* Outputs:
|
|
*
|
|
* @ppath_out Path with expanded tokens (caller must free() this)
|
|
*/
|
|
heim_error_code
|
|
heim_expand_path_tokensv(heim_context context,
|
|
const char *path_in,
|
|
int filepath,
|
|
char **ppath_out, va_list ap)
|
|
{
|
|
char *tok_begin, *tok_end, *append;
|
|
char **extra_tokens = NULL;
|
|
const char *path_left;
|
|
size_t nargs = 0;
|
|
size_t len = 0;
|
|
va_list ap2;
|
|
|
|
if (path_in == NULL || *path_in == '\0') {
|
|
*ppath_out = strdup("");
|
|
return 0;
|
|
}
|
|
|
|
*ppath_out = NULL;
|
|
|
|
#if defined(_MSC_VER)
|
|
ap2 = ap; /* Come on! See SO #558223 */
|
|
#else
|
|
va_copy(ap2, ap);
|
|
#endif
|
|
while (va_arg(ap2, const char *)) {
|
|
nargs++;
|
|
va_arg(ap2, const char *);
|
|
}
|
|
va_end(ap2);
|
|
nargs *= 2;
|
|
|
|
/* Get extra tokens */
|
|
if (nargs) {
|
|
size_t i;
|
|
|
|
extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
|
|
if (extra_tokens == NULL)
|
|
return heim_enomem(context);
|
|
for (i = 0; i < nargs; i++) {
|
|
const char *s = va_arg(ap, const char *); /* token key */
|
|
if (s == NULL)
|
|
break;
|
|
extra_tokens[i] = strdup(s);
|
|
if (extra_tokens[i++] == NULL) {
|
|
free_extra_tokens(extra_tokens);
|
|
return heim_enomem(context);
|
|
}
|
|
s = va_arg(ap, const char *); /* token value */
|
|
if (s == NULL)
|
|
s = "";
|
|
extra_tokens[i] = strdup(s);
|
|
if (extra_tokens[i] == NULL) {
|
|
free_extra_tokens(extra_tokens);
|
|
return heim_enomem(context);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (path_left = path_in; path_left && *path_left; ) {
|
|
|
|
tok_begin = strstr(path_left, "%{");
|
|
|
|
if (tok_begin && tok_begin != path_left) {
|
|
|
|
append = malloc((tok_begin - path_left) + 1);
|
|
if (append) {
|
|
memcpy(append, path_left, tok_begin - path_left);
|
|
append[tok_begin - path_left] = '\0';
|
|
}
|
|
path_left = tok_begin;
|
|
|
|
} else if (tok_begin) {
|
|
|
|
tok_end = strchr(tok_begin, '}');
|
|
if (tok_end == NULL) {
|
|
free_extra_tokens(extra_tokens);
|
|
if (*ppath_out)
|
|
free(*ppath_out);
|
|
*ppath_out = NULL;
|
|
heim_set_error_message(context, EINVAL, "variable missing }");
|
|
return EINVAL;
|
|
}
|
|
|
|
if (expand_token(context, tok_begin, tok_end, extra_tokens,
|
|
&append)) {
|
|
free_extra_tokens(extra_tokens);
|
|
if (*ppath_out)
|
|
free(*ppath_out);
|
|
*ppath_out = NULL;
|
|
return EINVAL;
|
|
}
|
|
|
|
path_left = tok_end + 1;
|
|
} else {
|
|
|
|
append = strdup(path_left);
|
|
path_left = NULL;
|
|
|
|
}
|
|
|
|
if (append == NULL) {
|
|
|
|
free_extra_tokens(extra_tokens);
|
|
if (*ppath_out)
|
|
free(*ppath_out);
|
|
*ppath_out = NULL;
|
|
return heim_enomem(context);
|
|
|
|
}
|
|
|
|
{
|
|
size_t append_len = strlen(append);
|
|
char * new_str = realloc(*ppath_out, len + append_len + 1);
|
|
|
|
if (new_str == NULL) {
|
|
free_extra_tokens(extra_tokens);
|
|
free(append);
|
|
if (*ppath_out)
|
|
free(*ppath_out);
|
|
*ppath_out = NULL;
|
|
return heim_enomem(context);
|
|
}
|
|
|
|
*ppath_out = new_str;
|
|
memcpy(*ppath_out + len, append, append_len + 1);
|
|
len = len + append_len;
|
|
free(append);
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
/* Also deal with slashes */
|
|
if (filepath && *ppath_out) {
|
|
char * c;
|
|
|
|
for (c = *ppath_out; *c; c++)
|
|
if (*c == '/')
|
|
*c = '\\';
|
|
}
|
|
#endif
|
|
|
|
free_extra_tokens(extra_tokens);
|
|
return 0;
|
|
}
|