//*****************************************************************************
//
//	GateDocEditor.m.  
//		
//		Create, edit and link using a Gate doc 
// 
//			by	Felipe A. Rodriguez		
//
//	This code is supplied "as is" the author makes no warranty as to its 
//	suitability for any purpose.  This code is free and may be distributed 
//	in accordance with the terms of the:
//		
//			GNU GENERAL PUBLIC LICENSE
//			Version 2, June 1991
//			copyright (C) 1989, 1991 Free Software Foundation, Inc.
// 			675 Mass Ave, Cambridge, MA 02139, USA
//
//*****************************************************************************

#import <dbkit/DBImageView.h>

#import "GKdefs.h"
#import "GateDocEditor.h"
#import "options.h"
#import "Coordinator.h"
#import "CommandScroll.h"
#import "DLDelegate.h"
#import "OptionsEditor.h"
#import "EParse.h"

				// filter for Gate doc path's
	const char *filter[2] = {"Gate", NULL};




@implementation GateDocEditor

//*****************************************************************************
//
//		create new Gate Document with default files inside.
//
//*****************************************************************************

- newDoc:sender
{
	theSavePanel = [SavePanel new];
	[theSavePanel setTitle:[[NXApp delegate] localString:"New"]];
	[theSavePanel setRequiredFileType:filter[0]]; // append ext if not supplied
	if(NX_OKTAG == [theSavePanel runModalForDirectory:
								NXGetDefaultValue([NXApp appName], LIBPATH) 
								file:NULL])
		{										// create .Gate doc wrapper
    	mkdir([theSavePanel filename], (_S_IRUSR | _S_IWUSR | _S_IXUSR));		
		chown([theSavePanel filename], getuid(), getgid());
		[self addDefaultFiles:[theSavePanel filename]];
//		[[NXApp delegate] updateBrowser:self];
		[self editGateDoc:[theSavePanel filename]];
		}

    return self;
}
//*****************************************************************************
//
//		execute pppd with info from .Gate open document
//
//*****************************************************************************

- linkDoc:sender
{
	if(![[[NXApp mainMenu] findCellWithTag:1] isEnabled])
		{									// is disconnect menu item enabled?
		[[sender window] close];
		[[NXApp delegate] setPhase:0];
		[[[NXApp delegate] commandView] appendString:
						[[NXApp delegate] localString:"Killing ppp daemon\n"]];
		[[NXApp delegate] killDaemon];
	  	[[NXApp delegate]linkWithFile:NXGetDefaultValue([NXApp appName],EDOC)];
		}
	else
		[[NXApp delegate] showAlert:"A ppp session is already active."];

    return self;
}
//*****************************************************************************
//
//		open an existing .Gate document
//
//*****************************************************************************

- openDoc:sender
{
	theOpenPanel = [OpenPanel new];
	[theOpenPanel setTitle:[[NXApp delegate] localString:"Open"]];
	if(NX_OKTAG == [theOpenPanel runModalForDirectory:
								NXGetDefaultValue([NXApp appName], LIBPATH) 
								file:NULL 	
								types:filter])
		[self editGateDoc:[theOpenPanel filename]];

    return self;
}
//*****************************************************************************
//
//		edit an existing .Gate document using built in editor
//
//*****************************************************************************

- editGateDoc:(const char *)aGateDoc
{
id image;
												// save doc path to ddb		
	if(!NXWriteDefault([NXApp appName], EDOC, aGateDoc))
		[[NXApp delegate] showAlert:"ddbWriteError"];
	if([[NXApp delegate] readable:[self pppupPath]])		// is readable 
		{
		if(!theOptionsEditor)					// open edit doc window
			[NXApp loadNibSection:"EditDoc.nib" owner:self withNames:NO];
		[docPath setStringValue:aGateDoc];		// display name
		[docName setStringValue:
					[[[NXApp delegate] docLDelegate] extractName:aGateDoc]];	
		[theOptionsEditor parseOptionsFile:[self optionsPath]];

		strcpy(Path, aGateDoc);								// display doc icon
		strcat(Path, IMAGE);								// in well
		[[GDEImageView setEditable:NO] setStyle:DB_ImageNoFrame];
		if(!(image = [[NXImage alloc] initFromFile:Path]))		// if imge
			image = [NXImage findImageNamed: ".dir"];		// else use blank
		[GDEImageView setImage:image];

//		[[theOptionsEditor editorWindow] center];
//		[[theOptionsEditor editorWindow] makeKeyAndOrderFront:self];
		if(!Parser)								// parse options for login name
			Parser = [[EParse alloc] init];			
///		if(loginName = [self optionsLoginName:[self optionsPath]])
			{
///			[loginNameField setStringValue:loginName];		
///			[self beginEditing];
			}
///		else
///		  [[NXApp delegate] showAlert:"Login name missing from options file."];
[self continueEditing];		/// temp
		}

    return self;
}
//*****************************************************************************
//
//		begin the editing process
//
//*****************************************************************************

