/* Implementation of login-related functions for GNUstep Copyright (C) 1996 Free Software Foundation, Inc. Written by: Andrew Kachites McCallum Created: May 1996 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for getenv() #if !defined(__WIN32__) #include // for getlogin() #endif #if HAVE_PWD_H #include // for getpwnam() #endif #include #include static NSString *theUserName = nil; void GSSetUserName(NSString* name) { if (theUserName == nil) { theUserName = RETAIN(name); } else if ([theUserName isEqualToString: name] == NO) { ASSIGN(theUserName, name); [NSUserDefaults resetUserDefaults]; } } /* * Return the caller's login name as an NSString object. * The 'LOGNAME' environment variable is our primary source, but we use * other system-dependent sources if LOGNAME is not set. */ NSString * NSUserName(void) { if (theUserName == nil) { const char *login_name = 0; #if defined(__WIN32__) /* The GetUserName function returns the current user name */ char buf[1024]; DWORD n = 1024; if (GetEnvironmentVariable("LOGNAME", buf, 1024)) login_name = buf; else if (GetUserName(buf, &n)) login_name = buf; #else login_name = getenv("LOGNAME"); #if HAVE_GETPWNAM /* * Check that LOGNAME contained legal name. */ if (login_name != 0 && getpwnam(login_name) == 0) { login_name = 0; } #endif /* HAVE_GETPWNAM */ #if HAVE_GETLOGIN /* * Try getlogin() if LOGNAME environmentm variable didn't work. */ if (login_name == 0) { login_name = getlogin(); } #endif /* HAVE_GETLOGIN */ #if HAVE_GETPWUID /* * Try getting the name of the effective user as a last resort. */ if (login_name == 0) { #if HAVE_GETEUID int uid = geteuid(); #else int uid = getuid(); #endif /* HAVE_GETEUID */ struct passwd *pwent = getpwuid (uid); login_name = pwent->pw_name; } #endif /* HAVE_GETPWUID */ #endif if (login_name) GSSetUserName([NSString stringWithCString: login_name]); else [NSException raise: NSInternalInconsistencyException format: @"Unable to determine curren user name"]; } return theUserName; } /* Return the caller's home directory as an NSString object. */ NSString * NSHomeDirectory(void) { return NSHomeDirectoryForUser (NSUserName ()); } /* Return LOGIN_NAME's home directory as an NSString object. */ NSString * NSHomeDirectoryForUser(NSString *login_name) { #if !defined(__MINGW__) struct passwd *pw; [gnustep_global_lock lock]; pw = getpwnam ([login_name cString]); [gnustep_global_lock unlock]; return [NSString stringWithCString: pw->pw_dir]; #else /* Then environment variable HOMEPATH holds the home directory for the user on Windows NT; Win95 has no concept of home. */ char buf[1024], *nb; DWORD n; NSString *s; [gnustep_global_lock lock]; n = GetEnvironmentVariable("HOMEPATH", buf, 1024); if (n > 1024) { /* Buffer not big enough, so dynamically allocate it */ nb = (char *)NSZoneMalloc(NSDefaultMallocZone(), sizeof(char)*(n+1)); n = GetEnvironmentVariable("HOMEPATH", nb, n+1); nb[n] = '\0'; s = [NSString stringWithCString: nb]; NSZoneFree(NSDefaultMallocZone(), nb); } else if (n > 0) { /* null terminate it and return the string */ buf[n] = '\0'; s = [NSString stringWithCString: buf]; } else { s = nil; } if (s != nil) { n = GetEnvironmentVariable("HOMEDRIVE", buf, 1024); buf[n] = '\0'; s = [[NSString stringWithCString: buf] stringByAppendingString: s]; } [gnustep_global_lock unlock]; return s; #endif } NSString * NSFullUserName(void) { #if HAVE_PWD_H struct passwd *pw; pw = getpwnam([NSUserName() cString]); return [NSString stringWithCString: pw->pw_gecos]; #else NSLog(@"Warning: NSFullUserName not implemented\n"); return NSUserName(); #endif } NSArray * GSStandardPathPrefixes(void) { NSDictionary *env; NSString *prefixes; NSArray *prefixArray; env = [[NSProcessInfo processInfo] environment]; prefixes = [env objectForKey: @"GNUSTEP_PATHPREFIX_LIST"]; if (prefixes != 0) { #if defined(__WIN32__) prefixArray = [prefixes componentsSeparatedByString: @";"]; #else prefixArray = [prefixes componentsSeparatedByString: @":"]; #endif } else { NSString *strings[4]; NSString *str; unsigned count = 0; str = [env objectForKey: @"GNUSTEP_USER_ROOT"]; if (str != nil) strings[count++] = str; str = [env objectForKey: @"GNUSTEP_LOCAL_ROOT"]; if (str != nil) strings[count++] = str; str = [env objectForKey: @"GNUSTEP_NETWORK_ROOT"]; if (str != nil) strings[count++] = str; str = [env objectForKey: @"GNUSTEP_SYSTEM_ROOT"]; if (str != nil) strings[count++] = str; if (count) prefixArray = [NSArray arrayWithObjects: strings count: count]; else prefixArray = [NSArray array]; } return prefixArray; } NSArray * NSStandardApplicationPaths(void) { return NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES); } NSArray * NSStandardLibraryPaths(void) { return NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory, NSAllDomainsMask, YES); } NSString * NSTemporaryDirectory(void) { NSFileManager *manager; NSString *tempDirName; NSString *baseTempDirName = nil; NSDictionary *attr; int perm; BOOL flag; #if defined(__WIN32__) char buffer[1024]; if (GetTempPath(1024, buffer)) { baseTempDirName = [NSString stringWithCString: buffer]; } #endif /* * If the user has supplied a directory name in the TEMP or TMP * environment variable, attempt to use that unless we already * have a tem porary directory specified. */ if (baseTempDirName == nil) { NSDictionary *env = [[NSProcessInfo processInfo] environment]; baseTempDirName = [env objectForKey: @"TEMP"]; if (baseTempDirName == nil) { baseTempDirName = [env objectForKey: @"TMP"]; if (baseTempDirName == nil) { #if defined(__WIN32__) baseTempDirName = @"C:\\"; #else baseTempDirName = @"/tmp"; #endif } } } /* * Check that the base directory exists ... if it doesn't we can't * go any further. */ tempDirName = baseTempDirName; manager = [NSFileManager defaultManager]; if ([manager fileExistsAtPath: tempDirName isDirectory: &flag] == NO || flag == NO) { NSLog(@"Temporary directory (%@) does not seem to exist", tempDirName); return nil; } /* * Check that the directory owner (presumably us) has access to it, * and nobody else. If other people have access, try to create a * secure subdirectory. */ attr = [manager fileAttributesAtPath: tempDirName traverseLink: YES]; perm = [[attr objectForKey: NSFilePosixPermissions] intValue]; perm = perm & 0777; if (perm != 0700 && perm != 0600) { /* NSLog(@"Temporary directory (%@) may be insecure ... attempting to " @"add secure subdirectory", tempDirName); */ tempDirName = [baseTempDirName stringByAppendingPathComponent: NSUserName()]; if ([manager fileExistsAtPath: tempDirName] == NO) { NSNumber *p = [NSNumber numberWithInt: 0700]; attr = [NSDictionary dictionaryWithObject: p forKey: NSFilePosixPermissions]; if ([manager createDirectoryAtPath: tempDirName attributes: attr] == NO) { tempDirName = baseTempDirName; NSLog(@"Temporary directory (%@) may be insecure", tempDirName); } } } if ([manager isWritableFileAtPath: tempDirName] == NO) { NSLog(@"Temporary directory (%@) is not writable", tempDirName); return nil; } return tempDirName; } NSString * NSOpenStepRootDirectory(void) { NSString *root = [[[NSProcessInfo processInfo] environment] objectForKey: @"GNUSTEP_ROOT"]; if (root == nil) #if defined(__MINGW__) root = @"C:\\"; #else root = @"/"; #endif return root; } NSArray * NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directoryKey, NSSearchPathDomainMask domainMask, BOOL expandTilde) { /* We read these four only once */ static NSString *gnustep_user_root = nil; /* GNUSTEP_USER_ROOT */ static NSString *gnustep_local_root = nil; /* GNUSTEP_LOCAL_ROOT */ static NSString *gnustep_network_root = nil; /* GNUSTEP_NETWORK_ROOT */ static NSString *gnustep_system_root = nil; /* GNUSTEP_SYSTEM_ROOT */ NSFileManager *fm; NSString *adminDir = @"Administrator"; NSString *appsDir = @"Apps"; NSString *demosDir = @"Demos"; NSString *devDir = @"Developer"; NSString *libraryDir = @"Library"; NSString *libsDir = @"Libraries"; NSString *docDir = @"Documentation"; NSMutableArray *paths = [NSMutableArray new]; NSString *path; unsigned i, count; if (gnustep_system_root == nil) { NS_DURING { NSDictionary *env; [gnustep_global_lock lock]; /* Double-Locking Pattern */ if (gnustep_system_root == nil) { env = [[NSProcessInfo processInfo] environment]; /* Any of the following might be nil */ gnustep_system_root = [env objectForKey: @"GNUSTEP_SYSTEM_ROOT"]; TEST_RETAIN (gnustep_system_root); if (gnustep_system_root == nil) { /* This is pretty important as we need it to load character sets, language settings and similar resources. Use fprintf to avoid recursive calls. */ fprintf (stderr, "Warning - GNUSTEP_SYSTEM_ROOT is not set " "- using /usr/GNUstep/System as a default\n"); gnustep_system_root = @"/usr/GNUstep/System"; } gnustep_local_root = [env objectForKey: @"GNUSTEP_LOCAL_ROOT"]; TEST_RETAIN (gnustep_local_root); gnustep_network_root = [env objectForKey: @"GNUSTEP_NETWORK_ROOT"]; TEST_RETAIN (gnustep_network_root); gnustep_user_root = [env objectForKey: @"GNUSTEP_USER_ROOT"]; TEST_RETAIN (gnustep_user_root); } [gnustep_global_lock unlock]; } NS_HANDLER { // unlock then re-raise the exception [gnustep_global_lock unlock]; [localException raise]; } NS_ENDHANDLER } /* The order in which we return paths is important - user must come first, followed by local, followed by network, followed by system. The calling code can then loop on the returned paths, and stop as soon as it finds something. So things in user automatically override things in system etc. */ /* FIXME - The following code will not respect this order for NSAllApplicationsDirectory. This should be fixed I think. */ #define ADD_PATH(mask, base_dir, add_dir) \ if (domainMask & mask) \ { \ path = [base_dir stringByAppendingPathComponent: add_dir]; \ if (path != nil) \ [paths addObject: path]; \ } if (directoryKey == NSApplicationDirectory || directoryKey == NSAllApplicationsDirectory) { ADD_PATH (NSUserDomainMask, gnustep_user_root, appsDir); ADD_PATH (NSLocalDomainMask, gnustep_local_root, appsDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, appsDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, appsDir); } if (directoryKey == NSDemoApplicationDirectory || directoryKey == NSAllApplicationsDirectory); { NSString *devDemosDir = [devDir stringByAppendingPathComponent: demosDir]; ADD_PATH (NSSystemDomainMask, gnustep_system_root, devDemosDir); } if (directoryKey == NSDeveloperApplicationDirectory || directoryKey == NSAllApplicationsDirectory) { NSString *devAppsDir = [devDir stringByAppendingPathComponent: appsDir]; /* FIXME - why not NSUserDomainMask ? */ ADD_PATH (NSLocalDomainMask, gnustep_local_root, devAppsDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, devAppsDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, devAppsDir); } if (directoryKey == NSAdminApplicationDirectory || directoryKey == NSAllApplicationsDirectory) { NSString *devAdminDir = [devDir stringByAppendingPathComponent: adminDir]; /* FIXME - NSUserDomainMask ? - users have no Administrator directory */ ADD_PATH (NSLocalDomainMask, gnustep_local_root, devAdminDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, devAdminDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, devAdminDir); } if (directoryKey == NSLibraryDirectory || directoryKey == NSAllLibrariesDirectory) { ADD_PATH (NSUserDomainMask, gnustep_user_root, libraryDir); ADD_PATH (NSLocalDomainMask, gnustep_local_root, libraryDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, libraryDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, libraryDir); } if (directoryKey == NSDeveloperDirectory) { /* FIXME - why not NSUserDomainMask ? */ ADD_PATH (NSLocalDomainMask, gnustep_local_root, devDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, devDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, devDir); } if (directoryKey == NSUserDirectory) { if (domainMask & NSUserDomainMask) { path = [NSHomeDirectory() stringByAppendingPathComponent: @"GNUstep"]; [paths addObject: path]; } } if (directoryKey == NSDocumentationDirectory) { ADD_PATH (NSUserDomainMask, gnustep_user_root, docDir); ADD_PATH (NSLocalDomainMask, gnustep_local_root, docDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, docDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, docDir); } if (directoryKey == GSLibrariesDirectory) { ADD_PATH (NSUserDomainMask, gnustep_user_root, libsDir); ADD_PATH (NSLocalDomainMask, gnustep_local_root, libsDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, libsDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, libsDir); } #undef ADD_PATH fm = [NSFileManager defaultManager]; count = [paths count]; for (i = 0; i < count; i++) { path = [paths objectAtIndex: i]; // remove bad paths if ([fm fileExistsAtPath: path] == NO) { [paths removeObjectAtIndex: i]; i--; count--; } /* * this may look like a performance hit at first glance, but if these * string methods don't alter the string, they return the receiver */ else if (expandTilde == YES) { [paths replaceObjectAtIndex: i withObject: [path stringByExpandingTildeInPath]]; } else { [paths replaceObjectAtIndex: i withObject: [path stringByAbbreviatingWithTildeInPath]]; } } AUTORELEASE (paths); return paths; }