/*
    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 "Users.h"
#import "Controller.h"
#import "NIDirectory.h"
#import "NIProperty.h"
#import "NetInfoKeys.h"
#import <sys/types.h>
#import <pwd.h>
#import <time.h>
#import <strings.h>
#import "cryptdefs.h"
#import "ni_crypt.h"

// The property names of a user's directory:
#define U_NAME				"name"
#define U_PASSWD			"passwd"
#define U_UID					"uid"
#define U_GID					"gid"
#define U_REALNAME		"realname"
#define U_HOME				"home"
#define U_SHELL				"shell"
#define U_SMBPASSWD		"smbpasswd"
#define U_SMBNTPASSWD	"smbntpasswd"
#define U_WPASSWD			"_writers_passwd"
#define U_WSMBPASSWD	"_writers_smbpasswd"
#define U_WNTPASSWD		"_writers_smbntpasswd"

// Class variable with the string for the open/save panel's title.
static const char *title;

@implementation Users

// ************************************************************************
// Class methods:
+ initialize
// Called once by the run time system
{
		title = [Service stringFor:"Title:Users"];
		return self;
}

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

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

+ open:sender at:(NXCoord *)offset
{
NIDirectory	*NIDir;
Users				*user = [Users alloc];

		if ((NIDir = [NIDirectory open:sender root:SMNI_USERS withTitle:title])) {		
			[NIDir setDelegate:user];
			if ([user init:sender dirObj:NIDir delta:offset service:[NIDir baseName]])
				return user;

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


// Local methods:
// We'll misuse the check for mandatory fields for password verification!
// This way we don't need to override the save and saveToDomain methods.
- (BOOL)minimumOK
{
int							signal;
char						*passwd, *p;
char						salt[3];
unsigned char		new_p16[16], new_nt_p16[16], new_p32[34], new_nt_p32[34];
struct timeval	tp;
struct timezone	tzp;
char						sa[64] = {'a','b','c','d','e','f','g','h','i','j','k','l','m',
													'n','o','p','q','r','s','t','u','v','w','x','y','z',
													'A','B','C','D','E','F','G','H','I','J','K','L','M',
													'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
													'0','1','2','3','4','5','6','7','8','9','.','/'};

		// Run a modal for the panel.
		[fieldVerifyPassword setStringValue:""];
		[fieldVerifyPassword selectText:self];
		signal = [NXApp runModalFor:password];
		[password close];

		switch (signal) {
			case NX_RUNABORTED:
				return NO;
			case NX_RUNSTOPPED: 
			default: ;
		}

		// Passwords both identical?
		passwd = (char *)[fieldVerifyPassword stringValue];
		if (strcmp(passwd, [textSMBPasswd stringValue]))
			return NO;
		
		if ([checkPassword state]) {
			/* Encrypt the unix password. */

			/* Have to pick a new salt! */
			if (gettimeofday(&tp, &tzp) != 0)
				strncpy(salt, [[ni_dirObj property:U_PASSWD] valueAt:0], 2);	/* Copy the old salt! */
			
			(void) srandom(tp.tv_sec);
			salt[0] = sa[random()%54];
			salt[1] = sa[random()%54];
			salt[2] = '\0';

			[[ni_dirObj property:U_PASSWD] updateValue:crypt((char *)passwd, salt) at:0];
    }

		memset(new_nt_p16, '\0', 16);
		E_md4hash((unsigned char *) passwd, new_nt_p16);
		
		// Mangle the passwords into Lanman format.
		passwd[14] = '\0';
		for (p = passwd; *p; p++)
			*p = NXToUpper(*p);
	
		// Calculate the SMB (lanman) hash functions of new password.
		memset(new_p16, '\0', 16);
		E_P16((unsigned char *) passwd, new_p16);
	
		// Create the 32 byte representation of the new p16.
		ni_encrypt(new_p16, new_p32);
	
		// Create the 32 byte representation of the new NT md4 hash.
		ni_encrypt(new_nt_p16, new_nt_p32);

		[[ni_dirObj property:U_SMBPASSWD] updateValue:new_p32 at:0];
		[[ni_dirObj property:U_SMBNTPASSWD] updateValue:new_nt_p32 at:0];


		return YES;
}

// ************************************************************************
//	Methods:

- delete:sender
{
		NXRunAlertPanel(getString("Alert:Alert"),
										getString("Message:Cannot delete users with SambaManager!"),
										getString("Button:OK"), NULL, NULL);
		return self;
}

- passwordCancel:sender
{
		[NXApp abortModal];
    return self;
}

- passwordOK:sender
{
		[NXApp stopModal];
		return self;
}

- installWithoutSetting:sender
{
char					ud[1024], *dName;
id						status = nil;
int						len = strlen(userName);
unsigned char	new_p32[34], new_nt_p32[34];

		// Set ther user's name as the encoded(!) password
		// This is safe, as the name would be `decoded' before comparing to any
		// entered password.
		(void)strcpy(new_p32, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
		(void)strcpy(new_nt_p32, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
		bcopy(userName, new_p32, (len>32)?32:len);
		bcopy(userName, new_nt_p32, (len>32)?32:len);
		

		[[ni_dirObj property:U_SMBPASSWD] updateValue:new_p32 at:0];
		[[ni_dirObj property:U_SMBNTPASSWD] updateValue:new_nt_p32 at:0];

		// Save
		if (status = [ni_dirObj save]) {
			[window setDocEdited:NO];
			dName = (char *)[ni_dirObj domainName];
			sprintf(ud, "%s/%s", (dName && *dName && (dName[1] != '\0'))?dName:"", [ni_dirObj baseName]);
			[window setTitleAsFilename:ud];
			[buttonInstall setEnabled:NO];
		}
		return status;
}

- setupAndLoad
{
struct passwd	*pwent;

		[ni_dirObj addString:U_REALNAME outlet:textFullName];
		userName = [[ni_dirObj addString:U_NAME outlet:textUserName] valueAt:0];
		[ni_dirObj addString:U_UID outlet:textUserID];
		[ni_dirObj addString:U_GID outlet:textDefaultGroup];
		[ni_dirObj addString:U_HOME outlet:textHomeDirectory];
		[ni_dirObj addString:U_SHELL outlet:textLoginShell];

		[buttonInstall setEnabled:![[ni_dirObj addProperty:U_SMBPASSWD] values]];
		[[ni_dirObj addProperty:U_WSMBPASSWD] updateValue:userName at:0];
		[ni_dirObj addProperty:U_SMBNTPASSWD];
		[[ni_dirObj addProperty:U_WNTPASSWD] updateValue:userName at:0];
		[ni_dirObj addProperty:U_PASSWD];

		pwent = getpwuid(getuid());
	
		// Future versions may allow any user for authentication, but
		// then only the passwords should be updated (and only if the
		// user is the same as given in the _writers properties).
//		[ni_dirObj setAuthenticationUser:pwent->pw_name];
		
		[ni_dirObj scan];
		
		[textUserName setEditable:NO];
		[textUserID setEditable:NO];
		[textFullName selectText:self];

		return self;
}

// ************************************************************************
// Delegates:
- textDidChange:sender
{
		return self;
}

- textDidEnd:textObject endChar:(unsigned short)whyEnd
{
		[NXApp stopModal];
		return self;
}


@end