- beginEditing;
{
char *passW = NULL;

	if(passW = [self secretOwnedBySelf])
		{										// found entry owned by user
		[passwordField setStringValue:passW];
		[self continueEditing];
		free(passW);
		}
	else			// if a pre-existing entry owned by another the user			
		{			// must supply the old password before editing is allowed
		if(passW = [self secretOwnedByOther])
			{			
			if([self comparePasswords:passW])
				[self continueEditing];
			free(passW);
			}
		else								
			{						// add new entry to secrets file					
			if(NX_ALERTDEFAULT == NXRunAlertPanel(0, [[NXApp delegate] 
			localString:"Login Name not found in secrets file.  Add it?"], 
			[[[NXApp delegate] stringTable] valueForStringKey:"Yes"], 
			[[[NXApp delegate] stringTable] valueForStringKey:"No"], NULL))
				{
				[self addToSecretsFile:
								NXGetDefaultValue([NXApp appName], "secrets")];
				[self continueEditing];
				}
			}
		}

    return self;
}
//*****************************************************************************
//
//		continue the editing process
//
//*****************************************************************************

- continueEditing
{
static char del2[] = {" \"\t\r\n"};
static char del3[] = {"\". =:|\t\r\nT"};
char *lName = NULL;

	[[Parser setKey1:"A"] setKey2:"D"];			// set search pattern  
	[[Parser setDelim1:del3] setDelim2:del2];		// and delimiters
	if(lName = [Parser parseFile:[self pppupPath]])
		{
		[phoneField setStringValue:lName];
		free(lName);
		}
	else
		[[NXApp delegate]showAlert:"Phone number not found in pppup file"];

	[editPanel center];
	[editPanel display];
	[editPanel makeKeyAndOrderFront:self];

    return self;
}
//*****************************************************************************
//
//		add login name and password to selected secrets file
//
//*****************************************************************************

- addToSecretsFile:(const char *)pathOfSecretsFile
{
NXStream *inFStream;
char buf[2] = {" \0"};
char * gLogin;

	if((inFStream = NXMapFile(pathOfSecretsFile, NX_READWRITE)) == NULL)
		{
		strcpy(Path, "Could not open file: ");
		strcat(Path, pathOfSecretsFile);
		NXRunAlertPanel(0, Path, 0, 0, 0);
		}
	else
		{
		NXSeek(inFStream, 0, NX_FROMEND);				// go to end of file

		strncpy(Path, "\"", MAXPATHLEN);				// concat dummy entry
		strncat(Path, [loginNameField stringValue], 
									MAXPATHLEN - strlen(rBuf));
		strncat(Path, "\" * \"", MAXPATHLEN - strlen(rBuf));
		strncat(Path, "myPassword", MAXPATHLEN - strlen(rBuf));
		strncat(Path, "\"", MAXPATHLEN - strlen(rBuf));

		if(gLogin = getlogin())
			strncpy(gBuf, gLogin, MAXPATHLEN);
		else
			sprintf(gBuf, "%d", (int)getuid());
		gLogin = gBuf;
		if(NXRead(inFStream, buf, 1) != 1)	// print to a new line by testing	
			{								// last char in file
			if(buf[0] == '\n' || buf[0] == '\r')		 // insert at cur ln
				NXPrintf(inFStream, "# owner:%s\r\n%s\r\n", gLogin, Path);		
			else										// insert at new ln
				NXPrintf(inFStream,"\r\n# owner:%s\r\n%s\r\n", gLogin, Path);	
			}
		else
			NXRunAlertPanel(0, "Error reading from memory stream.", 0, 0, 0);
		NXFlush(inFStream);
		if(NXSaveToFile(inFStream, pathOfSecretsFile) == -1)
			NXRunAlertPanel(0, "Error saving memory stream.", 0, 0, 0);
		else
			NXCloseMemory(inFStream, NX_FREEBUFFER);
		}

    return self;
}
//*****************************************************************************
//
// 		target action of Gate Doc editor text field editing by user 
//
//*****************************************************************************

