blob: 23fefd6e15f2d19ed5475153afd7b175ba8bd0d2 [file] [log] [blame]
/*
* Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved.
* Copyright (c) 2009 Grant Erickson <gerickson@nuovations.com>. All rights reserved.
*
* This source code is a modified version of the CoreFoundation sources released by Apple Inc. under
* the terms of the APSL version 2.0 (see below).
*
* For information about changes from the original Apple source release can be found by reviewing the
* source control system for the project at https://sourceforge.net/svn/?group_id=246198.
*
* The original license information is as follows:
*
* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/* CFPlatform.c
Copyright 1999-2002, Apple, Inc. All rights reserved.
Responsibility: Christopher Kane
*/
#include "CFInternal.h"
#include "CFPriv.h"
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#if DEPLOYMENT_TARGET_WINDOWS
/* In typical fragile fashion, order of include on Windows is important */
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <windows.h>
#include <tchar.h>
#ifndef _WIN32_IE
#define _WIN32_IE 0x0600
#endif
#define strdup _strdup
#include <shlobj.h>
extern size_t strlcpy(char *dst, const char *src, size_t siz);
extern size_t strlcat(char *dst, const char *src, size_t siz);
#ifndef SHGFP_TYPE_CURRENT
#define SHGFP_TYPE_CURRENT 0
#endif
#else
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <stdio.h>
#endif
#if DEPLOYMENT_TARGET_MACOSX
#include <mach-o/dyld.h>
#include <crt_externs.h>
#endif
#if DEPLOYMENT_TARGET_MACOSX
#define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
#else
#define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
#endif
static CFStringRef _CFUserName(void);
#if DEPLOYMENT_TARGET_MACOSX
// CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these
char **_CFArgv(void) { return *_NSGetArgv(); }
int _CFArgc(void) { return *_NSGetArgc(); }
#endif
__private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) {
#if DEPLOYMENT_TARGET_WINDOWS || 0
DWORD len = GetCurrentDirectoryA(maxlen, path);
return ((0 != len) && (maxlen > 0) && (len + 1 <= (DWORD)maxlen));
#else
return getcwd(path, maxlen) != NULL;
#endif
}
static Boolean __CFIsCFM = false;
// If called super early, we just return false
__private_extern__ Boolean _CFIsCFM(void) {
return __CFIsCFM;
}
#if DEPLOYMENT_TARGET_WINDOWS || 0
#define PATH_SEP '\\'
#else
#define PATH_SEP '/'
#endif
#if DEPLOYMENT_TARGET_WINDOWS
#define PATH_LIST_SEP ';'
#else
#define PATH_LIST_SEP ':'
#endif
static char *_CFSearchForNameInPath(const char *name, char *path) {
struct stat statbuf;
#if DEPLOYMENT_TARGET_WINDOWS && !defined(__GNUC__)
char nname[MAX_PATH + 1];
#else
char nname[strlen(name) + strlen(path) + 2];
#endif
#if !DEPLOYMENT_TARGET_WINDOWS
int no_hang_fd = open("/dev/autofs_nowait", 0);
#endif
for (;;) {
char *p = (char *)strchr(path, PATH_LIST_SEP);
if (NULL != p) {
*p = '\0';
}
nname[0] = '\0';
strlcat(nname, path, sizeof(nname));
strlcat(nname, "/", sizeof(nname));
strlcat(nname, name, sizeof(nname));
// Could also do access(us, X_OK) == 0 in next condition,
// for executable-only searching
if (0 == stat(nname, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFREG) {
if (p != NULL) {
*p = PATH_LIST_SEP;
}
#if !DEPLOYMENT_TARGET_WINDOWS
close(no_hang_fd);
#endif
return strdup(nname);
}
if (NULL == p) {
break;
}
*p = PATH_LIST_SEP;
path = p + 1;
}
#if !DEPLOYMENT_TARGET_WINDOWS
close(no_hang_fd);
#endif
return NULL;
}
#if DEPLOYMENT_TARGET_WINDOWS
// Returns the path to the CF DLL, which we can then use to find resources like char sets
__private_extern__ const char* _CFDLLPath(void) {
static char cachedPath[MAX_PATH+1] = "";
if ('\0' == cachedPath[0]) {
#if defined(DEBUG)
char* DLLFileName = "CoreFoundation_debug";
#elif defined(PROFILE)
char* DLLFileName = "CoreFoundation_profile";
#else
char* DLLFileName = "CoreFoundation";
#endif
HMODULE ourModule = GetModuleHandleA(DLLFileName);
CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed");
DWORD wResult = GetModuleFileNameA(ourModule, cachedPath, MAX_PATH+1);
CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError());
CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", cachedPath);
// strip off last component, the DLL name
CFIndex idx;
for (idx = wResult - 1; idx; idx--) {
if ('\\' == cachedPath[idx]) {
cachedPath[idx] = '\0';
break;
}
}
}
return cachedPath;
}
#endif
static const char *__CFProcessPath = NULL;
static const char *__CFprogname = NULL;
const char **_CFGetProgname(void) {
if (!__CFprogname)
_CFProcessPath(); // sets up __CFprogname as a side-effect
return &__CFprogname;
}
const char **_CFGetProcessPath(void) {
if (!__CFProcessPath)
_CFProcessPath(); // sets up __CFProcessPath as a side-effect
return &__CFProcessPath;
}
const char *_CFProcessPath(void) {
if (__CFProcessPath) return __CFProcessPath;
char *thePath = NULL;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX
if (!_CFIsSetUgid()) {
#else
if (!__CFProcessPath) {
#endif
thePath = getenv("CFProcessPath");
if (thePath) {
int len = (int)strlen(thePath);
__CFProcessPath = (const char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, len+1, 0);
if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)");
memmove((char *)__CFProcessPath, thePath, len + 1);
}
}
#if DEPLOYMENT_TARGET_WINDOWS
if (!__CFProcessPath) {
char buf[CFMaxPathSize] = {0};
HINSTANCE hinst = GetModuleHandle(NULL);
DWORD rlen = hinst ? GetModuleFileNameA(hinst, buf, CFMaxPathSize) : 0;
thePath = rlen ? buf : NULL;
#elif DEPLOYMENT_TARGET_MACOSX
int execIndex = 0;
if (!__CFProcessPath && NULL != (*_NSGetArgv())[execIndex]) {
int no_hang_fd = open("/dev/autofs_nowait", 0);
char buf[CFMaxPathSize] = {0};
struct stat statbuf;
const char *arg0 = (*_NSGetArgv())[execIndex];
if (arg0[0] == '/') {
// We've got an absolute path; look no further;
thePath = (char *)arg0;
} else {
char *theList = getenv("PATH");
if (NULL != theList && NULL == strrchr(arg0, '/')) {
thePath = _CFSearchForNameInPath(arg0, theList);
if (thePath) {
// User could have "." or "../bin" or other relative path in $PATH
if (('/' != thePath[0]) && _CFGetCurrentDirectory(buf, CFMaxPathSize)) {
strlcat(buf, "/", sizeof(buf));
strlcat(buf, thePath, sizeof(buf));
if (0 == stat(buf, &statbuf)) {
free(thePath);
thePath = buf;
}
}
if (thePath != buf) {
strlcpy(buf, thePath, sizeof(buf));
free((void *)thePath);
thePath = buf;
}
}
}
}
// After attempting a search through $PATH, if existant,
// try prepending the current directory to argv[0].
if (!thePath && _CFGetCurrentDirectory(buf, CFMaxPathSize)) {
if (buf[strlen(buf)-1] != '/') {
strlcat(buf, "/", sizeof(buf));
}
strlcat(buf, arg0, CFMaxPathSize);
if (0 == stat(buf, &statbuf)) {
thePath = buf;
}
}
if (thePath) {
// We are going to process the buffer replacing all "/./" and "//" with "/"
CFIndex srcIndex = 0, dstIndex = 0;
CFIndex len = strlen(thePath);
for (srcIndex=0; srcIndex<len; srcIndex++) {
thePath[dstIndex] = thePath[srcIndex];
dstIndex++;
while (srcIndex < len-1 && thePath[srcIndex] == '/' && (thePath[srcIndex+1] == '/' || (thePath[srcIndex+1] == '.' && srcIndex < len-2 && thePath[srcIndex+2] == '/'))) srcIndex += (thePath[srcIndex+1] == '/' ? 1 : 2);
}
thePath[dstIndex] = 0;
}
if (!thePath) {
thePath = (*_NSGetArgv())[execIndex];
}
#elif DEPLOYMENT_TARGET_LINUX
if (!__CFProcessPath) {
pid_t pid = getpid();
char buf[2][CFMaxPathSize] = {{0}, {0}};
int n;
n = snprintf(buf[0], CFMaxPathSize, "/proc/%u/exe", pid);
if (n > 0) {
n = readlink(buf[0], buf[1], CFMaxPathLength);
if (n > 0 && n <= CFMaxPathLength) {
buf[1][n] = '\0';
thePath = buf[1];
}
}
#else
#error "Don't know how to compute the process path for this platform."
#endif
if (thePath) {
int len = (int)strlen(thePath);
__CFProcessPath = (const char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, len + 1, 0);
if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)");
memmove((char *)__CFProcessPath, thePath, len + 1);
}
if (__CFProcessPath) {
const char *p = 0;
int i;
for (i = 0; __CFProcessPath[i] != 0; i++){
if (__CFProcessPath[i] == PATH_SEP)
p = __CFProcessPath + i;
}
if (p != 0)
__CFprogname = p + 1;
else
__CFprogname = __CFProcessPath;
}
#if DEPLOYMENT_TARGET_MACOSX
close(no_hang_fd);
#endif
}
if (!__CFProcessPath) {
__CFProcessPath = "";
__CFprogname = __CFProcessPath;
} else {
const char *p = 0;
int i;
for (i = 0; __CFProcessPath[i] != 0; i++){
if (__CFProcessPath[i] == PATH_SEP)
p = __CFProcessPath + i;
}
if (p != 0)
__CFprogname = p + 1;
else
__CFprogname = __CFProcessPath;
}
return __CFProcessPath;
}
__private_extern__ CFStringRef _CFProcessNameString(void) {
static CFStringRef __CFProcessNameString = NULL;
if (!__CFProcessNameString) {
const char *processName = *_CFGetProgname();
if (!processName) processName = "";
__CFProcessNameString = CFStringCreateWithCString(__CFGetDefaultAllocator(), processName, kCFPlatformInterfaceStringEncoding);
}
return __CFProcessNameString;
}
static CFStringRef __CFUserName = NULL;
#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
static CFURLRef __CFHomeDirectory = NULL;
static uint32_t __CFEUID = -1;
static uint32_t __CFUID = -1;
static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd) {
CFURLRef home = NULL;
if (!_CFIsSetUgid()) {
const char *path = getenv("CFFIXED_USER_HOME");
if (path) {
home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)path, strlen(path), true);
}
}
if (!home) {
if (upwd && upwd->pw_dir) {
home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)upwd->pw_dir, strlen(upwd->pw_dir), true);
}
}
return home;
}
static void _CFUpdateUserInfo(void) {
struct passwd *upwd;
__CFEUID = geteuid();
__CFUID = getuid();
if (__CFHomeDirectory) CFRelease(__CFHomeDirectory);
__CFHomeDirectory = NULL;
if (__CFUserName) CFRelease(__CFUserName);
__CFUserName = NULL;
upwd = getpwuid(__CFEUID ? __CFEUID : __CFUID);
__CFHomeDirectory = _CFCopyHomeDirURLForUser(upwd);
if (!__CFHomeDirectory) {
const char *cpath = getenv("HOME");
if (cpath) {
__CFHomeDirectory = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)cpath, strlen(cpath), true);
}
}
// This implies that UserManager stores directory info in CString
// rather than FileSystemRep. Perhaps this is wrong & we should
// expect NeXTSTEP encodings. A great test of our localized system would
// be to have a user "O-umlat z e r". XXX
if (upwd && upwd->pw_name) {
__CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, upwd->pw_name, kCFPlatformInterfaceStringEncoding);
} else {
const char *cuser = getenv("USER");
if (cuser)
__CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cuser, kCFPlatformInterfaceStringEncoding);
}
}
#endif
static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) {
#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
if (!uName) {
if (geteuid() != __CFEUID || getuid() != __CFUID || !__CFHomeDirectory)
_CFUpdateUserInfo();
if (__CFHomeDirectory) CFRetain(__CFHomeDirectory);
return __CFHomeDirectory;
} else {
struct passwd *upwd = NULL;
char buf[128], *user;
SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding);
CFIndex usedSize;
if (size < 127) {
user = buf;
} else {
user = (char*)CFAllocatorAllocate(kCFAllocatorSystemDefault, size+1, 0);
if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)");
}
if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, (uint8_t *)user, size, &usedSize) == len) {
user[usedSize] = '\0';
upwd = getpwnam(user);
}
if (buf != user) {
CFAllocatorDeallocate(kCFAllocatorSystemDefault, user);
}
return _CFCopyHomeDirURLForUser(upwd);
}
#elif DEPLOYMENT_TARGET_WINDOWS
CFStringRef user = !uName ? _CFUserName() : uName;
CFURLRef home = NULL;
if (!uName || CFEqual(user, _CFUserName())) {
const char *cpath = getenv("HOMEPATH");
const char *cdrive = getenv("HOMEDRIVE");
if (cdrive && cpath) {
char fullPath[CFMaxPathSize];
CFStringRef str;
strlcpy(fullPath, cdrive, sizeof(fullPath));
strlcat(fullPath, cpath, sizeof(fullPath));
str = CFStringCreateWithCString(kCFAllocatorSystemDefault, fullPath, kCFPlatformInterfaceStringEncoding);
home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
CFRelease(str);
}
}
if (home == NULL) {
UniChar pathChars[MAX_PATH];
if (S_OK == SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, (LPWSTR) pathChars)) {
UniChar* p = pathChars;
CFIndex len = 0;
CFStringRef str;
while (*p++ != 0)
++len;
str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathChars, len);
home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
CFRelease(str);
} else {
// We have to get "some" directory location, so fall-back to the
// processes current directory.
UniChar currDir[MAX_PATH];
DWORD dwChars = GetCurrentDirectory(MAX_PATH + 1, (LPWSTR)currDir);
if (dwChars > 0) {
UniChar* p = currDir;
CFIndex len = 0;
CFStringRef str;
while (*p++ != 0)
++len;
str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, currDir, len);
home = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
}
}
}
// We could do more here (as in KB Article Q101507). If that article is to
// be believed, we should only run into this case on Win95, or through
// user error.
if (home) {
CFStringRef str = CFURLCopyFileSystemPath(home, kCFURLWindowsPathStyle);
if (str && CFStringGetLength(str) == 0) {
CFRelease(home);
home=NULL;
}
if (str) CFRelease(str);
}
return home;
#else
#error Dont know how to compute users home directories on this platform
#endif
}
static CFStringRef _CFUserName(void) {
#if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
if (geteuid() != __CFEUID || getuid() != __CFUID)
_CFUpdateUserInfo();
#elif DEPLOYMENT_TARGET_WINDOWS
if (!__CFUserName) {
char username[1040];
DWORD size = 1040;
username[0] = 0;
if (GetUserNameA(username, &size)) {
__CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, username, kCFPlatformInterfaceStringEncoding);
} else {
const char *cname = getenv("USERNAME");
if (cname)
__CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cname, kCFPlatformInterfaceStringEncoding);
}
}
#else
#error Dont know how to compute user name on this platform
#endif
if (!__CFUserName)
__CFUserName = (CFStringRef)CFRetain(CFSTR(""));
return __CFUserName;
}
__private_extern__ CFStringRef _CFGetUserName(void) {
return CFStringCreateCopy(kCFAllocatorSystemDefault, _CFUserName());
}
#define CFMaxHostNameLength 256
#define CFMaxHostNameSize (CFMaxHostNameLength+1)
__private_extern__ CFStringRef _CFStringCreateHostName(void) {
char myName[CFMaxHostNameSize];
// return @"" instead of nil a la CFUserName() and Ali Ozer
if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0';
return CFStringCreateWithCString(kCFAllocatorSystemDefault, myName, kCFPlatformInterfaceStringEncoding);
}
/* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
These can return NULL.
*/
CF_EXPORT CFStringRef CFGetUserName(void) {
return _CFUserName();
}
CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) {
return _CFCreateHomeDirectoryURLForUser(uName);
}
#undef CFMaxHostNameLength
#undef CFMaxHostNameSize