/*
    SambaManger. A graphical frontend to configure the NetInfo enhanced samba.
    Copyright (C) 1998  Robert Frank

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
		
		Robert Frank, frank@ifi.unibas.ch
*/

#import "Services.h"
#import "Controller.h"
#import "NIDirectory.h"
#import "NIProperty.h"
#import "Panels.h"
#import "SMBKeys.h"
#import "NetInfoKeys.h"

#define ALERT_STATUS(a,m,b1,b2,b3,arg1,status) \
							NXRunAlertPanel(getString(a), getString(m), \
															getString(b1), \
															b2?getString(b2):NULL, \
															b3?getString(b3):NULL, \
															arg1, ni_error(status))

// The special sections (as spelled in smb.config):
char	*sectionNames[] = {
  NULL,	// This is just a place holder for any service!
	"homes",
	"global",
	"printers"
};
	
// An intermediate object for the dual text field (form):
@interface BiText:Object
{
	id		form;
	id		textDelegate;
	char 	sep, pre, post, *strng;
}
- init:(char)s form:f pre:(char)b post:(char)e;
- free;
- setTextDelegate:sender;
- selectText:sender;
- nextText;
- setNextText:sender;
- setStringValue:(const char *)s;
- (const char *)stringValue;
@end

@implementation BiText
- init:(char)s form:f pre:(char)b post:(char)e
{
		[super init];
		textDelegate = nil;
		form = f;
		sep = s;
		pre = b;
		post = e;
		strng = NULL;
		return self;
}

- free
{
	if (strng)
		NXZoneFree([self zone], strng);
	return [super free];
}

- textDidEnd:textObject endChar:(unsigned short)whyEnd
{
		if (whyEnd == NX_RETURN)
			if ([form selectedRow] == 1)
				[textDelegate textDidEnd:textObject endChar:whyEnd];
		return self;
}

- setTextDelegate:sender
{
		textDelegate = sender;
		return [form setTextDelegate:self];
}

- selectText:sender
{
		return [form selectTextAt:0];
}

- nextText
{
		return nil;
}

- setNextText:sender
{
		return self;
}

- setStringValue:(const char *)s
{
char	*p = NULL, *d, *str = NULL;

		if (!s) {
			[form setStringValue:"" at:0];
			[form setStringValue:"" at:1];
			return self;
		} else {
			p = str = NXCopyStringBufferFromZone(s, [self zone]);
			if (pre && (*p == pre))
				p++;
			if (post && (str[strlen(str)-1] == post))
				str[strlen(str)-1] = '\0';
		}

		if (d = strchr(p, sep)) {
			[form setStringValue:d+1 at:1];
			*d = '\0';
			[form setStringValue:p at:0];
		} else {
			[form setStringValue:p at:0];
			[form setStringValue:"" at:1];
		}
		
		NXZoneFree([self zone], str);
		return self;
}

- (const char *)stringValue
{
int		l1 = strlen([form stringValueAt:0]),
			l2 = strlen([form stringValueAt:1]);
char	*p;

		p = strng = NXZoneRealloc([self zone], strng, l1 + l2 + 3);
		if (pre)
			*p++ = pre;
		strcpy(p, [form stringValueAt:0]);
		p+= l1;
		*p++ = sep;
		strcpy(p, [form stringValueAt:1]);
		p+= l2;
		if (post)
			*p++ = post;
		*p = '\0';
		return strng;
}
@end

// An intermediate object for the file modes:
@interface FileModes:Object
{
	id		window;
	id		radio;
	id		check1, check2, check3;
	id		netinfoDir;
	id		prop;
	
	int		mask;
}
- init:w radio:r check1:c1 check2:c2 check3:c3 niDir:d label:(const char *)l;
- display:sender;
@end

@implementation FileModes
- show:(int)value
{
		[check1 setState:value&0400 at:0 :0];
		[check1 setState:value&0200 at:0 :1];
		[check1 setState:value&0100 at:0 :2];


		[check2 setState:value&0040 at:0 :0];
		[check2 setState:value&0020 at:0 :1];
		[check2 setState:value&0010 at:0 :2];

		[check3 setState:value&0004 at:0 :0];
		[check3 setState:value&0002 at:0 :1];
		[check3 setState:value&0001 at:0 :2];

		return self;
}