- editField:sender 
{
const char *aPath;
static char del2[] = {" \"\t\r\n"};
static char del3[] = {"\". =:|\t\r\nT"};

	switch([sender selectedRow])
		{
		case 0:											// phone number changed
			if([[NXApp delegate] readable:(aPath = [self pppupPath])])
				{
				[[Parser setKey1:"A"] setKey2:"D"];		// set search pattern  
				[[Parser setDelim1:del3] setDelim2:del2];	// and delimiters
				strncpy(rBuf, "ATDT", MAXPATHLEN);
				strncat(rBuf, [[sender findCellWithTag:
							[sender selectedRow]] stringValue], 
											MAXPATHLEN - strlen(rBuf));
				[Parser replaceWith:rBuf];	  
				if(![Parser editFile:aPath])
					NXRunAlertPanel(0, "Error editing file", 0, 0, 0);
				}
			break;
		case 1:											// login name changed
			[self loginNameChanged];
			break;						
		case 2:											// password changed
			if([[NXApp delegate] readable:(aPath = [self optionsPath])])
				[self editSecretsForName:[loginNameField stringValue]];
			break;
		default:
			break;
		} 
 
	return self;
}
//*****************************************************************************
//
//		loginNameChanged
//
//*****************************************************************************

- loginNameChanged
{
char *passW = NULL;

	if(passW = [self secretOwnedBySelf])
		{										// found entry owned by user
		[passwordField setStringValue:passW];
		[self editFilesForName:[loginNameField stringValue]];
		free(passW);
		}
	else			// if a pre-existing entry owned by another, the user			
		{			// must supply the old password before editing is allowed
		if(passW = [self secretOwnedByOther])
			{			
			if([self comparePasswords:passW])
				[self editFilesForName:[loginNameField stringValue]];
			free(passW);
			}
		else		// no pre-existing entry so just change current						
			[self editFilesForName:[self optionsLoginName:[self optionsPath]]];
		}

    return self;
}
//*****************************************************************************
//
//		edit options and secrets
//
//*****************************************************************************

- editFilesForName:(const char *)aLoginName
{
static char del1[] = {"\". =:|\t\r\n"}, del2[] = {" \"\t\r\n"};

	[self editSecretsForName:aLoginName];
									// replace login name in options file
	[[Parser setKey1:"user"] setKey2:""];		  
	[[Parser setDelim1:del1] setDelim2:del2];	
	strncpy(rBuf, "name \"", MAXPATHLEN);
	strncat(rBuf, [loginNameField stringValue], MAXPATHLEN - strlen(rBuf));
	strncat(rBuf, "\"", MAXPATHLEN - strlen(rBuf));
	[Parser replaceWith:rBuf];	  
	if(![Parser editFile:[self optionsPath]])
		NXRunAlertPanel(0, "Error editing file", 0, 0, 0);

    return self;
}
//*****************************************************************************
//
//		edit secrets
//
//*****************************************************************************

- editSecretsForName:(const char *)aLoginName
{							// replace login name, password in secrets file
	[self setParserForOtherOwner:aLoginName];
	[Parser replaceWith:[self concatSecret:[passwordField stringValue]]];	 
	if(![Parser editFile:NXGetDefaultValue([NXApp appName], "secrets")])
		NXRunAlertPanel(0, "Error editing secrets file.", 0, 0, 0);

    return self;
}
//*****************************************************************************
//
//		return concat'd secret
//
//*****************************************************************************

- (const char *)concatSecret:(const char *)aString
{
	strncpy(rBuf, "\"", MAXPATHLEN);
	strncat(rBuf, [loginNameField stringValue], 
								MAXPATHLEN - strlen(rBuf));
	strncat(rBuf, "\" * \"", MAXPATHLEN - strlen(rBuf));
	strncat(rBuf, aString, MAXPATHLEN - strlen(rBuf));
	strncat(rBuf, "\"", MAXPATHLEN - strlen(rBuf));

    return rBuf;
}
//*****************************************************************************
//
//		return YES if new password matches user supplied password 
//
//*****************************************************************************

