]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. | |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
22 | * | |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* CFPlatform.c | |
26 | Copyright 1999-2002, Apple, Inc. All rights reserved. | |
27 | Responsibility: Christopher Kane | |
28 | */ | |
29 | ||
30 | #include "CFInternal.h" | |
31 | #include "CFPriv.h" | |
32 | #if defined(__WIN32__) | |
33 | #include <sys/stat.h> | |
34 | #include <string.h> | |
35 | #include <windows.h> | |
36 | #include <stdlib.h> | |
37 | #else | |
38 | #include <sys/stat.h> | |
39 | #include <string.h> | |
40 | #include <unistd.h> | |
41 | #include <pwd.h> | |
42 | #endif | |
43 | #if defined(__MACH__) | |
44 | #include <mach-o/dyld.h> | |
45 | #include <crt_externs.h> | |
46 | #endif | |
47 | ||
48 | extern char *getenv(const char *name); | |
49 | ||
50 | #if defined(__MACH__) | |
51 | #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8 | |
52 | #else | |
53 | #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding() | |
54 | #endif | |
55 | ||
56 | char **_CFArgv(void) { | |
57 | #if defined(__MACH__) | |
58 | return *_NSGetArgv(); | |
59 | #else | |
60 | return NULL; | |
61 | #endif | |
62 | } | |
63 | ||
64 | int _CFArgc(void) { | |
65 | #if defined(__MACH__) | |
66 | return *_NSGetArgc(); | |
67 | #else | |
68 | return 0; | |
69 | #endif | |
70 | } | |
71 | ||
72 | ||
73 | __private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) { | |
74 | #if defined(__WIN32__) | |
75 | DWORD len = GetCurrentDirectoryA(maxlen, path); | |
76 | return (0 != len && len + 1 <= maxlen); | |
77 | #else | |
78 | return getcwd(path, maxlen) != NULL; | |
79 | #endif | |
80 | } | |
81 | ||
82 | static Boolean __CFIsCFM = false; | |
83 | ||
84 | // If called super early, we just return false | |
85 | __private_extern__ Boolean _CFIsCFM(void) { | |
86 | return __CFIsCFM; | |
87 | } | |
88 | ||
89 | ||
90 | #if defined(__WIN32__) | |
91 | #define PATH_LIST_SEP ';' | |
92 | #else | |
93 | #define PATH_LIST_SEP ':' | |
94 | #endif | |
95 | ||
96 | static char *_CFSearchForNameInPath(CFAllocatorRef alloc, const char *name, char *path) { | |
97 | struct stat statbuf; | |
98 | char *nname = CFAllocatorAllocate(alloc, strlen(name) + strlen(path) + 2, 0); | |
99 | if (__CFOASafe) __CFSetLastAllocationEventName(nname, "CFUtilities (temp)"); | |
100 | for (;;) { | |
101 | char *p = (char *)strchr(path, PATH_LIST_SEP); | |
102 | if (NULL != p) { | |
103 | *p = '\0'; | |
104 | } | |
105 | nname[0] = '\0'; | |
106 | strcat(nname, path); | |
107 | strcat(nname, "/"); | |
108 | strcat(nname, name); | |
109 | // Could also do access(us, X_OK) == 0 in next condition, | |
110 | // for executable-only searching | |
111 | if (0 == stat(nname, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFREG) { | |
112 | if (p != NULL) { | |
113 | *p = PATH_LIST_SEP; | |
114 | } | |
115 | return nname; | |
116 | } | |
117 | if (NULL == p) { | |
118 | break; | |
119 | } | |
120 | *p = PATH_LIST_SEP; | |
121 | path = p + 1; | |
122 | } | |
123 | CFAllocatorDeallocate(alloc, nname); | |
124 | return NULL; | |
125 | } | |
126 | ||
127 | ||
128 | ||
129 | static const char *__CFProcessPath = NULL; | |
130 | static const char *__CFprogname = ""; | |
131 | ||
132 | const char **_CFGetProgname(void) { // This is a hack around the broken _NSGetPrognam(), for now; will be removed | |
133 | return &__CFprogname; | |
134 | } | |
135 | ||
136 | const char *_CFProcessPath(void) { | |
137 | CFAllocatorRef alloc = NULL; | |
138 | char *thePath = NULL; | |
139 | int execIndex = 0; | |
140 | ||
141 | if (__CFProcessPath) return __CFProcessPath; | |
142 | if (!__CFProcessPath) { | |
143 | thePath = getenv("CFProcessPath"); | |
144 | ||
145 | alloc = CFRetain(__CFGetDefaultAllocator()); | |
146 | ||
147 | if (thePath) { | |
148 | int len = strlen(thePath); | |
149 | __CFProcessPath = CFAllocatorAllocate(alloc, len+1, 0); | |
150 | if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)"); | |
151 | memmove((char *)__CFProcessPath, thePath, len + 1); | |
152 | } | |
153 | } | |
154 | ||
155 | #if defined(__MACH__) | |
156 | { | |
157 | struct stat exec, lcfm; | |
158 | unsigned long size = CFMaxPathSize; | |
159 | char buffer[CFMaxPathSize]; | |
160 | if (0 == _NSGetExecutablePath(buffer, &size) && | |
161 | strcasestr(buffer, "LaunchCFMApp") != NULL && | |
162 | 0 == stat("/System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp", &lcfm) && | |
163 | 0 == stat(buffer, &exec) && | |
164 | (lcfm.st_dev == exec.st_dev) && | |
165 | (lcfm.st_ino == exec.st_ino)) { | |
166 | // Executable is LaunchCFMApp, take special action | |
167 | execIndex = 1; | |
168 | __CFIsCFM = true; | |
169 | } | |
170 | } | |
171 | #endif | |
172 | ||
173 | if (!__CFProcessPath && NULL != (*_NSGetArgv())[execIndex]) { | |
174 | char buf[CFMaxPathSize] = {0}; | |
175 | #if defined(__WIN32__) | |
176 | HINSTANCE hinst = GetModuleHandle(NULL); | |
177 | DWORD rlen = hinst ? GetModuleFileName(hinst, buf, 1028) : 0; | |
178 | thePath = rlen ? buf : NULL; | |
179 | #else | |
180 | struct stat statbuf; | |
181 | const char *arg0 = (*_NSGetArgv())[execIndex]; | |
182 | if (arg0[0] == '/') { | |
183 | // We've got an absolute path; look no further; | |
184 | thePath = (char *)arg0; | |
185 | } else { | |
186 | char *theList = getenv("PATH"); | |
187 | if (NULL != theList && NULL == strrchr(arg0, '/')) { | |
188 | thePath = _CFSearchForNameInPath(alloc, arg0, theList); | |
189 | if (thePath) { | |
190 | // User could have "." or "../bin" or other relative path in $PATH | |
191 | if (('/' != thePath[0]) && _CFGetCurrentDirectory(buf, CFMaxPathSize)) { | |
192 | strcat(buf, "/"); | |
193 | strcat(buf, thePath); | |
194 | if (0 == stat(buf, &statbuf)) { | |
195 | CFAllocatorDeallocate(alloc, (void *)thePath); | |
196 | thePath = buf; | |
197 | } | |
198 | } | |
199 | if (thePath != buf) { | |
200 | strcpy(buf, thePath); | |
201 | CFAllocatorDeallocate(alloc, (void *)thePath); | |
202 | thePath = buf; | |
203 | } | |
204 | } | |
205 | } | |
206 | } | |
207 | ||
208 | // After attempting a search through $PATH, if existant, | |
209 | // try prepending the current directory to argv[0]. | |
210 | if (!thePath && _CFGetCurrentDirectory(buf, CFMaxPathSize)) { | |
211 | if (buf[strlen(buf)-1] != '/') { | |
212 | strcat(buf, "/"); | |
213 | } | |
214 | strcat(buf, arg0); | |
215 | if (0 == stat(buf, &statbuf)) { | |
216 | thePath = buf; | |
217 | } | |
218 | } | |
219 | ||
220 | if (thePath) { | |
221 | // We are going to process the buffer replacing all "/./" with "/" | |
222 | CFIndex srcIndex = 0, dstIndex = 0; | |
223 | CFIndex len = strlen(thePath); | |
224 | for (srcIndex=0; srcIndex<len; srcIndex++) { | |
225 | thePath[dstIndex] = thePath[srcIndex]; | |
226 | dstIndex++; | |
227 | if ((srcIndex < len-2) && (thePath[srcIndex] == '/') && (thePath[srcIndex+1] == '.') && (thePath[srcIndex+2] == '/')) { | |
228 | // We are at the first slash of a "/./" Skip the "./" | |
229 | srcIndex+=2; | |
230 | } | |
231 | } | |
232 | thePath[dstIndex] = 0; | |
233 | } | |
234 | #endif | |
235 | if (!thePath) { | |
236 | thePath = (*_NSGetArgv())[execIndex]; | |
237 | } | |
238 | if (thePath) { | |
239 | int len = strlen(thePath); | |
240 | __CFProcessPath = CFAllocatorAllocate(alloc, len + 1, 0); | |
241 | if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)"); | |
242 | memmove((char *)__CFProcessPath, thePath, len + 1); | |
243 | } | |
244 | if (__CFProcessPath) { | |
245 | const char *p = 0; | |
246 | int i; | |
247 | for (i = 0; __CFProcessPath[i] != 0; i++){ | |
248 | if (__CFProcessPath[i] == '/') | |
249 | p = __CFProcessPath + i; | |
250 | } | |
251 | if (p != 0) | |
252 | __CFprogname = p + 1; | |
253 | else | |
254 | __CFprogname = __CFProcessPath; | |
255 | } | |
256 | } | |
257 | if (!__CFProcessPath) { | |
258 | __CFProcessPath = ""; | |
259 | } | |
260 | return __CFProcessPath; | |
261 | } | |
262 | ||
263 | __private_extern__ CFStringRef _CFProcessNameString(void) { | |
264 | static CFStringRef __CFProcessNameString = NULL; | |
265 | if (!__CFProcessNameString) { | |
266 | const char *processName = *_CFGetProgname(); | |
267 | if (!processName) processName = ""; | |
268 | __CFProcessNameString = CFStringCreateWithCString(__CFGetDefaultAllocator(), processName, kCFPlatformInterfaceStringEncoding); | |
269 | } | |
270 | return __CFProcessNameString; | |
271 | } | |
272 | ||
273 | static CFURLRef __CFHomeDirectory = NULL; | |
274 | static CFStringRef __CFUserName = NULL; | |
275 | static uint32_t __CFEUID = -1; | |
276 | static uint32_t __CFUID = -1; | |
277 | ||
278 | #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) | |
279 | static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd) { | |
280 | CFURLRef home = NULL; | |
281 | if (upwd && upwd->pw_dir) { | |
282 | home = CFURLCreateFromFileSystemRepresentation(NULL, upwd->pw_dir, strlen(upwd->pw_dir), true); | |
283 | } | |
284 | return home; | |
285 | } | |
286 | ||
287 | static void _CFUpdateUserInfo(void) { | |
288 | struct passwd *upwd; | |
289 | ||
290 | __CFEUID = geteuid(); | |
291 | __CFUID = getuid(); | |
292 | if (__CFHomeDirectory) CFRelease(__CFHomeDirectory); | |
293 | __CFHomeDirectory = NULL; | |
294 | if (__CFUserName) CFRelease(__CFUserName); | |
295 | __CFUserName = NULL; | |
296 | ||
297 | upwd = getpwuid(__CFEUID ? __CFEUID : __CFUID); | |
298 | __CFHomeDirectory = _CFCopyHomeDirURLForUser(upwd); | |
299 | if (!__CFHomeDirectory) { | |
300 | const char *cpath = getenv("HOME"); | |
301 | if (cpath) { | |
302 | __CFHomeDirectory = CFURLCreateFromFileSystemRepresentation(NULL, cpath, strlen(cpath), true); | |
303 | } | |
304 | } | |
305 | ||
306 | // This implies that UserManager stores directory info in CString | |
307 | // rather than FileSystemRep. Perhaps this is wrong & we should | |
308 | // expect NeXTSTEP encodings. A great test of our localized system would | |
309 | // be to have a user "O-umlat z e r". XXX | |
310 | if (upwd && upwd->pw_name) { | |
311 | __CFUserName = CFStringCreateWithCString(NULL, upwd->pw_name, kCFPlatformInterfaceStringEncoding); | |
312 | } else { | |
313 | const char *cuser = getenv("USER"); | |
314 | if (cuser) | |
315 | __CFUserName = CFStringCreateWithCString(NULL, cuser, kCFPlatformInterfaceStringEncoding); | |
316 | } | |
317 | } | |
318 | #endif | |
319 | ||
320 | static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) { | |
321 | #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) | |
322 | if (!uName) { | |
323 | if (geteuid() != __CFEUID || getuid() != __CFUID || !__CFHomeDirectory) | |
324 | _CFUpdateUserInfo(); | |
325 | if (__CFHomeDirectory) CFRetain(__CFHomeDirectory); | |
326 | return __CFHomeDirectory; | |
327 | } else { | |
328 | struct passwd *upwd = NULL; | |
329 | char buf[128], *user; | |
330 | SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding); | |
331 | CFIndex usedSize; | |
332 | if (size < 127) { | |
333 | user = buf; | |
334 | } else { | |
335 | user = CFAllocatorAllocate(kCFAllocatorDefault, size+1, 0); | |
336 | if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)"); | |
337 | } | |
338 | if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, user, size, &usedSize) == len) { | |
339 | user[usedSize] = '\0'; | |
340 | upwd = getpwnam(user); | |
341 | } | |
342 | if (buf != user) { | |
343 | CFAllocatorDeallocate(kCFAllocatorDefault, user); | |
344 | } | |
345 | return _CFCopyHomeDirURLForUser(upwd); | |
346 | } | |
347 | #elif defined(__WIN32__) | |
348 | #warning CF: Windows home directory goop disabled | |
349 | return NULL; | |
350 | #if 0 | |
351 | CFString *user = !uName ? CFUserName() : uName; | |
352 | ||
353 | if (!uName || CFEqual(user, CFUserName())) { | |
354 | const char *cpath = getenv("HOMEPATH"); | |
355 | const char *cdrive = getenv("HOMEDRIVE"); | |
356 | if (cdrive && cpath) { | |
357 | char fullPath[CFMaxPathSize]; | |
358 | CFStringRef str; | |
359 | strcpy(fullPath, cdrive); | |
360 | strncat(fullPath, cpath, CFMaxPathSize-strlen(cdrive)-1); | |
361 | str = CFStringCreateWithCString(NULL, fullPath, kCFPlatformInterfaceStringEncoding); | |
362 | home = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true); | |
363 | CFRelease(str); | |
364 | } | |
365 | } | |
366 | if (!home) { | |
367 | struct _USER_INFO_2 *userInfo; | |
368 | HINSTANCE hinstDll = GetModuleHandleA("NETAPI32"); | |
369 | if (!hinstDll) | |
370 | hinstDll = LoadLibraryEx("NETAPI32", NULL, 0); | |
371 | if (hinstDll) { | |
372 | FARPROC lpfn = GetProcAddress(hinstDll, "NetUserGetInfo"); | |
373 | if (lpfn) { | |
374 | unsigned namelen = CFStringGetLength(user); | |
375 | UniChar *username; | |
376 | username = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(UniChar) * (namelen + 1), 0); | |
377 | if (__CFOASafe) __CFSetLastAllocationEventName(username, "CFUtilities (temp)"); | |
378 | CFStringGetCharacters(user, CFRangeMake(0, namelen), username); | |
379 | if (!(*lpfn)(NULL, (LPWSTR)username, 2, (LPBYTE *)&userInfo)) { | |
380 | UInt32 len = 0; | |
381 | CFMutableStringRef str; | |
382 | while (userInfo->usri2_home_dir[len] != 0) len ++; | |
383 | str = CFStringCreateMutable(NULL, len+1); | |
384 | CFStringAppendCharacters(str, userInfo->usri2_home_dir, len); | |
385 | home = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true); | |
386 | CFRelease(str); | |
387 | } | |
388 | CFAllocatorDeallocate(kCFAllocatorDefault, username); | |
389 | } | |
390 | } else { | |
391 | } | |
392 | } | |
393 | // We could do more here (as in KB Article Q101507). If that article is to | |
394 | // be believed, we should only run into this case on Win95, or through | |
395 | // user error. | |
396 | if (CFStringGetLength(CFURLGetPath(home)) == 0) { | |
397 | CFRelease(home); | |
398 | home=NULL; | |
399 | } | |
400 | #endif | |
401 | ||
402 | #else | |
403 | #error Dont know how to compute users home directories on this platform | |
404 | #endif | |
405 | } | |
406 | ||
407 | static CFStringRef _CFUserName(void) { | |
408 | #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) | |
409 | if (geteuid() != __CFEUID || getuid() != __CFUID) | |
410 | _CFUpdateUserInfo(); | |
411 | #elif defined(__WIN32__) | |
412 | if (!__CFUserName) { | |
413 | char username[1040]; | |
414 | DWORD size = 1040; | |
415 | username[0] = 0; | |
416 | if (GetUserNameA(username, &size)) { | |
417 | __CFUserName = CFStringCreateWithCString(NULL, username, kCFPlatformInterfaceStringEncoding); | |
418 | } else { | |
419 | const char *cname = getenv("USERNAME"); | |
420 | if (cname) | |
421 | __CFUserName = CFStringCreateWithCString(NULL, cname, kCFPlatformInterfaceStringEncoding); | |
422 | } | |
423 | } | |
424 | #else | |
425 | #error Dont know how to compute user name on this platform | |
426 | #endif | |
427 | if (!__CFUserName) | |
428 | __CFUserName = CFRetain(CFSTR("")); | |
429 | return __CFUserName; | |
430 | } | |
431 | ||
432 | __private_extern__ CFStringRef _CFGetUserName(void) { | |
433 | return CFStringCreateCopy(NULL, _CFUserName()); | |
434 | } | |
435 | ||
436 | #define CFMaxHostNameLength 256 | |
437 | #define CFMaxHostNameSize (CFMaxHostNameLength+1) | |
438 | ||
439 | __private_extern__ CFStringRef _CFStringCreateHostName(void) { | |
440 | char myName[CFMaxHostNameSize]; | |
441 | ||
442 | // return @"" instead of nil a la CFUserName() and Ali Ozer | |
443 | if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0'; | |
444 | return CFStringCreateWithCString(NULL, myName, kCFPlatformInterfaceStringEncoding); | |
445 | } | |
446 | ||
447 | /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday. | |
448 | These can return NULL. | |
449 | */ | |
450 | CF_EXPORT CFStringRef CFGetUserName(void) { | |
451 | return _CFUserName(); | |
452 | } | |
453 | ||
454 | CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) { | |
455 | return _CFCreateHomeDirectoryURLForUser(uName); | |
456 | } | |
457 | ||
458 | #undef PATH_LIST_SEP | |
459 | #undef CFMaxHostNameLength | |
460 | #undef CFMaxHostNameSize | |
461 |