- changed:sender
{
char	buf[4];
int		col = [radio selectedCol];

		[window setDocEdited:YES];
		if (sender == radio) {
			if (col) {
				sprintf(buf, "%03o", mask&0777);
				[prop updateValue:buf at:0];
				[self show:mask];
			} else {
				[prop removeValues];
				[self show:0];
			}
		} else {
			[radio selectCellAt:0 :1];
			
			mask = 0;
			mask|= [[check1 cellAt:0 :0] state]?0400:0;
			mask|= [[check1 cellAt:0 :1] state]?0200:0;
			mask|= [[check1 cellAt:0 :2] state]?0100:0;

			mask|= [[check2 cellAt:0 :0] state]?0040:0;
			mask|= [[check2 cellAt:0 :1] state]?0020:0;
			mask|= [[check2 cellAt:0 :2] state]?0010:0;

			mask|= [[check3 cellAt:0 :0] state]?0004:0;
			mask|= [[check3 cellAt:0 :1] state]?0002:0;
			mask|= [[check3 cellAt:0 :2] state]?0001:0;
			
			sprintf(buf, "%03o", mask);
			[prop updateValue:buf at:0];
		}
		
		return self;
}

- init:w radio:r check1:c1 check2:c2 check3:c3 niDir:d label:(const char *)l
{
		window =w;
		radio = r;
		check1 = c1;
		check2 = c2;
		check3 = c3;
		netinfoDir = d;
		mask = 0;
		[radio setTarget:self];
		[radio setAction:@selector(changed:)];
		[check1 setTarget:self];
		[check1 setAction:@selector(changed:)];
		[check2 setTarget:self];
		[check2 setAction:@selector(changed:)];
		[check3 setTarget:self];
		[check3 setAction:@selector(changed:)];
		prop = [netinfoDir addCall:l displayAction:@selector(display:)];
		[prop setDelegate:self];
		return self;
}

- display:sender
{
		mask = 0;	// Isn't set if the string has no number!
		sscanf([sender valueAt:0], "%o", &mask);
		[radio selectCellAt:0 :mask?1:0];
		[self show:mask];
		return self;
}
@end

// Class variable with the string for the open/save panel's title.
static const char *hostTitle, *groupTitle, *userTitle, *servicesTitle;

@implementation Services
// ************************************************************************
// Class methods:
+ initialize
// Called once by the run time system
{
		hostTitle = getString("Title:Hosts");
		groupTitle = getString("Title:Groups");
		userTitle = getString("Title:Users");
		servicesTitle = getString("Title:Services");
		return self;
}

+ new:sender at:(NXCoord *)offset
{
NIDirectory	*NIDir;
Services		*service = [Services alloc];

		if ((service->serviceType = [[Panels new] selectService]) < 0)
			return [service free];

		if ((NIDir = [NIDirectory new:sender root:SMNI_SAMBA directory:NULL])) {
			[NIDir setDelegate:service];
			if ([service init:sender dirObj:NIDir delta:offset service:NULL]) {
				[NIDir setSaveTitle:servicesTitle];
				return service;
			}
			[NIDir close];
		}
		return [service free];
}

+ open:sender at:(NXCoord *)offset
{
NIDirectory	*NIDir;
Services		*service = [Services alloc];
const char 	*sName;

		if ((NIDir = [NIDirectory open:sender root:SMNI_SAMBA withTitle:servicesTitle])) {		
			[NIDir setDelegate:service];
			sName = [NIDir baseName];
			for (service->serviceType = 3; service->serviceType; service->serviceType--)
				if (!strcmp(sectionNames[service->serviceType], sName))
					break;
			if ([service init:sender dirObj:NIDir delta:offset service:sName])
				return service;

			[NIDir close];
		}
		return [service free];
}

// ************************************************************************
// Local methods:

- free
{
		[remAnnounce free];
		[mangleMap free];
		[interfaces free];
		[objCreateMask free];
		[objDirMask free];
		[objForceCreateMask free];
		[objForceDirMask free];

		return [super free];
}

// Set which page of options to display
- selectPage:sender
{
int	number = [[matrixPages selectedCell] tag];

		if (number != lastPage) {
			[[boxChoices contentView] replaceSubview:lastPageID with:boxes[number]];
			lastPageID = boxes[number];
			lastPage = number;
			[window display];
		}
		
		return self;
}

// Display the socket options
- getSocketOpts:sender
{
const char 	*p;
int					i, count = [sender values];
		
		for (i=0; i < count; i++) {
			p = [sender valueAt:i];
			if (!strcmp(p, S_SO_KEEPALIVE))
					[formSOChecks selectCellAt:0 :0];
			else if (!strcmp(p, S_SO_REUSEADDR))
				[formSOChecks selectCellAt:1 :0];
			else if (!strcmp(p, S_SO_BROADCAST))
				[formSOChecks selectCellAt:0 :1];
			else if (!strcmp(p, S_SO_TCP_NODEL))
				[formSOChecks selectCellAt:1 :1];
			else if (!strncmp(p, S_SO_SNDBUF, strlen(S_SO_SNDBUF))) {
				p = strchr(p, '=') + 1;
				[formSOBufSizes setStringValue:p at:0];
			} else if (!strncmp(p, S_SO_RCVBUF, strlen(S_SO_RCVBUF))) {
				p = strchr(p, '=') + 1;
				[formSOBufSizes setStringValue:p at:1];
			} else if (!strncmp(p, S_SO_SNDLOWAT, strlen(S_SO_SNDLOWAT))) {
				p = strchr(p, '=') + 1;
				[formSOLowWatMark setStringValue:p at:0];
			} else if (!strncmp(p, S_SO_RCVLOWAT, strlen(S_SO_RCVLOWAT))) {
				p = strchr(p, '=') + 1;
				[formSOLowWatMark setStringValue:p at:1];
			}
		}
		return self;
}