- (BOOL)comparePasswords:(const char *)newPass
{
BOOL ret = NO;

	if(NX_ALERTDEFAULT == NXRunAlertPanel(0, [[NXApp delegate] localString:
					"An entry owned by another user already exists within the secrets file for the Login Name specified.  Edit the existing entry?"], 
				[[[NXApp delegate] stringTable] valueForStringKey:"Yes"], 
				[[[NXApp delegate] stringTable] valueForStringKey:"No"], NULL))
		{
		[oldPassPanel makeKeyAndOrderFront:self];
					// begin modal loop
		[NXApp beginModalSession:&session for:oldPassPanel];	
		while([NXApp runModalSession:&session] == NX_RUNCONTINUES);	
					// end modal loop
		[NXApp endModalSession:&session];						
		[oldPassPanel orderOut:self];
					// test for correct old password before
					// allowing the user to edit a pre-existing
					// entry which he does not own
		if(strcmp([oldPassField stringValue], newPass) == 0)
			{
			ret = YES;
			[passwordField setStringValue:[oldPassField stringValue]];
			[oldPassField setStringValue:" "];
			free(loginName);					// store current login name
			loginName = NXCopyStringBuffer([loginNameField stringValue]);
			}
		else 		// incorrect password was entered, so revert to last login
			{		// name and post warning
			[[NXApp delegate] showAlert:"Incorrect Password."];
			[loginNameField setStringValue:loginName];		
			}
		}
		
	return ret;
}
//*****************************************************************************
//
//		sets the parser for detection of entries owned by self
//
//*****************************************************************************

- setParserForOwner:(const char *)aString
{
static char del2[] = {" \"\t\r\n"};
char * gLogin;
								// check for an entry in secrets with login 			
								// name which is owned by user 
	strncpy(gBuf, "owner:", MAXPATHLEN);
    if(gLogin = getlogin())
        strncat(gBuf, gLogin, MAXPATHLEN - strlen(gBuf));
	else
		{
		sprintf(rBuf, "%d", (int)getuid());
        strncat(gBuf, rBuf, MAXPATHLEN - strlen(gBuf));
		}
	[[Parser setKey1:gBuf] setKey2:aString];		  
	[[Parser setDelim1:del2] setDelim2:del2];			// and delimiters
	[Parser setEscape:1];								// words to escape

    return self;
}
//*****************************************************************************
//
//		sets the parser for detection of entries owned by others
//
//*****************************************************************************

- setParserForOtherOwner:(const char *)aString
{
static char del1[] = {"\". =:|\t\r\n"}, del2[] = {"\"\t\r\n"};
								// check for an entry in secrets with login 			
								// name and which is NOT owned by user 
	[[Parser setKey1:aString] setKey2:"*"];		  
	[[Parser setDelim1:del1] setDelim2:del2];			// and delimiters
	[Parser setEscape:0];								// words to escape

    return self;
}
//*****************************************************************************
//
//		return login name specified in options file
//
//*****************************************************************************

- (char *)optionsLoginName:(const char *)aPath
{
static char del1[] = {"\". =:|\t\r\n"}, del2[] = {" \"\t\r\n"};
char *oName;

	[[Parser setKey1:"user"] setKey2:""];		  
	[[Parser setDelim1:del1] setDelim2:del2];
	if(!(oName = [Parser parseFile:aPath]))
		[[NXApp delegate] showAlert:"Login Name not found in options file"];

    return oName;
}
//*****************************************************************************
//
//		return secrets password if the user owns the entry for login name
//
//*****************************************************************************

- (char *)secretOwnedBySelf
{
	[self setParserForOwner:[loginNameField stringValue]];

    return [Parser parseFile:NXGetDefaultValue([NXApp appName],"secrets")];
}
//*****************************************************************************
//
//		return secrets password if another user owns the entry for login name
//
//*****************************************************************************

- (char *)secretOwnedByOther
{
	[self setParserForOtherOwner:[loginNameField stringValue]];

    return [Parser parseFile:NXGetDefaultValue([NXApp appName],"secrets")];
}
//*****************************************************************************
//
//		add our default options and pppup files to our new Gate document
//
//		paths for pppup file set from pref editor path
//
//*****************************************************************************

