//
//	ExtendedApp.m -- a class to make extra information available via the
//		application object.  You no longer need to link against ni_s.
//		Written by Don Yacktman (c) 1993, 1994 by Don Yacktman.
//				Version 1.2  All rights reserved.
//
//		This notice may not be removed from this source code.
//
//		This is a free object!  Contact the author for the latest version.
//		Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604
//		e-mail:  Don_Yacktman@byu.edu
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

#import <misckit/misckit.h>
#import <mach/mach_error.h>
#import <strings.h>
#import <bsd/pwd.h>		// for getpwuid
#import <libc.h>		// for chdir, getwd, getuid
#include <sys/types.h>
#include <bsd/grp.h>

int MiscOSVersion() { // looks at kernel version string to get OS sub version
	// we could return 3.x as a float, but I am assuming version 3 or greater
	// since really the only info I need is if 3.1 additions are available
	// or not.  (GameKit needs to know which SoundKit stuff it can use...)
	kern_return_t       ret;
	kernel_version_t    string;
	id versString, temp;

	ret=host_kernel_version(host_self(), string);
	if (ret != KERN_SUCCESS) {
	    mach_error("host_kernel_version() call failed", ret);
		return(-1);
	}
	// use a MiscString to parse it; this is probably not the best way
	// to handle this since it expects the 3rd word to be "3.x:"
	// (but x can be any size, at least...)  the dependency is that
	// the version must be the 3rd word, and the last char should
	// be tossed.
	temp = [MiscString newWithString:string];
	versString = [temp wordNum:3]; [temp free];
	[versString removeFrom:([versString length] - 1) length:1];
	temp = [versString midFrom:2 length:([versString length] - 2)];
	ret = atoi([temp stringValue]); [temp free];
	return ret;
}

@implementation ExtendedApp

// There's nothing left; it's all in a category now.

@end

@implementation Application(MiscAppExtensions)

- (const char *)appDirectory
{
	return [(NXBundle *)[NXBundle mainBundle] directory];
}

- (int)osVersion { return MiscOSVersion(); }
- (int)userIDNum { return getuid(); }
- (int)groupIDNum { return getgid(); }
- (int)effectiveUserIDNum { return geteuid(); }
- (int)effectiveGroupIDNum { return getegid(); }
- (unsigned long int)hostID { return gethostid(); }
- (NXAtom)userHomeDirectory { return NXHomeDirectory(); }
- (NXAtom)userLoginName { return NXUniqueString(NXUserName()); }
- (NXAtom)userRealName { return [self realNameFor:[self userLoginName]]; }

- (NXAtom)realHostName {
	char realHostName[MAXHOSTNAMELEN];	// host running the application
	gethostname(realHostName, MAXHOSTNAMELEN);
	return NXUniqueString(realHostName);
}

// Attempt to get the real name corresponding to user id.
// Thanks to shayman@objectario.com (Steve Hayman) for recommending
// using this over a NetInfo search.  This is much simpler...and
// I had forgotten that this function actually does the right thing
// for NetInfo, no NetInfo, YP, etc!  Much better solution...
- (NXAtom)realNameFor:(NXAtom)userId
{
	struct passwd *user = getpwnam(userId); // get the passwd file entry
	if (!user) return userId; // fall back on uid if error
	return NXUniqueString(user->pw_gecos);	// unique full name and return it
}

// The next two methods were donated by John_Karabaic@NeXT.COM (John Karabaic)

// returns YES or NO depending on whether the person running the app
// is a member of the group specified in the message

- (BOOL) groupMemberForID:(gid_t)anID
{
	int numReturned, c;
	int groupArray[NGROUPS];
	
	numReturned = getgroups(NGROUPS, groupArray);	
	for (c=0; c < numReturned; c++) {
		if (groupArray[c] == anID) return YES;
	}	
	return NO;
}

- (BOOL) groupMemberForString:(const char *)aString
{
	char localString[MAXNAMLEN+1];
	struct group *aGroup;
	
	if (localString && *localString) {
		strncpy(localString, aString, MAXNAMLEN);
		aGroup = getgrnam(localString);
		return [self groupMemberForID:(aGroup->gr_gid)];
	}
	return NO;
}

@end
