/* 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 #define lowlevelstringify(X) #X #define stringify(X) lowlevelstringify(X) 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 current user name"]; } return theUserName; } /* Return the caller's home directory as an NSString object. */ NSString * NSHomeDirectory(void) { return NSHomeDirectoryForUser (NSUserName ()); } #if defined(__MINGW__) NSString * GSStringFromWin32EnvironmentVariable(const char * envVar) { char buf[1024], *nb; DWORD n; NSString *s = nil; [gnustep_global_lock lock]; n = GetEnvironmentVariable(envVar, buf, 1024); if (n > 1024) { /* Buffer not big enough, so dynamically allocate it */ nb = (char *)NSZoneMalloc(NSDefaultMallocZone(), sizeof(char)*(n+1)); n = GetEnvironmentVariable(envVar, 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]; } [gnustep_global_lock unlock]; return s; } #endif /* 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. */ NSString *s; [gnustep_global_lock lock]; s = GSStringFromWin32EnvironmentVariable("HOMEPATH"); if (s != nil) { s = [GSStringFromWin32EnvironmentVariable("HOMEDRIVE") 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 } /* 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 */ static void setupPathNames() { #if defined (__MINGW32__) NSString *systemDrive = GSStringFromWin32EnvironmentVariable("SystemDrive"); #endif if (gnustep_system_root == nil) { NS_DURING { NSDictionary *env; [gnustep_global_lock lock]; /* Double-Locking Pattern */ if (gnustep_system_root == nil) { BOOL warned = NO; 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. */ warned = YES; gnustep_system_root = [NSString stringWithCString: stringify(GNUSTEP_INSTALL_PREFIX)]; #if defined (__MINGW32__) gnustep_system_root = [systemDrive stringByAppendingString: gnustep_system_root]; #endif RETAIN(gnustep_system_root); fprintf (stderr, "Warning - GNUSTEP_SYSTEM_ROOT is not set " "- using %s\n", [gnustep_system_root lossyCString]); } gnustep_local_root = [env objectForKey: @"GNUSTEP_LOCAL_ROOT"]; TEST_RETAIN (gnustep_local_root); if (gnustep_local_root == nil) { gnustep_local_root = [NSString stringWithCString: stringify(GNUSTEP_LOCAL_ROOT)]; #if defined (__MINGW32__) gnustep_local_root = [systemDrive stringByAppendingString: gnustep_local_root]; #endif if ([gnustep_local_root length] == 0) gnustep_local_root = nil; else RETAIN(gnustep_local_root); } if (gnustep_local_root == nil) { if ([[gnustep_system_root lastPathComponent] isEqual: @"System"] == YES) { gnustep_local_root = [[gnustep_system_root stringByDeletingLastPathComponent] stringByAppendingPathComponent: @"Local"]; TEST_RETAIN (gnustep_local_root); } else { gnustep_local_root = @"/usr/GNUstep/Local"; } #ifndef NDEBUG if (warned == NO) { warned = YES; fprintf (stderr, "Warning - GNUSTEP_LOCAL_ROOT is not set " "- using %s\n", [gnustep_local_root lossyCString]); } #endif } gnustep_network_root = [env objectForKey: @"GNUSTEP_NETWORK_ROOT"]; TEST_RETAIN (gnustep_network_root); if (gnustep_network_root == nil) { gnustep_network_root = [NSString stringWithCString: stringify(GNUSTEP_NETWORK_ROOT)]; #if defined (__MINGW32__) gnustep_network_root = [systemDrive stringByAppendingString: gnustep_network_root]; #endif if ([gnustep_network_root length] == 0) gnustep_network_root = nil; else RETAIN(gnustep_network_root); } if (gnustep_network_root == nil) { if ([[gnustep_system_root lastPathComponent] isEqual: @"System"] == YES) { gnustep_network_root = [[gnustep_system_root stringByDeletingLastPathComponent] stringByAppendingPathComponent: @"Network"]; TEST_RETAIN (gnustep_network_root); } else { gnustep_network_root = @"/usr/GNUstep/Network"; } #ifndef NDEBUG if (warned == NO) { warned = YES; fprintf (stderr, "Warning - GNUSTEP_NETWORK_ROOT is not set " "- using %s\n", [gnustep_network_root lossyCString]); } #endif } gnustep_user_root = [env objectForKey: @"GNUSTEP_USER_ROOT"]; TEST_RETAIN (gnustep_user_root); if (gnustep_user_root == nil) { gnustep_user_root = [NSHomeDirectory() stringByAppendingPathComponent: @"GNUstep"]; TEST_RETAIN (gnustep_user_root); if (warned == NO) { warned = YES; fprintf (stderr, "Warning - GNUSTEP_USER_ROOT is not set " "- using %s\n", [gnustep_user_root lossyCString]); } } } [gnustep_global_lock unlock]; } NS_HANDLER { // unlock then re-raise the exception [gnustep_global_lock unlock]; [localException raise]; } NS_ENDHANDLER } } /** Returns a string containing the path to the GNUstep system installation directory. This function is gaurenteed to return a non-nil answer (unless something is seriously wrong, in which case the application will probably crash anyway) */ NSString * GSSystemRootDirectory(void) { if (gnustep_system_root == nil) { setupPathNames(); } return gnustep_system_root; } /** Returns an array of strings which contain paths that should be in the standard search order for resources, etc. If the environment variable GNUSTEP_PATHPREFIX_LIST is set. It returns the list of paths set in that variable. Otherwise, it returns the user, local, network, and system paths, in that order This function is gaurenteed to return a non-nil answer (unless something is seriously wrong, in which case the application will probably crash anyway) */ NSArray * GSStandardPathPrefixes(void) { NSDictionary *env; NSString *prefixes; NSArray *prefixArray; env = [[NSProcessInfo processInfo] environment]; prefixes = [env objectForKey: @"GNUSTEP_PATHPREFIX_LIST"]; if (prefixes != nil) { #if defined(__WIN32__) prefixArray = [prefixes componentsSeparatedByString: @";"]; #else prefixArray = [prefixes componentsSeparatedByString: @":"]; #endif if ([prefixArray count] <= 1) { /* This probably means there was some parsing error, but who knows. Play it safe though... */ prefixArray = nil; } } if (prefixes == nil) { NSString *strings[4]; NSString *str; unsigned count = 0; if (gnustep_system_root == nil) { setupPathNames(); } str = gnustep_user_root; if (str != nil) strings[count++] = str; str = gnustep_local_root; if (str != nil) strings[count++] = str; str = gnustep_network_root; if (str != nil) strings[count++] = str; str = 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; 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) { NSFileManager *fm; NSString *adminDir = @"Administrator"; NSString *appsDir = @"Apps"; NSString *demosDir = @"Demos"; NSString *devDir = @"Developer"; NSString *libraryDir = @"Library"; NSString *libsDir = @"Libraries"; NSString *toolsDir = @"Tools"; NSString *docDir = @"Documentation"; NSMutableArray *paths = [NSMutableArray new]; NSString *path; unsigned i; unsigned count; if (gnustep_system_root == nil) { setupPathNames(); } /* * 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]; ADD_PATH (NSUserDomainMask, gnustep_local_root, devAppsDir); 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) { ADD_PATH (NSUserDomainMask, gnustep_local_root, devDir); 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); } if (directoryKey == GSToolsDirectory) { ADD_PATH (NSUserDomainMask, gnustep_user_root, toolsDir); ADD_PATH (NSLocalDomainMask, gnustep_local_root, toolsDir); ADD_PATH (NSNetworkDomainMask, gnustep_network_root, toolsDir); ADD_PATH (NSSystemDomainMask, gnustep_system_root, toolsDir); } #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; }