- addDefaultFiles:(const char *)nameOfFile
{
NXStream *Stream;
							// add example options
	if(![[NXBundle mainBundle]getPath:Path forResource:"Examples" ofType:NULL])
		[[NXApp delegate] showAlert:"Error getting examples path"];
	strncat(Path, XOPTION,  MAXPATHLEN - strlen(XOPTION) - strlen(Path) + 1);
	if((Stream = NXMapFile(Path, NX_READONLY)) == NULL)
		[[NXApp delegate] showAlert:"Unable to open example options File"];
	else
		{
		strncpy(Path, nameOfFile, MAXPATHLEN - strlen(OPTION));
		strncat(Path, OPTION, (strlen(OPTION) + 1));
		if((NXSaveToFile(Stream, Path)) == -1)
			[[NXApp delegate] showAlert:"Error saving options to Gate doc"];
		else
			chown(Path, getuid(), getgid());
		NXCloseMemory(Stream, NX_FREEBUFFER);
		}
							// add example pppup
	if(![[NXBundle mainBundle]getPath:Path forResource:"Examples" ofType:NULL])
		[[NXApp delegate] showAlert:"Error getting examples path"];
	strncat(Path, PPPUP,  MAXPATHLEN - strlen(PPPUP) - strlen(Path) + 1);
	if((Stream = NXMapFile(Path, NX_READONLY)) == NULL)
		[[NXApp delegate] showAlert:"Unable to open pppup example File"];
	else
		{
		strncpy(Path, nameOfFile, MAXPATHLEN - strlen(PPPUP));
		strncat(Path, PPPUP, (strlen(PPPUP) + 1));
		if((NXSaveToFile(Stream, Path)) == -1)
			[[NXApp delegate] showAlert:"Error saving pppup to .Gate doc"];
		else
			{
			chmod(Path, (_S_IRUSR | _S_IWUSR));		// set to owner read/write
			chown(Path, getuid(), getgid());
			}
		NXCloseMemory(Stream, NX_FREEBUFFER);
		}
						// add default Icon.tiff
	if(![[NXBundle mainBundle] getPath:Path 
										forResource:"Icon.tiff" ofType:NULL])
		[[NXApp delegate] showAlert:"Error getting Icon.tiff path"];
	if((Stream = NXMapFile(Path, NX_READONLY)) == NULL)
		[[NXApp delegate] showAlert:"Unable to open Icon.tiff File"];
	strncpy(Path, nameOfFile, MAXPATHLEN - strlen(IMAGE));
	strncat(Path, IMAGE, (strlen(IMAGE) + 1));
	if((NXSaveToFile(Stream, Path)) == -1)
	   	[[NXApp delegate] showAlert:"Unable to save Icon.tiff to Gate doc"];
	else
		chown(Path, getuid(), getgid());
	NXCloseMemory(Stream, NX_FREEBUFFER);

    return self;
}
//*****************************************************************************
//
//		return path to options file
//
//*****************************************************************************

- (const char *)optionsPath
{
const char *aPath = NULL;

	if(aPath = [self docPath])
		{
		strcpy(Path, aPath);
		strcat(Path, OPTION);	
		aPath = Path;
		}
	else
		[[NXApp delegate] showAlert:"ddbWriteError"];

    return aPath;
}
//*****************************************************************************
//
//		return path to pppup file
//
//*****************************************************************************

- (const char *)pppupPath
{
const char *aPath = NULL;

	if(aPath = [self docPath])
		{
		strcpy(Path, aPath);
		strcat(Path, PPPUP);	
		aPath = Path;
		}
	else
		[[NXApp delegate] showAlert:"ddbWriteError"];

    return aPath;
}
//*****************************************************************************
//
//		return path to document
//
//*****************************************************************************

- (const char *)docPath
{
    return NXGetDefaultValue([NXApp appName], EDOC);
}
//*****************************************************************************
//
// 		stop parsing  
//
//*****************************************************************************

- ok:sender
{
    return [NXApp stopModal];
}
//*********************** text field matrix delegation ************************
//*****************************************************************************
//
// 		as text delegate we recieve this when textfield cell is being edited 
//
//*****************************************************************************

- (BOOL)textWillChange:textObject
{
	[editPanel setDocEdited:YES];	// indicate change is not saved

    return NO;
} 
//*****************************************************************************
//
// 		text delegate receives when editing of textfield cell is ended 
//
//*****************************************************************************

- textDidEnd:textObject endChar:(unsigned short)whyEnd
{
    if ((whyEnd == 16) || (whyEnd == 17)) 
		[editPanel setDocEdited:NO];	// indicate change is saved

    return self;
}
//******************* Gate doc panel delegate methods *************************
//*****************************************************************************
//
// 		called whenever the user minituriazes our status window.
//
//*****************************************************************************

- windowWillMiniaturize:sender toMiniwindow:miniwindow 
{
    return [sender setMiniwindowIcon:".dir"];
}
//************************************************************************
//
// 		Edit options file in Edit.app
//
//************************************************************************

- editPppup:sender 
{
	if([[NXApp delegate] readable:[self pppupPath]])	// open with edit
		[[Application workspace] openFile:[self pppupPath] 
						  withApplication:"Edit"];
	
    return self;
}
//************************************************************************
//
// 		Edit options file in Edit.app
//
//************************************************************************

- editOptions:sender 
{
	if([[NXApp delegate] readable:[self optionsPath]])	// open with edit
		[[Application workspace] openFile:[self optionsPath] 
						  withApplication:"Edit"];
	
    return self;
}

@end

