/*
* Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*!
* @header PathRecord
*/
#import <unistd.h>
#import <DirectoryService/DirServicesConst.h>
#import "PathRecord.h"
#import "DSoDirectory.h"
#import "DSoNode.h"
#import "DSoRecord.h"
#import "DSoUser.h"
#import "DSoException.h"
@implementation PathRecord
// ----------------------------------------------------------------------------
// Initialization / teardown
#pragma mark ******** Initialization / teardown ********
- init
{
[super init];
_record = nil;
return self;
}
- initWithRecord:(DSoRecord*)inRec
{
[self init];
_record = [inRec retain];
return self;
}
- (void)dealloc
{
[_record release];
[super dealloc];
}
// ----------------------------------------------------------------------------
// PathItemProtocol implementations
#pragma mark ******** PathItemProtocol implementations ********
- (tDirStatus) appendKey:(NSString*)inKey withValues:(NSArray*)inValues
{
return [self modify:ATTR_APPEND withKey:inKey withValues:inValues];
}
- (tDirStatus) authenticateName:(NSString*)inUsername withPassword:(NSString*)inPassword authOnly:(BOOL)inAuthOnly
{
tDirStatus status = eDSNoErr;
status = [[_record node] authenticateName:inUsername withPassword:inPassword authOnly:inAuthOnly];
if (status == eDSNoErr && inAuthOnly == NO)
{
// Since we are now authenticated, we need to re-open this record.
DSoRecord *newRec = nil;
newRec = [[_record node] findRecord:[_record getName] ofType:[_record getType]];
[_record release];
_record = [newRec retain];
}
return status;
}
- (NSString*) name
{
return [_record getName];
}
- (tDirStatus) list:(NSString*)inPath key:(NSString*)inKey
{
return eDSNoErr;
}
- (PathItem*) cd:(NSString*)dest
{
return nil;
}
- (tDirStatus) createKey:(NSString*)inKey withValues:(NSArray*)inValues
{
return [self modify:ATTR_CREATE withKey:inKey withValues:inValues];
}
- (tDirStatus) deleteItem
{
tDirStatus nError = eDSNoErr;
NS_DURING
[_record removeRecord];
NS_HANDLER
if ([localException isKindOfClass:[DSoException class]])
nError = [(DSoException*)localException status];
else
[localException raise];
NS_ENDHANDLER
return nError;
}
- (tDirStatus) delete:(NSString*)inKey atIndex:(int)index plistPath:(NSString*)inPlistPath values:(NSArray*)inValues
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *attribs = nil;
NSMutableArray *values = nil;
NSString *value = nil;
NSMutableDictionary *plist = nil;
NSArray *pathElements = nil;
NSEnumerator *pathEnum = nil;
NSString *currentPathElement = nil;
NSString *previousPathElement = nil;
id currentElement = nil;
id previousElement = nil;
NSPropertyListFormat format = NSPropertyListXMLFormat_v1_0;
BOOL changed = NO;
tDirStatus status = eDSNoErr;
NS_DURING
attribs = [self getDictionary:[NSArray arrayWithObject:inKey]];
if([attribs count] == 0)
{
printf("Invalid key.\n");
NS_VALUERETURN(eDSEmptyAttribute,tDirStatus);
}
NSString *attrib;
if (!([inKey hasPrefix:@kDSStdAttrTypePrefix] || [inKey hasPrefix:@kDSNativeAttrTypePrefix]))
{
attrib = [@kDSStdAttrTypePrefix stringByAppendingString:inKey];
if([attribs objectForKey:attrib] != nil)
inKey = attrib;
attrib = [@kDSNativeAttrTypePrefix stringByAppendingString:inKey];
if([attribs objectForKey:attrib] != nil)
inKey = attrib;
}
values = [[[attribs objectForKey:inKey] mutableCopy] autorelease];
if([values count] < 1)
{
printf("There is no value for attribute NS_VALUERETURN(eDSEmptyAttribute,tDirStatus);
}
else if(index >= [values count])
{
printf("Value index out of range\n");
NS_VALUERETURN(eDSIndexOutOfRange,tDirStatus);
}
else
{
value = [values objectAtIndex:index];
}
plist = [NSPropertyListSerialization propertyListFromData:[value dataUsingEncoding:NSUTF8StringEncoding]
mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:nil];
pathElements = [inPlistPath componentsSeparatedByString:@":"];
pathEnum = [pathElements objectEnumerator];
currentElement = plist;
while (currentElement != nil && ((currentPathElement = (NSString*)[pathEnum nextObject]) != nil))
{
previousPathElement = currentPathElement;
previousElement = currentElement;
if ([currentElement isKindOfClass:[NSDictionary class]])
{
currentElement = [currentElement objectForKey:currentPathElement];
}
else if([currentElement isKindOfClass:[NSArray class]])
{
NSString* intString = [[NSString alloc] initWithFormat:@"
if([currentPathElement intValue] >= [currentElement count] || ![currentPathElement isEqualToString:intString])
{
break; // index out of range
}
else
{
currentElement = [currentElement objectAtIndex:[currentPathElement intValue]];
}
[intString release];
}
else
{
break; // not a valid path
}
}
inValues = [inValues sortedArrayUsingSelector:@selector(compare:)];
int c;
for(c = [inValues count] - 1; c >= 0; c--)
{
if (currentPathElement == nil && currentElement != nil)
{
// found something
if([currentElement isKindOfClass:[NSDictionary class]])
{
[currentElement removeObjectForKey:[inValues objectAtIndex:c]];
changed = YES;
}
else if([currentElement isKindOfClass:[NSArray class]])
{
NSString* intString = [[NSString alloc] initWithFormat:@" if(![[inValues objectAtIndex:c] isEqualToString:intString])
{
printf("Invalid index }
else if([[inValues objectAtIndex:c] intValue] >= [currentElement count])
{
printf("Index out of range\n");
}
else
{
[currentElement removeObjectAtIndex:[[inValues objectAtIndex:c] intValue]];
changed = YES;
}
[intString release];
}
}
else
{
// bogus path
printf("No such plist path: NS_VALUERETURN(eDSUnknownMatchType, tDirStatus);
}
}
if([inValues count] == 0)
{
if(previousPathElement && previousElement)
{
if([previousElement isKindOfClass:[NSDictionary class]])
{
[previousElement removeObjectForKey:previousPathElement];
changed = YES;
}
else if([previousElement isKindOfClass:[NSArray class]])
{
NSString* intString = [[NSString alloc] initWithFormat:@" if(![previousPathElement isEqualToString:intString])
{
printf("Invalid index }
else if([previousPathElement intValue] >= [previousElement count])
{
printf("Index out of range\n");
}
else
{
[previousElement removeObjectAtIndex:[previousPathElement intValue]];
changed = YES;
}
}
}
}
if(changed)
{
NSData *data = [NSPropertyListSerialization dataFromPropertyList:plist format:format errorDescription:nil];
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[values replaceObjectAtIndex:index withObject:dataString];
[dataString release];
status = [self modify:ATTR_CREATE withKey:inKey withValues:values];
}
NS_HANDLER
[localException retain];
[pool release];
[[localException autorelease] raise];
NS_ENDHANDLER
[pool release];
return status;
}
- (tDirStatus) delete:(NSString*)inKey plistPath:(NSString*)inPlistPath values:(NSArray*)inValues
{
return [self delete:inKey atIndex:0 plistPath:inPlistPath values:inValues];
}
- (tDirStatus) create:(NSString*)inKey atIndex:(int)index plistPath:(NSString*)inPlistPath values:(NSArray*)inValues
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *attribs = nil;
NSMutableArray *values = nil;
NSString *value = nil;
NSMutableDictionary *plist = nil;
NSArray *pathElements = nil;
NSEnumerator *pathEnum = nil;
NSString *currentPathElement = nil;
NSString *previousPathElement = nil;
id currentElement = nil;
id previousElement = nil;
NSPropertyListFormat format = NSPropertyListXMLFormat_v1_0;
BOOL changed = NO;
tDirStatus status = eDSNoErr;
NS_DURING
attribs = [self getDictionary:[NSArray arrayWithObject:inKey]];
if([attribs count] == 0)
{
printf("Invalid key.\n");
}
NSString *attrib;
if (!([inKey hasPrefix:@kDSStdAttrTypePrefix] || [inKey hasPrefix:@kDSNativeAttrTypePrefix]))
{
attrib = [@kDSStdAttrTypePrefix stringByAppendingString:inKey];
if([attribs objectForKey:attrib] != nil)
inKey = attrib;
attrib = [@kDSNativeAttrTypePrefix stringByAppendingString:inKey];
if([attribs objectForKey:attrib] != nil)
inKey = attrib;
}
values = [[[attribs objectForKey:inKey] mutableCopy] autorelease];
if([values count] < 1)
{
printf("There is no value for attribute NS_VALUERETURN(eDSEmptyAttribute,tDirStatus);
}
else if(index >= [values count])
{
printf("Value index out of range\n");
NS_VALUERETURN(eDSIndexOutOfRange,tDirStatus);
}
else
{
value = [values objectAtIndex:index];
}
plist = [NSPropertyListSerialization propertyListFromData:[value dataUsingEncoding:NSUTF8StringEncoding]
mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:nil];
pathElements = [inPlistPath componentsSeparatedByString:@":"];
pathEnum = [pathElements objectEnumerator];
currentElement = plist;
while (currentElement != nil && ((currentPathElement = (NSString*)[pathEnum nextObject]) != nil))
{
previousPathElement = currentPathElement;
previousElement = currentElement;
if ([currentElement isKindOfClass:[NSDictionary class]])
{
currentElement = [currentElement objectForKey:currentPathElement];
}
else if([currentElement isKindOfClass:[NSArray class]])
{
NSString* intString = [[NSString alloc] initWithFormat:@"
if([currentPathElement intValue] > [currentElement count] || ![currentPathElement isEqualToString:intString])
{
currentPathElement = nil;
currentElement = nil;
printf("Index out of range\n");
break; // index out of range
}
else if([currentPathElement intValue] == [currentElement count])
{
currentElement = nil;
break;
}
else
{
currentElement = [currentElement objectAtIndex:[currentPathElement intValue]];
}
[intString release];
}
else
{
currentPathElement = [pathEnum nextObject];
if (currentPathElement != nil)
{
break; // not a valid path
}
}
}
id container = nil;
id key = nil;
if((currentPathElement == nil && currentElement != nil) ||
(currentPathElement != nil && previousElement != nil && [pathEnum nextObject] == nil))
{
container = previousElement;
key = previousPathElement;
}
else
{
container = nil;
key = nil;
printf("No such plist path: NS_VALUERETURN(eDSUnknownMatchType, tDirStatus);
}
if([container isKindOfClass:[NSArray class]])
{
// Adding to an array
printf("Changing an array\n");
if([inValues count] == 1)
{
//[container removeAllObjects];
//[container addObject:[inValues objectAtIndex:0]];
if([key intValue] < [container count])
[container replaceObjectAtIndex:[key intValue] withObject:[inValues objectAtIndex:0]];
else
[container addObject:[inValues objectAtIndex:0]];
}
else
{
if([key intValue] < [container count])
[container replaceObjectAtIndex:[key intValue] withObject:inValues];
else
[container addObject:inValues];
}
changed = YES;
}
else if([container isKindOfClass:[NSDictionary class]])
{
// Adding to a dictionary
printf("Changing a dictionary\n");
if([inValues count] == 1)
{
[container setObject:[inValues objectAtIndex:0] forKey:key];
}
else
{
[container setObject:inValues forKey:key];
}
changed = YES;
}
else
{
printf("No such plist path: NS_VALUERETURN(eDSUnknownMatchType, tDirStatus);
}
if(changed)
{
NSData *data = [NSPropertyListSerialization dataFromPropertyList:plist format:format errorDescription:nil];
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[values replaceObjectAtIndex:index withObject:dataString];
[dataString release];
status = [self modify:ATTR_CREATE withKey:inKey withValues:values];
}
NS_HANDLER
[localException retain];
[pool release];
[[localException autorelease] raise];
NS_ENDHANDLER
[pool release];
return status;
}
- (tDirStatus) create:(NSString*)inKey plistPath:(NSString*)inPlistPath values:(NSArray*)inValues
{
return [self create:inKey atIndex:0 plistPath:inPlistPath values:inValues];
}
- (tDirStatus) deleteKey:(NSString*)inKey withValues:(NSArray*)inValues
{
return [self modify:ATTR_DELETE withKey:inKey withValues:inValues];
}
- (tDirStatus) mergeKey:(NSString*)inKey withValues:(NSArray*)inValues
{
return [self modify:ATTR_MERGE withKey:inKey withValues:inValues];
}
- (tDirStatus) changeKey:(NSString*)inKey oldAndNewValues:(NSArray*)inValues
{
return [self modify:ATTR_CHANGE withKey:inKey withValues:inValues];
}
- (tDirStatus) changeKey:(NSString*)inKey indexAndNewValue:(NSArray*)inValues
{
return [self modify:ATTR_CHANGE_INDEX withKey:inKey withValues:inValues];
}
- (NSString*)nodeName
{
return [[_record node] getName];
}
- (NSDictionary*)getDictionary:(NSArray*)inKeys
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *attribs = nil;
NS_DURING
if (inKeys == nil || [inKeys count] == 0)
{
attribs = [_record getAllAttributesAndValues];
}
else
{
NSArray* niceKeys = prefixedAttributeKeysWithNode([_record node], inKeys);
attribs = [_record getAttributes:niceKeys];
}
NS_HANDLER
[localException retain];
[pool release];
[[localException autorelease] raise];
NS_ENDHANDLER
[attribs retain];
[pool release];
return [attribs autorelease];
}
- (tDirStatus) setPassword:(NSArray*)inParams
{
if (!strcmp([_record getType], kDSStdRecordTypeUsers))
{
// This is a user record, proceed to set the password
if ([inParams count] == 2)
[(DSoUser*)_record changePassword:[inParams objectAtIndex:0]
toNewPassword:[inParams objectAtIndex:1]];
else
{
NS_DURING
[(DSoUser*)_record setPassword:[inParams objectAtIndex:0]];
NS_HANDLER
if (DS_EXCEPTION_STATUS_IS(eDSPermissionError))
{
NSString* oldPassword = [NSString stringWithUTF8String:getpass("Permission denied. Please enter user's old password:")];
[(DSoUser*)_record changePassword:oldPassword toNewPassword:[inParams objectAtIndex:0]];
}
else
[localException raise];
NS_ENDHANDLER
}
}
else
{
// This is some other record type, we can't set a password
[DSoException raiseWithStatus:eDSInvalidRecordType];
}
return eDSNoErr;
}
// ----------------------------------------------------------------------------
// Utility methods
#pragma mark ******** Utility methods ********
- (tDirStatus) modify:(tAttrCAM)inAction withKey:(NSString*)inKey withValues:(NSArray*)inValues
{
NSString *attrib = nil;
tDirStatus nError = eDSNoErr;
tDirStatus firstError = eDSNoErr;
if ([inKey hasPrefix:@kDSStdAttrTypePrefix] || [inKey hasPrefix:@kDSNativeAttrTypePrefix])
{
attrib = inKey;
}
else
{
attrib = [@kDSStdAttrTypePrefix stringByAppendingString:inKey];
if (![[[[_record node] directory] standardAttributeTypes] containsObject:attrib])
attrib = [@kDSNativeAttrTypePrefix stringByAppendingString:inKey];
}
NS_DURING
switch (inAction)
{
case ATTR_CREATE:
[_record setAttribute:[attrib UTF8String] values:inValues];
break;
case ATTR_APPEND:
[_record addAttribute:[attrib UTF8String] values:inValues mergeValues:NO];
break;
case ATTR_MERGE:
[_record addAttribute:[attrib UTF8String] values:inValues mergeValues:YES];
break;
case ATTR_DELETE:
if (inValues == nil || [inValues count] == 0)
[_record removeAttribute:[attrib UTF8String]];
else
[_record removeAttribute:[attrib UTF8String] values:inValues];
break;
case ATTR_CHANGE:
[_record changeAttribute:[attrib UTF8String] oldValue:[inValues objectAtIndex:0]
newValue:[inValues objectAtIndex:1]];
break;
case ATTR_CHANGE_INDEX:
[_record changeAttribute:[attrib UTF8String] index:[[inValues objectAtIndex:0] intValue]
newValue:[inValues objectAtIndex:1]];
break;
}
NS_HANDLER
if ([localException isKindOfClass:[DSoException class]])
{
nError = [(DSoException*)localException status];
firstError = nError;
//printf("standard createKey status was: }
else
[localException raise];
NS_ENDHANDLER
return nError;
}
-(DSoRecord*) record
{
// ATM - give PlugInManager access to record instance
return _record;
}
@end