/* ResolverWindowController.m created by andrew on Sat 05-Jul-1997 */


#import "ResolverWindowController.h"


#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <NIAccess/NIAccess.h>
#import <NIInterface/NIInterface.h>
// for geteuid
#import <System/bsd/sys/types.h>
#import <System/bsd/unistd.h>


@interface ResolverWindowController (Private)
- (NIDirectory *)_resolverDirectoryCreateIfNeeded:(BOOL)createIfNeeded;
- (NIDirectory *)_resolverDirectory;
@end


@implementation ResolverWindowController

static NSString *netinfoAuthenticationBugString = nil;
static NSString *savePartialAttributesString = nil;
static NSString *authenticationFailedString = nil;
static NSString *canNotSaveChangesString = nil;
static NSString *nameserverIPString = nil;
static NSString *canNotCreateNIDirectoryString = nil;
static NSString *canNotCreateParticularNIDirectoryString = nil;
static NSString *saveString = nil;
static NSString *cancelString = nil;

static NSMutableArray *windowControllers = nil;

+ (void)initialize;
{
    static BOOL initialized = NO;
    
    [super initialize];

    if (initialized)
        return;

    initialized = YES;

    windowControllers = [[NSMutableArray alloc] init];

    netinfoAuthenticationBugString = NSLocalizedString (@"NetInfoAuthenticationBug", user has hit up against the netinfo authentication crasher);
    savePartialAttributesString = NSLocalizedString (@"SavePartialAttributes", user has defined only a subset of the attributes - which can be bad);
    authenticationFailedString = NSLocalizedString (@"AuthenticationFailed", user entered an incorrect password for the NetInfo domain);
    canNotSaveChangesString = NSLocalizedString (@"CanNotSaveChangesWithReason", unable to save changes for some reason);
    nameserverIPString = NSLocalizedString (@"NameserverIPAddress", prompt to enter a nameserver IP);
    canNotCreateNIDirectoryString = NSLocalizedString (@"CanNotCreateNIDirectory", unable to create a NetInfo directory);
    canNotCreateParticularNIDirectoryString = NSLocalizedString (@"CanNotCreateParticularNIDirectory", unable to create a specific NetInfo directory);
    saveString = NSLocalizedString (@"Save", save prompt);
    cancelString = NSLocalizedString (@"Cancel", cancel prompt);
}

+ resolverWindowControllerForDomain:(NIDomain *)aDomain;
{
    NSEnumerator *enumerator;
    ResolverWindowController *controller;

    enumerator = [windowControllers objectEnumerator];
    while ((controller = [enumerator nextObject]))
        if ([[controller domain] isEqual:aDomain])
            return controller;
    
    controller = [[self alloc] initWithDomain:aDomain];
    [controller release];

    return controller;
}

+ resolverWindowControllerForLocalDomain;
{
    return [self resolverWindowControllerForDomain:[NIDomain localDomain]];
}

+ resolverWindowControllerForRootDomain;
{
    return [self resolverWindowControllerForDomain:[NIDomain rootDomain]];
}

+ (BOOL)applicationShouldTerminate:(NSApplication *)application;
{
    NSEnumerator *enumerator;
    ResolverWindowController *controller;

    enumerator = [windowControllers objectEnumerator];
    while ((controller = [enumerator nextObject]))
        if (![controller shouldClose])
            return NO;

    enumerator = [windowControllers objectEnumerator];
    while ((controller = [enumerator nextObject]))
        [controller close];
    
    return YES;
}

- init;
{
    return [self initWithDomain:[NIDomain localDomain]];
}

- initWithDomain:(NIDomain *)aDomain;
{
    [super initLazily];
    domain = [aDomain retain];
    nameservers = [[NSMutableArray alloc] initWithCapacity:2];
    [windowControllers addObject:self];

    return self;
}

- (void)dealloc;
{
    [domain release];
    [nameservers release];
    [super dealloc];
}

- (NIDomain *)domain;
{
    return domain;
}

- (void)revertChanges;
{
    NIDirectory *resolverDirectory;

    [super revertChanges];
    
    resolverDirectory = [self _resolverDirectory];
    if (resolverDirectory) {
        NSString *domainName;

        domainName = [resolverDirectory firstValueForKey:@"domain"];
        [domainField setStringValue:domainName ? domainName : @""];
        [nameservers release];
        nameservers = [[NSMutableArray alloc] initWithArray:[resolverDirectory valuesForKey:@"nameserver"]];
    } else {
        [domainField setStringValue:@""];
        [nameservers release];
        nameservers = [[NSMutableArray alloc] initWithCapacity:2];
    }
    [nameserverTable reloadData];
}

