/** Enterprise Control Configuration and Logging Copyright (C) 2012 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Date: Febrary 2010 Originally developed from 1996 to 2012 by Brainstorm, and donated to the FSF. This file is part of the GNUstep project. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. */ #import #import #import #import #import #import #import #import #import "EcUserDefaults.h" #import "EcHost.h" static NSRecursiveLock *lock = nil; static NSMutableDictionary *fromWellKnown = nil; static NSMutableDictionary *toWellKnown = nil; static NSString *controlName = nil; @implementation NSHost (EcHost) typedef NSHost *(*hwnType)(Class c, SEL s, NSString *n); static IMP hwnImp; // Original +hostWithName: implementation /* This method is used to replace +[NSHost hostWithName:] * First it checks to see if the supplied name is a well known name aliasing * another. Then it performs the host lookup using the original method. */ + (NSHost*) _hostWithName: (NSString*)aName { NSHost *found = nil; if (YES == [aName isKindOfClass: [NSString class]]) { NSString *rep; [lock lock]; if (nil != (rep = [fromWellKnown objectForKey: aName])) { aName = [[rep retain] autorelease]; } [lock unlock]; found = ((hwnType)hwnImp)(self, @selector(hostWithName:), aName); } return found; } + (void) _EcHostSetup { if (nil == lock) { if (YES == [NSThread isMainThread]) { NSUserDefaults *defs; NSString *name; NSHost *host; NSRecursiveLock *l; /* Create the lock locked ... until it is unlocked all other * threads are waiting for this method to complete, and we * can do what we like. */ l = [NSRecursiveLock new]; [l lock]; lock = l; fromWellKnown = [NSMutableDictionary alloc]; toWellKnown = [NSMutableDictionary alloc]; /* We need to override the class method of NSHost so that it * knows about well known host names. */ Method m; IMP r; m = class_getClassMethod(self, @selector(_hostWithName:)); r = method_getImplementation(m); m = class_getClassMethod([NSHost class], @selector(hostWithName:)); hwnImp = method_setImplementation(m, r); /* Perform initial setup of control host and current host * from user defaults system. */ defs = [NSUserDefaults prefixedDefaults]; if (nil == defs) { defs = [NSUserDefaults userDefaultsWithPrefix: nil]; } name = [defs stringForKey: @"ControlHost"]; if (nil != name) { controlName = [name copy]; /* Use mapping from domain name to well known name. */ name = [defs stringForKey: @"ControlDomain"]; } else if (nil != (name = [defs stringForKey: @"ControlDomain"])) { /* Use domain name as the known name. */ controlName = [name copy]; } if (nil != name) { host = ((hwnType)hwnImp)(self, @selector(hostWithName:), name); if (nil == host) { /* No such host ... set up name mapping in case * the host becomes available later. */ [toWellKnown setObject: controlName forKey: name]; [fromWellKnown setObject: name forKey: controlName]; } else { [host setWellKnownName: controlName]; } } host = [self currentHost]; name = [defs stringForKey: @"CurrentHost"]; if (nil == name) { /* If the current host is the control host, we may have the * well known name set already, but if we don't this method * will select an arbitrary name that we can use. */ name = [host wellKnownName]; } [host setWellKnownName: name]; [lock unlock]; } else { NSArray *modes; modes = [NSArray arrayWithObject: NSDefaultRunLoopMode]; [self performSelectorOnMainThread: _cmd withObject: nil waitUntilDone: YES modes: modes]; } } } + (NSString*) controlWellKnownName { if (nil == lock) [self _EcHostSetup]; return controlName; } + (NSHost*) hostWithWellKnownName: (NSString*)aName { NSHost *found = nil; if (nil == lock) [self _EcHostSetup]; if (YES == [aName isKindOfClass: [NSString class]]) { [lock lock]; aName = [[[fromWellKnown objectForKey: aName] retain] autorelease]; [lock unlock]; if (nil != aName) { found = ((hwnType)hwnImp)(self, @selector(hostWithName:), aName); } } return found; } + (void) setWellKnownNames: (NSDictionary*)map { if (nil == lock) [self _EcHostSetup]; if ([map isKindOfClass: [NSDictionary class]]) { NSEnumerator *e; NSString *k; e = [map keyEnumerator]; while (nil != (k = [e nextObject])) { NSString *v = [map objectForKey: k]; if ([k isKindOfClass: [NSString class]] && [v isKindOfClass: [NSString class]]) { NSHost *h; h = ((hwnType)hwnImp)(self, @selector(hostWithName:), k); if (nil == h) { NSEnumerator *e; NSString *name; /* No such host ... set up name mapping in case * the host becomes available later. */ [lock lock]; /* Remove any existing names which map to this new * well known name. */ e = [[toWellKnown allKeys] objectEnumerator]; while (nil != (name = [e nextObject])) { NSString *wellKnown; wellKnown = [toWellKnown objectForKey: name]; if ([wellKnown isEqualToString: v]) { [toWellKnown removeObjectForKey: name]; } } /* Set up the specified mappings to and from the new * well known name and its normal host name. */ [toWellKnown setObject: v forKey: k]; [fromWellKnown setObject: k forKey: v]; [lock unlock]; } else { /* We have found a host with the specified names ... * so set the well known name for it. */ [h setWellKnownName: v]; } } } } } - (void) setWellKnownName: (NSString*)aName { if (nil == lock) [[self class] _EcHostSetup]; if ([aName isKindOfClass: [NSString class]]) { NSEnumerator *e; NSString *name; [lock lock]; /* Set a mapping to this host from the well known name. */ name = [self name]; if (nil == name) { name = [self address]; } [fromWellKnown setObject: name forKey: aName]; /* Remove any old mappings to this well known name. */ e = [[toWellKnown allKeys] objectEnumerator]; while (nil != (name = [e nextObject])) { if ([[toWellKnown objectForKey: name] isEqualToString: aName]) { [toWellKnown removeObjectForKey: name]; } } /* Add mappings to the well known name from any of our names. */ e = [[self names] objectEnumerator]; while (nil != (name = [e nextObject])) { [toWellKnown setObject: aName forKey: name]; } /* Add mappings to the well known name from any of our addresses. */ e = [[self addresses] objectEnumerator]; while (nil != (name = [e nextObject])) { [toWellKnown setObject: aName forKey: name]; } [lock unlock]; } } - (NSString*) wellKnownName { NSString *found; if (nil == lock) [[self class] _EcHostSetup]; [lock lock]; found = [[[toWellKnown objectForKey: [self name]] retain] autorelease]; if (nil == found) { NSEnumerator *e; NSString *name; e = [[self names] objectEnumerator]; while (nil == found && nil != (name = [e nextObject])) { found = [[[toWellKnown objectForKey: name] retain] autorelease]; } if (nil == found) { e = [[self addresses] objectEnumerator]; while (nil == found && nil != (name = [e nextObject])) { found = [[[toWellKnown objectForKey: name] retain] autorelease]; } } } [lock unlock]; if (nil == found) { found = [self name]; if (nil == found) { found = [self address]; } } return found; } @end