This is NIDirectory.m in view mode; [Download] [Up]
/*
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 "NIDirectory.h"
#import "NIProperty.h"
#import <nikit/NIDomainPanel.h>
#import <nikit/NILoginPanel.h>
#import <nikit/NIOpenPanel.h>
#import <nikit/NISavePanel.h>
#define PROPNAME "name"
#define ALERT1(a,m,b1,b2,b3,arg) \
NXRunAlertPanel([strings valueForStringKey:a], \
[strings valueForStringKey:m], \
[strings valueForStringKey:b1], \
b2?[strings valueForStringKey:b2]:NULL, \
b3?[strings valueForStringKey:b3]:NULL, \
arg)
#define ALERT_STATUS(a,m,b1,b2,b3,arg1,status) \
NXRunAlertPanel([strings valueForStringKey:a], \
[strings valueForStringKey:m], \
[strings valueForStringKey:b1], \
b2?[strings valueForStringKey:b2]:NULL, \
b3?[strings valueForStringKey:b3]:NULL, \
arg1, ni_error(status))
//*****************************************************
// An auxillary class for the authentication.
@interface AuthEntry:Object
{
char *key; // This must remain unchanged during the object's lifetime!
char *userName;
char *password;
}
- (BOOL)authenticate:(void *)handle;
- free;
- (const char *)key;
- init:(const char *)path user:(const char *)user passwd:(const char *)passwd;
@end
@implementation AuthEntry
- init:(const char *)path user:(const char *)user passwd:(const char *)passwd
{
key = NXCopyStringBufferFromZone(path, [self zone]);
userName = NXCopyStringBufferFromZone(user, [self zone]);
password = NXCopyStringBufferFromZone(passwd, [self zone]);
return self;
}
- free
{
NXZoneFree([self zone], key);
NXZoneFree([self zone], userName);
NXZoneFree([self zone], password);
return self;
}
- (const char *)key
{
return key;
}
- (BOOL)authenticate:(void *)handle
{
int status;
status = ni_setuser(handle, userName);
if (status == NI_OK)
status = ni_setpassword(handle, password);
if (status == NI_OK)
return YES;
else
return NO;
}
@end
//*****************************************************
// The actual NIDirectory class.
// Static (class) variables:
static NXStringTable *strings = nil; // The localized strings.
static HashTable *authHash = nil; // The hashtable for authenticated domains
static const char *defaultText = "default";
static AuthEntry *globalLogin;
static const char *gobalDomainName;
@implementation NIDirectory
// Dummy methods for the compiler:
- (BOOL)niDirOk:(const char *)domain path:(const char *)directory
{
return NO;
}
//*****************************************************
// Class methods:
+ initialize
// Called once by the run time system
{
char buf[MAXPATHLEN + 1];
if (!strings)
if ( [[NXBundle mainBundle] getPath:buf forResource:"NIDirectory" ofType:"strings"] )
if (strings = [[[NXStringTable alloc] init] readFromFile:buf]) {
defaultText = [strings valueForStringKey:"String:default"];
[NIProperty init:strings];
}
if (!authHash)
authHash = [[HashTable alloc] initKeyDesc:"*"];
return nil;
}
+ new:sender root:(const char *)rootPath directory:(const char *)dirPath
{
return [[NIDirectory alloc] init:sender dom:NULL root:rootPath dir:dirPath errors:YES];
}
+ open:sender root:(const char *)rootPath withTitle:(const char *)title
// Open a panel displaying the domains and the entries of the directory given in baseDir.
{
NIOpenPanel *openPanel;
NIDirectory *propList;
const char *dir, *domainPath;
char *fullDir;
openPanel = [NIOpenPanel new];
[openPanel setDirectoryPath:rootPath];
[openPanel setPanelTitle:[strings valueForStringKey:"Title:Select in NetInfo Domain"]];
[openPanel setListTitle:title];
if ([openPanel runModal] != NX_ALERTDEFAULT)
return nil;
if (((dir = [openPanel directory]) == NULL) || ((domainPath = [openPanel domain]) == NULL))
return nil;
fullDir = NXZoneMalloc([self zone], sizeof(char)*(strlen(rootPath)+strlen(dir)+2));
(void)strcpy(fullDir, rootPath);
if ((fullDir[strlen(fullDir)-1] != '/') && (*dir != '/'))
(void)strcat(fullDir, "/");
(void)strcat(fullDir, dir);
// Check for possible duplications. If the sender can
// respond to niDirOk and returns NO, abort.
if ([sender respondsTo:@selector(niDirOk:path:)])
if (![sender niDirOk:domainPath path:fullDir]) {
NXZoneFree([self zone], fullDir);
return nil;
}
propList = [[NIDirectory alloc] init:sender dom:domainPath root:rootPath dir:dir errors:YES];
NXZoneFree([self zone], fullDir);
if (propList)
[propList setSaveTitle:title];
return propList;
}
//*****************************************************
// Local methods
- (BOOL)panel:thePanel authenticateUser:(const char *)userName
withPassword:(const char *)password inDomain:(void *)aDomain
{
globalLogin = [[AuthEntry alloc] init:gobalDomainName user:userName passwd:password];
return YES;
}
- (BOOL)authenticate:(const char *)path forDomain:(NIDomain *)aDomain andUser:(const char *)userName
// Check for an authenticated path. If userName is NULL, use root and disallow changing the name.
{
NILoginPanel *loginPanel = [NILoginPanel new];
gobalDomainName = [aDomain getFullPath];
if (!(globalLogin = [authHash valueForKey:gobalDomainName])) {
[loginPanel setDelegate:self];
if ([loginPanel runModalWithValidation:self inDomain:[aDomain getDomainHandle]
withUser:userName?userName:"root"
withInstruction:[strings valueForStringKey:"Title:User Authentication"]
allowChange:(userName != NULL)])
[authHash insertKey:[globalLogin key] value:globalLogin];
}
return [globalLogin authenticate:[aDomain getDomainHandle]];
}
- doSave
// Check if baseName is identical to the name property, if so, save. Otherwise,
// delete the old entry and create a new on.
{
ni_name tempName = baseName, tempFullDir, tempFullPath;
int index, status, choice;
u_long oldInstance = directory.nii_instance;
ni_id tempDir, tempRoot;
void *tempHandle = NULL;
NIDomain *tempDomain;
// Have to check each textfield, as it may not have been ended.
for (index = 0; index < [list count]; index++)
if (![[list objectAt:index] updateProperty])
return nil;
// Check the properties list for a name property. If none exists and
// baseName isn't defined, display an error and exit with nil.
index = ni_proplist_match(properties, PROPNAME, NULL);
if (index != NI_INDEX_NULL)
tempName = properties.ni_proplist_val[index].nip_val.ni_namelist_val[0];
else if (!baseName || !*baseName) {
ALERT1("Alert:Alert", "Message:Need a name", "Button:OK", NULL, NULL, NULL);
return nil;
}
tempFullDir = NXZoneMalloc([self zone], sizeof(char)*(strlen(dirName)+strlen(tempName)+2));
(void)strcpy(tempFullDir, dirName);
if ((tempFullDir[strlen(tempFullDir)-1] != '/') && (*tempName != '/'))
(void)strcat(tempFullDir, "/");
(void)strcat(tempFullDir, tempName);
tempFullPath = NXZoneMalloc([self zone], sizeof(char)*(strlen(domainName)+strlen(tempFullDir)+2));
(void)strcpy(tempFullPath, domainName);
if ((tempFullPath[strlen(tempFullPath)-1] != '/') && (*tempFullDir != '/'))
(void)strcat(tempFullPath, "/");
(void)strcat(tempFullPath, tempFullDir);
// Allocate and connect a NetInfo handle.
tempDomain = [[NIDomain alloc] init];
status = [tempDomain setConnection:domainName readTimeout:5 writeTimeout:10 canAbort:YES mustWrite:YES];
if (status == NI_OK) {
tempHandle = [tempDomain getDomainHandle];
if (tempHandle == NULL) {
status = [tempDomain lastError];
}
}
if (status != NI_OK) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Connecting to", "Button:OK", NULL, NULL, domainName, status);
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
[tempDomain free];
return nil;
}
// Locate the directory.
status = ni_pathsearch(tempHandle, &tempDir, tempFullDir);
// If the paths differ, check the new name and path for an existing
// entry with that name and path. If it exists, display an alert and
// ask whether to overwrite. If no, exit with nil. OtherWise, overwrite
// the old entry.
if (strcmp(tempFullPath, fullPath?fullPath:"") && (status == NI_OK)) {
oldInstance = 0;
choice = ALERT1("Alert:NetInfo Exists", "Message:Overwrite Existing Entry?",
"Button:Yes", "Button:Cancel", NULL, tempFullPath);
if (choice == NX_ALERTALTERNATE) {
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
[tempDomain free];
return nil;
}
}
if (![self authenticate:domainName forDomain:tempDomain andUser:user]) {
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
[tempDomain free];
return nil;
}
// If the check for an existing entry was successful, overwrite.
// Otherwise create a new entry.
if (status == NI_OK) {
// Just in case someone else wrote to this directory before us!
if (oldInstance && (oldInstance != tempDir.nii_instance)) {
choice = ALERT1("Alert:Warning", "Message:old instance",
"Button:Yes", "Button:No", NULL, tempFullPath);
switch (choice) {
case NX_ALERTDEFAULT:
break;
case NX_ALERTALTERNATE:
default:
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
[tempDomain free];
return nil;
}
}
do {
status = ni_write([tempDomain getDomainHandle], &tempDir, properties);
if (status != NI_OK) {
choice = ALERT_STATUS("Alert:NetInfo Error", "Message:Writing directory",
"Button:Reauthenticate", "Button:Cancel", NULL, tempFullPath, status);
switch (choice) {
case NX_ALERTDEFAULT:
[(AuthEntry *)[authHash valueForKey:[tempDomain getFullPath]] free];
[authHash removeKey:[tempDomain getFullPath]];
if ([self authenticate:domainName forDomain:tempDomain andUser:user])
break;
case NX_ALERTALTERNATE:
default:
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
[tempDomain free];
return nil;
}
}
} while (status != NI_OK);
directory = tempDir;
} else {
// Get the root directory.
if ((status = ni_pathsearch(tempHandle, &tempDir, dirName)) != NI_OK) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Reading directory", "Button:OK", NULL, NULL,
dirName, status);
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
[tempDomain free];
return nil;
}
// Create the new directory.
do {
status = ni_create(tempHandle, &tempDir, properties, &directory, NI_INDEX_NULL);
if (status != NI_OK) {
choice = ALERT_STATUS("Alert:NetInfo Error", "Message:Creating directory",
"Button:Reauthenticate", "Button:Cancel", NULL, tempFullPath, status);
switch (choice) {
case NX_ALERTDEFAULT:
[(AuthEntry *)[authHash valueForKey:[tempDomain getFullPath]] free];
[authHash removeKey:[tempDomain getFullPath]];
if ([self authenticate:domainName forDomain:tempDomain andUser:user])
break;
case NX_ALERTALTERNATE:
default:
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
[tempDomain free];
return nil;
}
}
} while (status != NI_OK);
}
// Update some variables.
ni_name_free(&fullPath);
fullPath = ni_name_dup(tempFullPath);
if (domain)
[domain free];
domain = tempDomain;
NXZoneFree([self zone], tempFullPath);
NXZoneFree([self zone], tempFullDir);
// If the names differed and baseName was set, remove the old entry.
if (baseName && strcmp(tempName, baseName)) {
tempFullDir = NXZoneMalloc([self zone], sizeof(char)*(strlen(dirName)+strlen(baseName)+2));
(void)strcpy(tempFullDir, dirName);
if ((tempFullDir[strlen(tempFullDir)-1] != '/') && (*baseName != '/'))
(void)strcat(tempFullDir, "/");
(void)strcat(tempFullDir, baseName);
// Locate the directory.
status = ni_pathsearch(tempHandle, &tempDir, tempFullDir);
if (status == NI_OK)
status = ni_pathsearch(tempHandle, &tempRoot, dirName);
if (status == NI_OK)
status = ni_destroy(tempHandle, &tempRoot, tempDir);
if (status != NI_OK) {
ALERT_STATUS("Alert:NetInfo Warning", "Message:Removing directory", "Button:OK", NULL, NULL,
dirName, status);
NXZoneFree([self zone], tempFullDir);
}
NXZoneFree([self zone], tempFullDir);
}
// Update the remaining variables.
ni_name_free(&baseName);
baseName = ni_name_dup(tempName);
return self;
}
- add:(NIProperty *)p
{
[hash insertKey:[p name] value:p];
[list addObject:p];
return p;
}
//*****************************************************
// Public methods
- init:sender dom:(const char *)domPath root:(const char *)basePath dir:(const char *)dirPath errors:(BOOL)warn
{
void *handle = NULL;
int status;
ni_name fullDir;
delegate = sender;
domainName = domPath?ni_name_dup(domPath):NULL;
dirName = ni_name_dup(basePath);
baseName = dirPath?ni_name_dup(dirPath):NULL;
NI_INIT(&fullPath);
NI_INIT(&saveTitle);
NI_INIT(&user);
domain = nil;
NI_INIT(&directory);
NI_INIT(&properties);
hash = [[HashTable alloc] initKeyDesc:"*"];
list = [[List alloc] init];
if (!domPath || !dirPath)
return self;
// Allocate and connect a NetInfo handle.
domain = [[NIDomain alloc] init];
status = [domain setConnection:domPath readTimeout:5 writeTimeout:10 canAbort:YES mustWrite:YES];
if (status == NI_OK) {
handle = [domain getDomainHandle];
if (handle == NULL) {
status = [domain lastError];
}
}
if (status != NI_OK) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Connecting to", "Button:OK", NULL, NULL,
domPath, status);
[domain free];
return nil;
}
if (baseName) {
fullDir = NXZoneMalloc([self zone], sizeof(char)*(strlen(dirName)+strlen(baseName)+2));
(void)strcpy(fullDir, dirName);
if ((fullDir[strlen(fullDir)-1] != '/') && (baseName[0] != '/'))
(void)strcat(fullDir, "/");
(void)strcat(fullDir, baseName);
} else {
fullDir = NXZoneMalloc([self zone], sizeof(char)*strlen(dirName));
(void)strcpy(fullDir, dirName);
}
// Locate the directory.
status = ni_pathsearch(handle, &directory, fullDir);
if ((status != NI_OK) && warn) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Locating directory", "Button:OK", NULL, NULL,
fullDir, status);
NXZoneFree([self zone], fullDir);
[domain free];
return nil;
}
fullPath = NXZoneMalloc([self zone], sizeof(char)*(strlen(domainName)+strlen(fullDir)+2));
(void)strcpy(fullPath, domainName);
if ((fullDir[strlen(fullPath)-1] != '/') && (*fullDir != '/'))
(void)strcat(fullPath, "/");
(void)strcat(fullPath, fullDir);
if (status == NI_OK) {
// Load the existing data by reading the directory.
if ((status = ni_read(handle, &directory, &properties)) != NI_OK) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Reading directory", "Button:OK", NULL, NULL,
fullDir, status);
NXZoneFree([self zone], fullDir);
[domain free];
return nil;
}
}
NXZoneFree([self zone], fullDir);
domainName = ni_name_dup(domPath);
return self;
}
- setSaveTitle:(const char *)title
{
ni_name_free(&saveTitle);
saveTitle = ni_name_dup(title);
return self;
}
- setAuthenticationUser:(const char *)userName
{
ni_name_free(&user);
if (userName && *userName)
user = ni_name_dup(userName);
return self;
}
- save
{
if (domain)
return [self doSave];
else
return [self saveToDomain];
return nil;
}
- saveToDomain
{
NISavePanel *savePanel;
int index, status;
ni_name oldDomainName = domainName,
oldBaseName = baseName,
oldFullPath = fullPath;
ni_id oldDir = directory;
ni_property nameProp;
savePanel = [NISavePanel new];
[savePanel setDirectoryPath:dirName];
[savePanel setStartingDomainPath:domainName?domainName:"/"];
[savePanel setPanelTitle:[strings valueForStringKey:"Title:Select in NetInfo Domain"]];
[savePanel setListTitle:saveTitle?saveTitle:""];
// If we have a property 'name', set that for the modal.
// If not, ask for a new name!
index = ni_proplist_match(properties, PROPNAME, NULL);
if (index == NI_INDEX_NULL)
status = [savePanel runModalWithString:""];
else {
status = [savePanel runModalWithUneditableString:
properties.ni_proplist_val[index].nip_val.ni_namelist_val[0]];
}
if (status != NX_ALERTDEFAULT)
return nil;
domainName = ni_name_dup([savePanel domain]);
baseName = ni_name_dup([savePanel directory]);
fullPath = NULL;
NI_INIT(&directory);
// If no name was set, create a name entry an set it!
if (index == NI_INDEX_NULL) {
NI_INIT(&nameProp);
nameProp.nip_name = PROPNAME;
ni_namelist_insert(&nameProp.nip_val, [savePanel directory], NI_INDEX_NULL);
ni_proplist_insert(&properties, nameProp, 0);
ni_namelist_free(&nameProp.nip_val);
}
// Try to save to the new directory.
if ([self doSave]) {
ni_name_free(&oldDomainName);
ni_name_free(&oldBaseName);
ni_name_free(&oldFullPath);
return self;
} else {
domainName = oldDomainName;
baseName = oldBaseName;
fullPath = oldFullPath;
directory = oldDir;
return nil;
}
}
- delete
{
ni_id parent;
int status;
if (![self authenticate:domainName forDomain:domain andUser:user])
return nil;
if ((status = ni_self([domain getDomainHandle], &directory)) == NI_OK)
if ((status = ni_pathsearch([domain getDomainHandle], &parent, dirName)) == NI_OK)
if ((status = ni_destroy([domain getDomainHandle], &parent, directory)) == NI_OK) {
ni_name_free(&domainName);
[domain free];
domain = nil;
}
if (status != NI_OK) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Removing directory", "Button:OK", NULL, NULL,
dirName, status);
return nil;
}
return self;
}
- close
{
if (fullPath)
NXZoneFree([self zone], fullPath);
ni_name_free(&saveTitle);
ni_name_free(&user);
ni_name_free(&domainName);
ni_name_free(&dirName);
ni_name_free(&baseName);
if (domain)
[domain free];
ni_proplist_free(&properties);
[hash free];
[list freeObjects];
[list free];
return [super free];
}
- setDelegate:sender
{
delegate = sender;
return self;
}
- delegate
{
return delegate;
}
- (const char *)domainName
{
return domainName;
}
- (const char *)directory
// Return just the directory path.
{
return fullPath+strlen(domainName);
}
- (const char *)baseName
{
return baseName;
}
- (NIDomain *)domain
{
return domain;
}
- (void *)handle
{
return [domain getDomainHandle];
}
- (ni_id)directoryID
{
return directory;
}
// Methods for adding properties of specific types.
- addBool:(const char *)label outlet:obj
{
return [self add:[[NIBoolProperty alloc] init:delegate properties:&properties name:label outlet:obj]];
}
- addChar:(const char *)label outlet:obj
{
return [self add:[[NICharProperty alloc] init:delegate properties:&properties name:label outlet:obj]];
}
- addInt:(const char *)label text:tObj slider:sObj zero:(const char *)string
{
return [self add:[[NIIntProperty alloc] init:delegate properties:&properties name:label text:tObj slider:sObj
default:defaultText zero:string]];
}
- addString:(const char *)label outlet:obj
{
return [self add:[[NIStringProperty alloc] init:delegate properties:&properties name:label text:obj button:nil
mode:NIPT_NONE path:NULL title:NULL]];
}
- addString:(const char *)label text:tObj button:bObj mode:(int)m path:(const char *)p
title:(const char *)tString
{
return [self add:[[NIStringProperty alloc] init:delegate properties:&properties name:label text:tObj button:bObj
mode:m path:p title:tString]];
}
- addBrowser:(const char *)label browser:bObj text:tObj add:aObj remove:dObj
{
return [self add:[[NIBrowserProperty alloc] init:delegate properties:&properties name:label text:tObj browser:bObj
mode:NIPT_NONE path:NULL
add:aObj remove:dObj title:NULL]];
}
- addBrowser:(const char *)label browser:bObj add:aObj remove:dObj
mode:(int)m path:(const char *)p title:(const char *)tString
{
return [self add:[[NIBrowserProperty alloc] init:delegate properties:&properties name:label
text:nil browser:bObj mode:m path:p
add:aObj remove:dObj title:tString]];
}
- addPopup:(const char *)label outlet:obj
{
return [self add:[[NIPopupProperty alloc] init:delegate properties:&properties name:label
outlet:obj default:defaultText]];
}
- addPopup:(const char *)label outlet:obj default:(const char *)defStrng
{
return [self add:[[NIPopupProperty alloc] init:delegate properties:&properties name:label
outlet:obj default:defStrng]];
}
- addCall:(const char *)label displayAction:(SEL)action
{
return [self add:[[NICallProperty alloc] init:delegate properties:&properties
name:label displayAction:action]];
}
- addProperty:(const char *)label
{
return [self add:[[NICallProperty alloc] init:delegate properties:&properties
name:label displayAction:(SEL)nil]];
}
// Transfere from NetInfo to the GUI
- scan
{
int i;
for (i = 0; i < [list count]; i++)
[[list objectAt:i] display];
return self;
}
// redisplay previously read values
- reset
{
int status;
ni_name fullDir;
ni_id tdir;
ni_proplist props;
NI_INIT(&tdir);
fullDir = NXZoneMalloc([self zone], sizeof(char)*(strlen(dirName)+strlen(baseName)+2));
(void)strcpy(fullDir, dirName);
if ((fullDir[strlen(fullDir)-1] != '/') && (*baseName != '/'))
(void)strcat(fullDir, "/");
(void)strcat(fullDir, baseName);
if ((status = ni_pathsearch([domain getDomainHandle], &tdir, fullDir)) != NI_OK) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Reading directory", "Button:OK", NULL, NULL,
fullDir, status);
NXZoneFree([self zone], fullDir);
return nil;
}
NI_INIT(&props);
if ((status = ni_read([domain getDomainHandle], &tdir, &props)) != NI_OK) {
ALERT_STATUS("Alert:NetInfo Error", "Message:Reading directory", "Button:OK", NULL, NULL,
fullDir, status);
NXZoneFree([self zone], fullDir);
return nil;
}
NXZoneFree([self zone], fullDir);
ni_proplist_free(&properties);
properties = props;
directory = tdir;
return [self scan];
}
// Return the NetInfo property of the given label.
- property:(const char *)label
{
return [hash valueForKey:label];
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.