//
//	MiscRegistration.m -- a class to sit underneath a Registration panel
//		Written by Don Yacktman (c) 1994 by Don Yacktman.
//				Version 1.0.  All rights reserved.
//
//		This notice may not be removed from this source code.
//
//	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>

#define _notRegistered [strings valueForStringKey:"NotRegistered"]
#define _unreg [strings valueForStringKey:"Unreg"]
#define _reg [strings valueForStringKey:"Reg"]
#define _OK [strings valueForStringKey:"OK"]
#define _cantReg [strings valueForStringKey:"CantReg"]
#define _badRegNum [strings valueForStringKey:"BadRegNum"]

@implementation MiscRegistration

+ initialize
{
	static NXDefaultsVector MiscRegistrationDefaults = {
		{ MISC_KEY_DEFAULT, MISC_DEFAULT_KEY },
		{ NULL }
	};
	NXRegisterDefaults([NXApp appName], MiscRegistrationDefaults);
	return self;
}

- init				// make sure that *Panel is nil.
{
	self = [super init];
	_alertOnNoWrite = YES;
	registerPanel = nil;
	// get name of key file (inside app wrapper)
	_keyFileName = [MiscString newWithString:[NXApp appDirectory]];
	[_keyFileName cat:"/"];
	[_keyFileName cat:MISC_KEY_FILE_NAME];
	_key = [[MiscString alloc] init];
	_serialNum = [MiscString newWithString:MISC_UNREG_SERIAL_NUM];
	[self readKey];
	return self;
}

- setController:sender { controller = sender; return self; }

- readKey
{
	if (![self readKeyFromFile]) {
		// if read from file failed, then we need to try the one from
		// the defaults system.
		id keyString = [MiscString newWithString:
				NXGetDefaultValue([NXApp appName], MISC_KEY_DEFAULT)];
		[_key setStringValue:[keyString stringValueAndFree]];
		_alertOnNoWrite = NO;
		[self writeKeyToFile]; // attempt to remember the key
		_alertOnNoWrite = YES;
	}
	[self registered]; // make sure we are up to date on key verification
	// ***** even if not nil returned, may be invalid key...
	return self;
}

- writeKey
{
	[self writeKeyToFile];
	// **** this should only be written if the key is valid and single user,
	// in which case we do NOT write to key file!
	NXWriteDefault([NXApp appName], MISC_KEY_DEFAULT, [_key stringValue]);
	return self;
}

- readKeyFromFile
{
    NXTypedStream *typedStream;
    FILE *testFile;
	char *temp;

	// first check for existence of file
	testFile = fopen([_keyFileName stringValue], "r");
	if (testFile == NULL) {
		if (_notRegistered)
			[_key setStringValue:_notRegistered];
		else [_key setStringValue:"No key."];
		return nil;
	}
    fclose(testFile);
    typedStream = NXOpenTypedStreamForFile([_keyFileName stringValue],
			NX_READONLY);
	NXReadTypes(typedStream, "*", &temp);
	if (temp) {
		[_key setStringValue:temp];
	} else {
		if (_notRegistered) {
			[_key setStringValue:_notRegistered];
		} else { [_key setStringValue:"No key."]; }
		return nil;
	}
    NXCloseTypedStream(typedStream);
    return self;
}

- writeKeyToFile
{
	NXTypedStream *typedStream;
	id chmodString = [MiscString newWithString:"chmod 644 "]; // only owner...
	const char *temp = [_key stringValue];

	typedStream = NXOpenTypedStreamForFile([_keyFileName stringValue],
			NX_WRITEONLY);
	if (!typedStream) {
		// ***** should only put this up for network licenses...
		if (_alertOnNoWrite)
			NXRunAlertPanel("writeKey", _cantReg, NULL, NULL, _OK);
		return nil;
    }
	NXWriteTypes(typedStream, "*", &temp);
	NXCloseTypedStream(typedStream);
	[chmodString concatenate:_keyFileName];
	system([chmodString stringValueAndFree]);
	return self;
}

- registerPanel		// return the registerPanel, load it if needed.
{
	if( !registerPanel) {
		[NXApp loadNibSection:"Register.nib" owner:self withNames:NO];
		if (!strings) { // attempt to load a ".strings" file *****
		}
		if (!strings) { // failing that, use the controller's string table
			strings = [controller strings];
		}
	}
	return registerPanel;
}

- registration:sender
{
	[self registerPanel]; // make sure nib is loaded
	[self fillRegistrationText:regText];
	[[self registerPanel] makeFirstResponder:regNumText];
	if ([_key emptyString])
		[_key setStringValue:_notRegistered];
	[regNumText setStringValue:[_key stringValue]];
	[regNumText selectText:self];
	[NXApp runModalFor:[self registerPanel]];
	return self;
}

- (BOOL)registered
{	// determine if the user has registered.	
	if (![_key emptyString] && [self keyOK]) return YES;
	return NO;
}

- (BOOL)keyOK
{	// override to check the "_key" variable (a MiscString) for OK values...
	// you should set the stringValue of _serialNum to the serial number
	// that corresponds to the key.  (ie, the serial num should be embedded
	// in the key in some way along with everything else.)
	return YES;
	// Why don't I provide a mechanism here?  Because the code would allow
	// any fool to come up with fake keys.  So you have to write your own
	// code for this method for your security.  If you would like me to
	// write you a simple key encoding mechanism, I'd be happy to do so;
	// just contact me:  Don_Yacktman@byu.edu.  I will charge you for the
	// service, but I don't ask much.  (It's based upon how long it takes
	// me to write it, and since I already have some algorithms, I could use
	// a simpler method that's a permutation of what I already have and
	// your cost wouldn't be much more that about $15.  Contact me...)
	// One other note:  A hacker could always come in and disable the
	// keys.  We don't try to circumvent this at all, so you might want
	// to use a more complex integrity method to fool hackers.  Again, I
	// can give suggestions to the truly paranoid, for a fee.  However,
	// unless you're really paranoid, there is no need for this since a
	// hacker will eventually crack whatever scheme you use, and this
	// scheme is sufficient to keep everyone else honest, I believe.
}

- registerApp:sender		// accept registration...or not...
{
	[_key setStringValue:[regNumText stringValue]]; // grab the key they typed
	[self writeKey];
	[self fillRegistrationText:regText];
	[registerPanel orderOut:self];
	[NXApp stopModal];
	if (![self registered])
		NXRunAlertPanel(NULL, _badRegNum, NULL, NULL, NULL);
	return self;
}

- cancelRegistration:sender		// cancel registration modal loop...
{
	[registerPanel orderOut:self];
	[NXApp stopModal];
	return self;
}

- fillRegistrationText:textField
{	// sets up a text field with the registration number...
	if ([self registered]) {
		const char *regFormat = _reg;
		id tempStr;
		if (!regFormat) regFormat = "Copy #%s is registered.";
		tempStr = [[MiscString alloc] initFromFormat:regFormat,
				[_serialNum stringValue]];
		[[textField setStringValue:[tempStr stringValueAndFree]]
				setTextGray:NX_DKGRAY];
	} else {
		[[textField setStringValue:_unreg] setTextGray:NX_WHITE];
	}
	return self;
}

- (const char *)serialNumber { return [_serialNum stringValue]; }
- (const char *)registrationKey { return [_key stringValue]; };

@end