- setSocketOpts:sender
{
		[window setDocEdited:YES];
		return self;
}


// ************************************************************************
//	Methods:
- (BOOL)minimumOK
{
const char	*value = [[ni_dirObj property:S_PATH] valueAt:0];
char				*pathEnd = strrchr(value, '/');

		if (serviceType > 0)
			return [super minimumOK];
		else if (*value) {
			if (!*[[ni_dirObj property:S_NAME] valueAt:0]) {
				[[ni_dirObj property:S_NAME] updateValue:pathEnd?pathEnd+1:value at:0];
				[ni_dirObj setAllowChangName:YES];
			}
		} else {
			NXRunAlertPanel(getString("Alert:Alert"),
											getString("Message:No path set!"),
											getString("Button:OK"),
											NULL, NULL);
			return NO;
		}
		return YES;
}

- setGlobService
{		
		// GLOBALS

		// access password security name mangling
		[ni_dirObj addBool:S_ENCRYPT_PASS outlet:radioEncryptPasswd];
		[ni_dirObj addBool:S_NULL_PASSWDS outlet:radioNullPasswd];
		[ni_dirObj addBool:S_UNIX_REALNAME outlet:radioUnixRealName];
		[ni_dirObj addInt:S_PASSWD_LEVEL text:textPasswdLevel slider:sliderPasswdLevel zero:NULL];
		[ni_dirObj addPopup:S_SECURITY outlet:popupSecurity];
		[ni_dirObj addString:S_HOSTS_EQUIV text:textHostsEquiv
							 button:[formSetPassSec cellAt:0 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_PASSWD_CHAT outlet:textPasswdChat];
		[ni_dirObj addString:S_PASSWD_PROG text:textPasswdProgram
							 button:[formSetPassSec cellAt:2 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_ROOT_DIR text:textRootDirectory
							 button:[formSetPassSec cellAt:3 :0]  mode:NIPT_DIR path:NULL title:NULL];
		[ni_dirObj addString:S_SMB_PASSWD_FL text:textSMBPasswdFile
							 button:[formSetPassSec cellAt:4 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addBrowser:S_PASSWD_SERVER browser:browserPasswdServer text:textPasswdServer
							 add:buttonAddPasswdServer remove:buttonRemPasswdServer];
		[ni_dirObj addBool:S_CASE_SENSITIV outlet:radioCaseSensitive];
		[ni_dirObj addBool:S_STRIP_DOT outlet:radioStripDot];
		[ni_dirObj addInt:S_MANGLED_STACK text:textMangleStack slider:sliderMangleStack zero:NULL];
		[ni_dirObj addPopup:S_CHAR_SET outlet:popupCharSet];
		[ni_dirObj addPopup:S_CLNT_CODE_PG outlet:popupCodePage];

		// Logon locking logging
		[ni_dirObj addBool:S_DOMAIN_LOGONS outlet:radioDomainLogons];
		[ni_dirObj addString:S_LOGON_PATH outlet:textLogonPath];
		[ni_dirObj addString:S_LOGON_SCRIPT text:textLogonScript
							 button:[formGSetLogon cellAt:1 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_MESSAGE outlet:textMessageCommand];
		[ni_dirObj addString:S_SMB_RUN text:textSMBRun
							 button:[formGSetLogon cellAt:3 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_USERNAME_MAP text:textUserNameMap
							 button:[formGSetLogon cellAt:4 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addBool:S_STATUS outlet:radioStatus];
		[ni_dirObj addBool:S_SYSLOG_ONLY outlet:radioSyslogOnly];
		[ni_dirObj addString:S_LOCK_DIR text:textLockDir
							 button:buttonSetLockDir mode:NIPT_DIR path:NULL title:NULL];
//!!! suffix = log.%m
		propLogFile = [ni_dirObj addString:S_LOG_FILE text:textLogFile
														 button:buttonSetLogFile mode:NIPT_DIR path:NULL title:NULL];
		[ni_dirObj addInt:S_MAX_LOG_SIZE text:textLogSize slider:sliderLogSize
							 zero:getString("string:unlimited")];
		[ni_dirObj addInt:S_LOG_LEVEL text:textLogLevel slider:sliderLogLevel zero:NULL];
		
		// nmbd
		[ni_dirObj addBool:S_DNS_PROXY outlet:radioDNSProxy];
		[ni_dirObj addBool:S_LOCAL_MASTER outlet:radioLocalMaster];
		[ni_dirObj addBool:S_TIME_SERVER outlet:radioTimeServer];
		[ni_dirObj addBool:S_WINS_PROXY outlet:radioWINSProxy];
		remAnnounce = [[BiText alloc] init:'/' form:formRemAnnounce pre:'\0' post:'\0'];
		[ni_dirObj addBrowser:S_REM_ANNOUNCE browser:browserRemAnnounce text:remAnnounce
							 add:buttonAddRemAnnounce remove:buttonRemRemAnnounce];
		[ni_dirObj addBrowser:S_NB_ALIASES browser:browserNBAliases text:textNBAliases
							 add:buttonAddNBAliases remove:buttonRemNBAliases];

		// Startup
		[ni_dirObj addBool:S_DOMAIN_MASTER outlet:radioDomainMaster];
		[ni_dirObj addBool:S_LOAD_PRNTRS outlet:radioLoadPrinters];
		[ni_dirObj addBool:S_PREF_MASTER outlet:radioPrefMaster];
		[ni_dirObj addBool:S_WINS_SUPPORT outlet:radioWINSSupport];
		[ni_dirObj addInt:S_OS_LEVEL text:textOSLevel slider:sliderOSLevel zero:NULL];
		[ni_dirObj addPopup:S_ANNOUNCE_AS outlet:popupAnnounceAs];
		[ni_dirObj addPopup:S_PRINTING outlet:popupPrinting];
		[ni_dirObj addPopup:S_PROTOCOL outlet:popupProtocol];
		propAnnounceVersion = [ni_dirObj addString:S_ANNOUNCE_VER outlet:textAnnounceVersion];
		[ni_dirObj addString:S_CONF_FILE text:textConfigFile
							 button:[formSetStartup cellAt:1 :0]  mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_DEFAULT_SERV text:textDefaultService
							 button:[formSetStartup cellAt:2 :0]  mode:NIPT_NETINFO
							 path:SMNI_SAMBA title:servicesTitle];
		[ni_dirObj addString:S_NB_NAME outlet:textNetBIOSName];
		[ni_dirObj addString:S_PRNTCAP_NAME text:textPrintcapName
							 button:[formSetStartup cellAt:4 :0]  mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_SERVER_STRNG outlet:textServerString];
		[ni_dirObj addString:S_SOCK_ADDR outlet:textSocketAddress];
		[ni_dirObj addString:S_WINS_SERVER text:textWINSServer
							 button:[formSetStartup cellAt:7 :0] mode:NIPT_NETINFO
							 path:SMNI_SAMBA title:hostTitle];
		[ni_dirObj addString:S_WORKGROUP outlet:textWorkGroup];
		interfaces = [[BiText alloc] init:'/' form:formInterfaces pre:'\0' post:'\0'];
		[ni_dirObj addBrowser:S_INTERFACES browser:browserInterfaces text:interfaces
					 		 add:buttonAddInterfaces remove:buttonRemInterfaces];
		[ni_dirObj addBrowser:S_AUTO_SERVICES browser:browserAutoServices
							 add:buttonAddAutoServices remove:buttonRemAutoServices
							 mode:NIPT_NETINFO path:SMNI_SAMBA title:servicesTitle];

		// Tuning
		[ni_dirObj addBool:S_GETWD_CACHE outlet:radioGetwdCache];
		[ni_dirObj addBool:S_READ_PRED outlet:radioReadPrediction];
		[ni_dirObj addBool:S_READ_RAW outlet:radioReadRaw];
		[ni_dirObj addBool:S_SYNC_ALWAYS outlet:radioSyncAlways];
		[ni_dirObj addBool:S_WRITE_RAW outlet:radioWriteRaw];
		[ni_dirObj addInt:S_DEAD_TIME text:textDeadTime slider:sliderDeadTime
					 zero:getString("string:no timeout")];
		[ni_dirObj addInt:S_KEEP_ALIVE text:textKeepAlive slider:sliderKeepAlive
					 zero:getString("string:no packets")];
		[ni_dirObj addInt:S_LPQ_CACHE_TM text:textlpqCache slider:sliderlpqCache
					 zero:getString("string:no caching")];
		[ni_dirObj addInt:S_READ_SIZE text:textReadSize slider:sliderReadSize zero:NULL];
		[formSOBufSizes setTextDelegate:self];
		[formSOLowWatMark setTextDelegate:self];
		[formSOChecks setTarget:self];
		[formSOChecks setAction:@selector(setSocketOpts:)];
		[ni_dirObj addCall:S_SOCK_OPTS displayAction:@selector(getSocketOpts:)];
		
		return self;
}

- setService
{	
		// SERVICES
		
		// access
		[ni_dirObj addBool:S_BROWSEABLE outlet:radioBrowseable];
		[ni_dirObj addBool:S_GUEST_ONLY outlet:radioGuestOnly];
		[ni_dirObj addBool:S_ONLY_USER outlet:radioOnlyUser];
		[ni_dirObj addBool:S_PRNTABLE outlet:radioPrintable];
		[ni_dirObj addBool:S_PUBLIC outlet:radioPublic];
		[ni_dirObj addBool:S_READ_ONLY outlet:radioReadOnly];
		propAllowHosts = [ni_dirObj addBrowser:S_ALLOWHOSTS browser:browserAllowHosts
																add:buttonAddAllowHosts remove:buttonRemAllowHosts
																mode:NIPT_NETINFO path:SMNI_HOSTS title:hostTitle];
		propDenyHosts = [ni_dirObj addBrowser:S_DENY_HOSTS browser:browserDenyHosts
															 add:buttonAddDenyHosts remove:buttonRemDenyHosts
															 mode:NIPT_NETINFO path:SMNI_HOSTS title:hostTitle];
		propReadList = [ni_dirObj addBrowser:S_READ_LIST browser:browserReadList
															 add:buttonAddReadList remove:buttonRemReadList
															 mode:NIPT_NETINFO path:SMNI_USERS title:userTitle];
		propWriteList = [ni_dirObj addBrowser:S_WRITE_LIST browser:browserWriteList
															 add:buttonAddWriteList remove:buttonRemWriteList
															 mode:NIPT_NETINFO path:SMNI_USERS title:userTitle];
		propValidUsers = [ni_dirObj addBrowser:S_VALID_USERS browser:browserValidUsers
																add:buttonAddValidUsers remove:buttonRemValidUsers
																mode:NIPT_NETINFO path:SMNI_USERS title:userTitle];
		propInvalidUsers = [ni_dirObj addBrowser:S_INVALID_USERS browser:browserInvalidUsers
																	add:buttonAddInvalidUsers remove:buttonRemInvalidUsers
																	mode:NIPT_NETINFO path:SMNI_USERS title:userTitle];
		[ni_dirObj addInt:S_MAX_CONNS text:textMaxCons slider:sliderMaxCons
							 zero:getString("string:unlimited")];
		[ni_dirObj addString:S_VOLUME outlet:textVolume];

		// File visibility name mangling
		[ni_dirObj addBool:S_HIDE_DOT_FILE outlet:radioHideDots];
		[ni_dirObj addBool:S_SET_DIR outlet:radioSetDir];
		[ni_dirObj addBool:S_FOLLOW_SYMLNK outlet:radioFollowSymLinks];
		[ni_dirObj addBool:S_WIDE_LINKS outlet:radioWideLinks];
		[ni_dirObj addBrowser:S_DONT_DESCEND browser:browserDescend
					 		 add:buttonAddDescend remove:buttonRemDescend
							 mode:NIPT_DIR path:NULL title:NULL];
// seperator = '/'
		propHideFiles = [ni_dirObj addBrowser:S_HIDE_FILES browser:browserHideFiles text:textHideFiles
					 										 add:buttonAddHideFiles remove:buttonRemHideFiles];
		propHideFilesSeparator = [ni_dirObj addProperty:S_HIDE_FILES_S];
// seperator = '/'
		propVetoFiles = [ni_dirObj addBrowser:S_VETO_FILES browser:browserVetoFiles text:textVetoFiles
															 add:buttonAddVetoFiles remove:buttonRemVetoFiles];
		propVetoFilesSeparator = [ni_dirObj addProperty:S_VETO_FILES_S];
		[ni_dirObj addBool:S_MANGLE_CASE outlet:radioMangleCase];
		[ni_dirObj addBool:S_MANGLED_NAMES outlet:radioMangledName];
		[ni_dirObj addBool:S_PRESERVE_CASE outlet:radioPreserveCase];
		[ni_dirObj addBool:S_SHRT_PRS_CASE outlet:radioShortPresCase];
		[ni_dirObj addBool:S_DEFAULT_CASE outlet:radioDefaultCase];
		[ni_dirObj addBrowser:S_VALID_CHARS browser:browserValidChars text:textValidChars
					 		 add:buttonAddValidChars remove:buttonRemValidChars];
		mangleMap = [[BiText alloc] init:' ' form:formMangleMap pre:'(' post:')'];
		[ni_dirObj addBrowser:S_MANGLED_MAP browser:browserMangledMap text:mangleMap
					 		 add:buttonAddMangledMap remove:buttonRemMangledMap];
		[ni_dirObj addChar:S_MANGLING_CHAR outlet:textMangleChar];

		// File permissions locking logging
		[ni_dirObj addBool:S_ALTPERM outlet:radioAlternate];
		[ni_dirObj addBool:S_DEL_READ_ONLY outlet:radioDelReadOnly];
		[ni_dirObj addBool:S_DOS_FILETIMES outlet:radioDOSFileTimes];
		[ni_dirObj addBool:S_MAP_ARCHIVE outlet:radioMapArchive];
		[ni_dirObj addBool:S_MAP_HIDDEN outlet:radioMapHidden];
		[ni_dirObj addBool:S_MAP_SYSTEM outlet:radioMapSystem];
		[ni_dirObj addString:S_FRCE_USER text:textForceUser
							 button:buttonSetForceUser mode:NIPT_NETINFO
							 path:SMNI_USERS title:userTitle];
		[ni_dirObj addString:S_FRCE_GROUP text:textForceGroup
							 button:buttonSetForceGroup mode:NIPT_NETINFO 
							 path:SMNI_GROUPS title:groupTitle];
		objCreateMask = [[FileModes alloc] init:window radio:radioCreateMask
																			 check1:check1CreateMask
																			 check2:check2CreateMask
																			 check3:check3CreateMask
																			 niDir:ni_dirObj
																			 label:S_CREATE_MASK];
		objDirMask = [[FileModes alloc] init:window radio:radioDirMask
																			 check1:check1DirMask
																			 check2:check2DirMask
																			 check3:check3DirMask
																			 niDir:ni_dirObj
																			 label:S_DIR_MASK];
		objForceCreateMask = [[FileModes alloc] init:window radio:radioForceCreateMask
																			 check1:check1ForceCreateMask
																			 check2:check2ForceCreateMask
																			 check3:check3ForceCreateMask
																			 niDir:ni_dirObj
																			 label:S_FRCE_CREAT_MD];
		objForceDirMask = [[FileModes alloc] init:window radio:radioForceDirMask
																			 check1:check1ForceDirMask
																			 check2:check2ForceDirMask
																			 check3:check3ForceDirMask
																			 niDir:ni_dirObj
																			 label:S_FRCE_DIR_MODE];
		[ni_dirObj addBool:S_FAKE_OPLOCKS outlet:radioFakeOpLocks];
		[ni_dirObj addBool:S_LOCKING outlet:radioLocking];
		[ni_dirObj addBool:S_SHARE_MODES outlet:radioShareModes];
		[ni_dirObj addBool:S_STRCT_LOCKING outlet:radioStrictLocking];

		// Passwords and Security logon and execution startup
		[ni_dirObj addBool:S_REVALIDATE outlet:radioRevalidate];
		[ni_dirObj addString:S_GUEST_ACCOUNT text:textGuestAccount
							 button:buttonSetGuestAccount mode:NIPT_NETINFO 
							 path:SMNI_USERS title:userTitle];
		[ni_dirObj addBrowser:S_ADMINUSERS browser:browserAdminUsers
					 		 add:buttonAddAdminUsers remove:buttonRemAdminUsers
							 mode:NIPT_NETINFO path:SMNI_USERS title:userTitle];
		propUserNames = [ni_dirObj addBrowser:S_USERNAME browser:browserUserNames
								 		 add:buttonAddUserNames remove:buttonRemUserNames
										 mode:NIPT_NETINFO path:SMNI_USERS title:userTitle];
		propMagicOutput= [ni_dirObj addString:S_MAGIC_OUTPUT text:textMagicOutput
																button:[formSetLogon cellAt:0 :0] mode:NIPT_DIR path:NULL title:NULL];
		[ni_dirObj addString:S_MAGIC_SCRIPT text:textMagicScript
							 button:[formSetLogon cellAt:1 :0]  mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_PATH text:textPath
							 button:[formSetLogon cellAt:2 :0] mode:NIPT_DIR path:NULL title:NULL];
		[ni_dirObj addString:S_PRE_EXEC text:textPreExecution
							 button:[formSetLogon cellAt:3 :0]  mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_POST_EXEC text:textPostExecution
							 button:[formSetLogon cellAt:4 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_ROOT_PRE_EX text:textRootPreExecution
							 button:[formSetLogon cellAt:5 :0] mode:NIPT_FILE path:NULL title:NULL];
		[ni_dirObj addString:S_ROOT_POST_EX text:textRootPostExecution
							 button:[formSetLogon cellAt:6 :0] mode:NIPT_FILE path:NULL title: NULL];
		[ni_dirObj addString:S_INCLUDE text:textInclude
							 button:buttonSetInclude mode:NIPT_FILE path:NULL title: NULL];

		return self;
}

- setPrinters
{
		// PRINTERS
		[[ni_dirObj addProperty:S_COMMENT] updateValue:"Template for all Samba Printers" at:0];
		[[ni_dirObj addProperty:S_SHARE_MODES] updateValue:"no" at:0];
		[[ni_dirObj addProperty:S_READ_ONLY] updateValue:"no" at:0];

		[ni_dirObj addInt:S_MIN_PRNT_SPC text:textMinPrntSpace slider:sliderMinPrntSpace
					 zero:getString("string:no caching")];
		[ni_dirObj addBool:S_AVAILABLE outlet:checkPAvailable];
		[ni_dirObj addBool:S_BROWSEABLE outlet:radioPBrowseable];
		[ni_dirObj addBool:S_POSTSCRIPT outlet:radioPostscript];
		[ni_dirObj addBool:S_PUBLIC outlet:radioPPublic];
		[ni_dirObj addBool:S_GUEST_ONLY outlet:radioPGuestOnly];
		[ni_dirObj addBool:S_WRITEABLE outlet:radioPWriteable];
		[[ni_dirObj addProperty:S_PRNTABLE] updateValue:"yes" at:0];
		[ni_dirObj addString:S_PRNT_DRIVER outlet:textPrinterDriver];
		[ni_dirObj addString:S_PATH text:textSpoolDirectory
							 button:buttonSetSpoolDir mode:NIPT_DIR path:NULL title: NULL];
		[ni_dirObj addString:S_PRNT_COMM outlet:textUnixPrint];
		[ni_dirObj addString:S_LPRM outlet:textUnixDelete];
		[ni_dirObj addString:S_LPQ outlet:textUnixShowQueue];
		[ni_dirObj addString:S_LPPAUSE outlet:textUnixPause];
		[ni_dirObj addString:S_LPRESUME outlet:textUnixResume];

		return self;
}


- setupAndLoad
{
NXRect	bframe;
id			nameProp;

		// Set the target and action of the page pop up list.
		popupPages = [buttonPages target];
		[popupPages setTarget:self];
		[popupPages setAction:@selector(selectPage:)];
		matrixPages = [popupPages itemList];

		// Set the users or group panel
		panels = [Panels new];

		locked = NO;
		
		// Set the name field. If this was a +new, set the name for homes, 
		// globals, and printers (serviceType > 0)
		nameProp = [ni_dirObj addProperty:S_NAME];
		if (![ni_dirObj domainName] && (serviceType > 0))
			[nameProp insertValue:sectionNames[serviceType]];

#define setOrigin(n) [n getFrame:&bframe]; bframe.origin.x = 5.0; bframe.origin.y = 5.0; [n setFrame:&bframe]

		if (serviceType < 3) {

			boxes[0] = boxSAccess; setOrigin(boxSAccess);
			boxes[1] = boxSVisMangle; setOrigin(boxSVisMangle);
			boxes[2] = boxSPermLock; setOrigin(boxSPermLock);
			boxes[3] = boxSPwSecLogExecStart; setOrigin(boxSPwSecLogExecStart);
			[self setService];
			
			if (serviceType < 2) {
				boxes[8] = nil; [popupPages removeItemAt:9];
				boxes[7] = nil; [popupPages removeItemAt:8];
				boxes[6] = nil; [popupPages removeItemAt:7];
				boxes[5] = nil; [popupPages removeItemAt:6];
				boxes[4] = nil; [popupPages removeItemAt:5];
			} else {
				// Put the box ids into a list:
				boxes[4] = boxGAccPwSecNM; setOrigin(boxGAccPwSecNM);
				boxes[5] = boxGLockLog; setOrigin(boxGLockLog);
				boxes[6] = boxGBinder; setOrigin(boxGBinder);
				boxes[7] = boxGStartup; setOrigin(boxGStartup);
				boxes[8] = boxGTunig; setOrigin(boxGTunig);
				[self setGlobService];
			}

			[[boxChoices contentView] replaceSubview:lastPageID with:boxes[0]];
			lastPageID = boxSAccess;
			lastPage = 0;
		} else {
			setOrigin(boxPrinters);
			[[window contentView] replaceSubview:boxChoices with:boxPrinters];
			[self setPrinters];
		}
		
		[ni_dirObj scan];
		return self;
}


// ************************************************************************
// Delegates:
- (int)willSelect:sender what:(int)kind path:(char **)ni_path title:(char **)ni_panel_title state:(int *)mode
{
		if ((sender == propReadList) || (sender == propWriteList) ||
				(sender == propValidUsers) || (sender == propInvalidUsers) ||
				(sender == propUserNames)) {
			if ((*mode = [panels userOrGroup]) == PS_NONE)
				return NIPT_NONE;
			if (*mode == PS_GROUP) {
				*ni_path = SMNI_GROUPS;
				*ni_panel_title = (char *)groupTitle;
			}
		} else if ((sender == propAllowHosts) || (sender == propDenyHosts)) {
			if ((*mode = [panels netgroupOrHost]) == PS_NONE)
				return NIPT_NONE;
		}
		return kind;
}

- (int)didSelect:sender what:(int)kind value:(char **)selection domain:(const char *)niDomain state:(int)mode
{
ni_id				tempDir;
ni_name			tempFullDir;
ni_namelist	netgroupList;
void				*tempHandle = NULL;
NIDomain		*tempDomain;
char				*p;
int					status;

		if (!*selection || !**selection)
			return 0;
	
		if ((sender == propLogFile) || (sender == propMagicOutput)) {
			if (!strrchr(*selection, '.')) {
				p = NXZoneRealloc([NXApp zone], *selection, strlen(*selection)+8);
				if (p[strlen(p)-1] != '/')
					(void)strcat(p,"/");
				if (sender == propMagicOutput)
					(void)strcat(p, "%U.out");
				else
					(void)strcat(p, "log.%m");
				*selection = p;
			}
		} else if ((sender == propReadList) || (sender == propWriteList) ||
							 (sender == propValidUsers) || (sender == propInvalidUsers) ||
							 (sender == propUserNames)) {
			if (mode == PS_GROUP) {
				p = NXZoneRealloc([NXApp zone], *selection, strlen(*selection)+1);
				bcopy(p, p+1, strlen(p)+1);
				*p = '@';
				*selection = p;
			}
		} else if ((sender == propAllowHosts) || (sender == propDenyHosts)) {
			if (mode == PS_GROUP) {
				// Present a browser with all possible choices.
				// Allocate and connect a NetInfo handle.
				tempDomain = [[NIDomain alloc] init];
				status = [tempDomain setConnection:niDomain readTimeout:5 writeTimeout:10 canAbort:YES mustWrite:NO];
				if (status == NI_OK) {
					tempHandle = [tempDomain getDomainHandle];
					if (tempHandle == NULL) {
						status = [tempDomain lastError];
					}
				}
				if (status == NI_OK) {
					tempFullDir = NXZoneMalloc([self zone], sizeof(char)*(strlen(SMNI_HOSTS)+strlen(*selection)+2));
					(void)strcpy(tempFullDir, SMNI_HOSTS);
					(void)strcat(tempFullDir, "/");
					(void)strcat(tempFullDir, *selection);
					// Locate the directory.
					status = ni_pathsearch(tempHandle, &tempDir, tempFullDir);
					ni_name_free(&tempFullDir);
					if (status == NI_OK)
						status = ni_lookupprop(tempHandle, &tempDir, SMNIP_NETGROUP, &netgroupList);
				}
				[tempDomain free];
				if (status != NI_OK) {
					ALERT_STATUS("Alert:NetInfo Error", "Message:Connecting to",
											 "Button:OK", NULL, NULL, niDomain, status);
					return -1;
				}
				if (netgroupList.ni_namelist_len <= 0) {
					status = NXRunAlertPanel(getString("Alert:Alert"),
															getString("Message:No Netgroups"),
															getString("Button:Try Again"),
															getString("Button:Cancel"), NULL);
					if (status ==  NX_ALERTALTERNATE)
						return -1;
					return 1;
				}
				p = (char *)[panels showNetgroups:&netgroupList];
				if (!p) {
					ni_namelist_free(&netgroupList);
					return -1;
				}
				
				// Prepend the '@'
				*selection = NXZoneRealloc([NXApp zone], *selection, strlen(p)+1);
				strcpy(*selection+1, p);
				**selection = '@';
				ni_namelist_free(&netgroupList);
			}
		}
		return 0;
}

- (BOOL)propertyWillChange:sender value:(char **)string default:(BOOL *)remove index:(int)anInt
{
char	*p = *string;
int		alert = 0;

		if (*string && ((sender == propHideFiles) || (sender == propVetoFiles))) {
			if (strchr(*string, '/')) {
				NXRunAlertPanel(getString("Alert:Alert"),
												getString("Message:No '/' in names allowed!"),
												getString("Button:OK"),
												NULL, NULL);
				return YES;
			}
		} else if (*string && (sender == propAnnounceVersion)) {
			alert = 1;
			if (atoi(*string) > 0) {
				if (p = strchr(*string, '.')) {
					if (atoi(p+1) > 0)
						alert = 0;
				}
			}
			if (alert) {
				NXRunAlertPanel(getString("Alert:Alert"),
												getString("Message:Illegal version number in Anounce Version"),
												getString("Button:OK"),
												NULL, NULL);
				return YES;
			}
		}
		
		return NO;
}

- propertyDidChange:sender value:(const char *)string
{
		if (locked)
			return self;
		
		[window setDocEdited:YES];
		 if (sender == propHideFiles) {
			if (string)
				[propHideFilesSeparator updateValue:"/" at:0];
			else
				[propHideFilesSeparator removeValues];
		} else if (sender == propVetoFiles) {
			if (string)
				[propVetoFilesSeparator updateValue:"/" at:0];
			else
				[propVetoFilesSeparator removeValues];
		}
		return self;
}

@end