- (void)prepareToSaveChanges;
{
    NSString *domainName;
    int numAttributes;

    [super prepareToSaveChanges];
    [window makeFirstResponder:window];

    // try to ensure that both attributes (or neither) are set
    numAttributes = 0;

    domainName = [domainField stringValue];
    if ([domainName length] > 0)
        numAttributes++;

    if ([nameservers count] > 0)
        numAttributes++;
#warning AFA: it would be nice to validate the actual IP addresses here, and ask the user to confirm them if we are unable to reach any

    if ((numAttributes != 2) && (numAttributes != 0)) {
        int choice;

        choice = NSRunAlertPanel (nil, savePartialAttributesString, saveString, cancelString, nil);
        if (choice != NSAlertDefaultReturn)
            [NSException raise:@"DidNotSave" format:@"User cancelled save operation"];
    }

    // see if we can avoid the authentication panel
    if (!authenticated) {
        // if the user is editing a domain on the current host and they are root, no need to authenticate
        if ([[[domain currentServer] host] isEqual:[NSHost currentHost]])
            authenticated = (geteuid() == 0);
    }
    
    // authenticate user
    if (!authenticated) {
        NIAuthenticationPanel *authenticationPanel;

#warning AFA: workaround for NetInfo bug: if the local domain is also the root domain, the app will croak when the user hits the "authenticate" button (on OPENSTEP the root must also have no password?)
        if ([[[[NSBundle mainBundle] infoDictionary] objectForKey:@"useNetInfoAuthenticationHack"] isEqualToString:@"YES"]) {
            NSHost *currentHost;

            currentHost = [NSHost currentHost];
            if (([[[domain currentServer] host] isEqual:currentHost]) && ([[[[NIDomain rootDomain] currentServer] host] isEqual:currentHost])) {
                NSRunAlertPanel(nil, netinfoAuthenticationBugString, nil, nil, nil);
                [NSException raise:@"NetInfoAuthenticationBug" format:netinfoAuthenticationBugString];
            }
        }

        authenticationPanel = [NIAuthenticationPanel authenticationPanel];
        authenticated = [authenticationPanel runModalForDomain:domain];
    }
    
    if (!authenticated) {
        NSRunAlertPanel (nil, authenticationFailedString, nil, nil, nil);
        [NSException raise:@"DidNotSave" format:authenticationFailedString];
    }
}

- (void)saveChanges;
{
    NIDirectory *resolverDirectory;
    NSString *domainName;

    [super saveChanges];

    // get the resolver directory - create if necessary
    NS_DURING {
        resolverDirectory = [self _resolverDirectoryCreateIfNeeded:YES];
    } NS_HANDLER {
        NSRunAlertPanel (nil, canNotSaveChangesString, nil, nil, nil, [localException reason]);
        [NSException raise:@"DidNotSave" format:@"Error finding or creating resolver directory"];
    } NS_ENDHANDLER;

    // save the domain
    domainName = [domainField stringValue];
    if ([domainName length] > 0)
        [resolverDirectory replaceValues:[NSArray arrayWithObject:domainName] forKey:@"domain"];
    else
        [resolverDirectory removeKey:@"domain"];

    // save the DNS servers
    if ([nameservers count] > 0)
        [resolverDirectory replaceValues:nameservers forKey:@"nameserver"];
    else
        [resolverDirectory removeKey:@"nameserver"];

    // if the resolver directory is empty, it can be bad to leave it around - destroy it
    // (keys == 1 means we're empty - the last one must [well, should] be the name key)
    if ((![resolverDirectory hasSubdirectories]) && ([[resolverDirectory allKeys] count] == 1))
        [resolverDirectory destroy];
}

- (void)newNameServer:sender;
{
    int count;

    count = [nameservers count];
    if (count == 0)
        [nameservers addObject:nameserverIPString];
    else
        [nameservers addObject:[[nameservers objectAtIndex:count - 1] copy]];
    [nameserverTable reloadData];
    [nameserverTable selectRow:count byExtendingSelection:NO];
    [nameserverTable editColumn:0 row:count withEvent:nil select:YES];
    
    [window setDocumentEdited:YES];
}

- (void)deleteNameServer:sender;
{
    NSEnumerator *enumerator;
    NSMutableArray *deletedServers;
    NSString *server;
    NSNumber *row;

    deletedServers = [NSMutableArray arrayWithCapacity:2];
    
    enumerator = [nameserverTable selectedRowEnumerator];
    while ((row = [enumerator nextObject]))
        [deletedServers addObject:[nameservers objectAtIndex:[row intValue]]];

    enumerator = [deletedServers objectEnumerator];
    while ((server = [enumerator nextObject]))
        [nameservers removeObjectIdenticalTo:server];

    [nameserverTable reloadData];
    
    [window setDocumentEdited:YES];
}

@end


@implementation ResolverWindowController (WindowControllerSubclass)

- (void)load;
{
    [super load];
    [window setTitle:[domain name]];
    [nameserverTable setDataSource:self];
    [self revert:nil];
}

@end


@implementation ResolverWindowController (NSTableViewDataSource)

- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
{
    return [nameservers count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row;
{
    return [nameservers objectAtIndex:row];
}

- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row;
{
    NSString *oldNameserver;

    oldNameserver = [nameservers objectAtIndex:row];
    if ([oldNameserver isEqualToString:object])
        return;
    [window setDocumentEdited:YES];
    [nameservers replaceObjectAtIndex:row withObject:object];
}

@end


@implementation ResolverWindowController (Private)

- (NIDirectory *)_resolverDirectoryCreateIfNeeded:(BOOL)createIfNeeded;
{
    NIDirectory *resolverDirectory;

    resolverDirectory = [domain directoryWithPath:@"/locations/resolver"];
    if (!resolverDirectory && createIfNeeded) {
        NIDirectory *rootDirectory;

        rootDirectory = [domain directoryWithPath:@"/"];
        if (![rootDirectory createPath:@"locations/resolver"])
            [NSException raise:canNotCreateNIDirectoryString format:canNotCreateParticularNIDirectoryString, @"/locations/resolver"];
        resolverDirectory = [domain directoryWithPath:@"/locations/resolver"];
    }

    return resolverDirectory;
}

- (NIDirectory *)_resolverDirectory;
{
    NIDirectory *resolverDirectory = nil;
    
    NS_DURING {
        resolverDirectory = [self _resolverDirectoryCreateIfNeeded:NO];
    } NS_HANDLER {
        NSRunAlertPanel (nil, [localException reason], nil, nil, nil);
    } NS_ENDHANDLER;

    return resolverDirectory;
}

@end


@implementation ResolverWindowController (NSWindowDelegate)

- (void)windowWillClose:(NSNotification *)notification;
{
    [super windowWillClose:notification];
    [[self retain] autorelease];		// ensure we aren't freed by next line
    [windowControllers removeObject:self];
}

@end
