/*
* 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 DSoNode
*/
#import "DSoNode.h"
#import <Security/Authorization.h>
#import <unistd.h>
#import "DSoBuffer.h"
#import "DSoDataNode.h"
#import "DSoDataList.h"
#import "DSoRecord.h"
#import "DSoUser.h"
#import "DSoGroup.h"
#import "DSoException.h"
#import "DSoAttributeUtils.h"
@interface DSoNode (DSoNodePrivate)
- (void) reopen;
- (BOOL) _hasRecordsOfType:(const char*)inType;
- (NSArray*) _findRecordsOfTypes: (NSArray*)inTypes
withAttribute: (const char*)inAttrib
value: (id)inValue
matchType: (tDirPatternMatch)inMatchType
retrieveAttributes: (NSArray*)inAttribsToRetrieve
allowBinary: (BOOL)inAllowBinary;
@end
@implementation DSoNode
// ----------------------------------------------------------------------------
// ¥ DSoNode Protected Instance Methods
// ----------------------------------------------------------------------------
#pragma mark **** DSoNode Protected Instance Methods ****
// ctor and dtor are protected; instances should be created via FindNode().
- (id)init
{
[super init];
mDirectory = nil;
mNodeName = nil;
mNodeRef = 0;
mIsAuthenticated = NO;
mSupportsSetAttributeValues = YES;
mTypeList = nil;
return self;
}
- (id)initWithDir:(DSoDirectory*)inDir nodeRef:(tDirNodeReference)inNodeRef
nodeName:(NSString*)inNodeName
{
[self init];
mNodeRef = inNodeRef;
[inDir verifiedDirRef]; // Since we don't actually need to keep the ref itself, verify it on creation
mDirectory = [inDir retain];
mNodeName = [inNodeName retain];
return self;
}
- (void) dealloc
{
[mTypeList release];
[mNodeName release];
dsCloseDirNode (mNodeRef) ;
[mDirectory release];
[super dealloc];
}
- (void) finalize
{
dsCloseDirNode (mNodeRef) ;
[super finalize];
}
- (RecID) _findRecord: (NSString*)inName
ofType: (const char*)inType
{
DSoDataNode *dnName = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory string:inName] ;
DSoDataNode *dnType = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory cString:inType] ;
tRecordReference rrTemp = 0;
tDirStatus nError = eDSNoErr;
RecID retValue;
nError = dsOpenRecord (mNodeRef, [dnType dsDataNode], [dnName dsDataNode], &rrTemp);
[dnName release];
[dnType release];
if (nError)
[DSoException raiseWithStatus:nError] ;
retValue.node = self;
retValue.recordRef = rrTemp;
return retValue;
}
// ----------------------------------------------------------------------------
// ¥ DSoNode Public Instance Methods
// ----------------------------------------------------------------------------
#pragma mark **** DSoNode Public Instance Methods ****
// ---------------------------------------------------------------
// ¥ Read Methods
// ---------------------------------------------------------------
#pragma mark ** Read Methods **
- (NSDictionary*) searchAttributes:(const char*)inAttributeType allowBinary:(BOOL)allowBinary
{
DSoDataList *attr = nil;
DSoBuffer *bufAttrList = nil;
tAttributeListRef listRef = 0;
tContextData context = 0;
tDirStatus err = eDSNoErr;
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
UInt32 count = 0;
unsigned int baseSize = 1024;
@try
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
// Tell dsGetDirNodeInfo to only get the requested attribute, or all if none specified.
if (inAttributeType == nil)
attr = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory cString:kDSAttributesAll];
else
attr = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory cString:inAttributeType];
bufAttrList = [[DSoBuffer alloc] initWithDir:mDirectory bufferSize:baseSize];
do
{
err = dsGetDirNodeInfo(mNodeRef, [attr dsDataList], [bufAttrList dsDataBuffer],
FALSE, (UInt32 *)&count, (tAttributeListRef *)&listRef, (tContextData *)&context);
if (err == eDSBufferTooSmall)
{
[bufAttrList grow:[bufAttrList getBufferSize] + baseSize];
}
else if (err == eDSNoErr && count > 0)
{
NSDictionary *d = [DSoAttributeUtils getAttributesAndValuesInNode: self
fromBuffer: bufAttrList
listReference: listRef
count: count
allowBinary: allowBinary];
[attributes addEntriesFromDictionary:d];
dsCloseAttributeList(listRef);
listRef = 0;
}
}
while ( (err == eDSNoErr && context != 0) || (err == eDSBufferTooSmall));
[pool drain];
} @catch( NSException *exception ) {
[attributes release];
attributes = nil;
@throw;
} @finally {
[attr release];
[bufAttrList release];
}
if (listRef != 0)
{
dsCloseAttributeList(listRef);
listRef = 0;
}
if (err)
{
[attributes release];
[DSoException raiseWithStatus:err];
}
if ([attributes count] == 0)
{
[attributes release];
[DSoException raiseWithStatus:eDSAttributeNotFound];
}
return [attributes autorelease];
}
- (NSDictionary*) searchAttributes:(const char*)inAttributeType
{
return [self searchAttributes: inAttributeType allowBinary: NO];
}
- (NSString*) getAttributeFirstValue:(const char*)inAttributeType
{
return [self getAttributeFirstValue: inAttributeType allowBinary: NO];
}
- (id) getAttributeFirstValue:(const char *)inAttributeType allowBinary:(BOOL)allowBinary
{
NSArray *values = [self getAttribute: inAttributeType allowBinary: allowBinary];
return ([values count] ? [values objectAtIndex: 0] : nil);
}
- (NSArray*) getAttribute:(const char*)inAttributeType
{
return [self getAttribute: inAttributeType allowBinary: NO];
}
- (NSArray*) getAttribute:(const char*)inAttributeType allowBinary:(BOOL)allowBinary
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSDictionary *d = nil;
NSArray *attribute = nil;
d = [self searchAttributes: inAttributeType allowBinary: allowBinary];
attribute = [[d objectForKey:[NSString stringWithUTF8String:inAttributeType]] retain];
[pool drain];
return [attribute autorelease];
}
- (NSDictionary*) getAllAttributes
{
return [self searchAttributes: nil allowBinary: NO];
}
- (NSDictionary*) getAllAttributesAllowingBinary:(BOOL)allowBinary
{
return [self searchAttributes: nil allowBinary: allowBinary];
}
// findRecordTypes
//
// First check kDSNAttrRecordType attribute in dsGetDirNodeInfo. If that is not
// available fall back to the legacy discovery method described below.
//
// For each Record type in our master list, call dsGetRecordList with just big enough
// of a buffer to obtain at least one record of that type.
// If at least one record is found, then that type is contained in the node and is added
// to the list of types to return.
// Using a small buffer, means that the api call will return quicker, because it can't
// return as much. We thrown away any context data.
//
// Right now we are making some assumptions about the fact that one record will always
// fit into the buffer size given, and that if the count found is zero, then context
// will be null. This also will not handle the case of a eDSBufferTooSmall error.
- (NSArray*) findRecordTypes
{
NSMutableArray *setOfTypes = nil;
@try
{
@try
{
setOfTypes = [[[self getAttribute:kDSNAttrRecordType] mutableCopy] autorelease];
} @catch( NSException *exception ) {
}
if ([setOfTypes count] == 0)
{
NSString *iterType = nil;
unsigned long iterCount = 0;
unsigned long limitCount = 0;
setOfTypes = [NSMutableArray array];
if (mTypeList == nil)
mTypeList = [[mDirectory standardRecordTypes] retain];
limitCount = [mTypeList count];
for (iterCount = 0 ; iterCount < limitCount; iterCount++)
{
iterType = [mTypeList objectAtIndex:iterCount];
if ([self _hasRecordsOfType:[iterType UTF8String]])
[setOfTypes addObject:iterType];
}
}
// Alphabetize the list.
[setOfTypes sortUsingSelector:@selector(caseInsensitiveCompare:)];
} @catch( NSException *exception ) {
}
return setOfTypes;
}
- (BOOL) hasRecordsOfType:(const char*)inType
{
NSString* dsType = [NSString stringWithUTF8String:inType];
BOOL bHasType = NO;
@try
{
if ([dsType hasPrefix:@kDSStdRecordTypePrefix])
{
bHasType = [[self getAttribute:kDSNAttrRecordType] containsObject:dsType];
}
} @catch( NSException *exception ) {
// don't worry about the exception
}
if (!bHasType)
{
bHasType = [self _hasRecordsOfType:inType];
}
return bHasType;
}
- (NSArray*) findRecordNames:(NSString*)inName andAttributes:(NSArray*)inAttributes ofType:(const char*)inType matchType:(tDirPatternMatch)inMatchType
{
if (inAttributes != nil)
{
if ([inAttributes containsObject:@kDSNAttrRecordName] == NO
&& [inAttributes containsObject:@kDSAttributesAll] == NO
&& [inAttributes containsObject:@kDSAttributesStandardAll] == NO)
inAttributes = [inAttributes arrayByAddingObject:@kDSNAttrRecordName];
}
return [self _findRecordsOfTypes: [NSArray arrayWithObject:[NSString stringWithUTF8String:inType]]
withAttribute: nil
value: inName
matchType: inMatchType
retrieveAttributes: inAttributes
allowBinary: NO];
}
- (NSArray*) findRecordNames:(NSString*)inName ofType:(const char*)inType matchType:(tDirPatternMatch)inMatchType
{
return [self _findRecordsOfTypes: [NSArray arrayWithObject:[NSString stringWithUTF8String:inType]]
withAttribute: nil
value: inName
matchType: inMatchType
retrieveAttributes: nil
allowBinary: NO];
}
- (NSArray*) findRecordNamesOfTypes:(NSArray*)inTypes withAttribute:(const char*)inAttrib value:(id)inValue matchType:(tDirPatternMatch)inMatchType
{
return [self _findRecordsOfTypes: inTypes
withAttribute: inAttrib
value: inValue
matchType: inMatchType
retrieveAttributes: nil
allowBinary: NO];
}
- (NSArray*) findRecordsOfTypes:(NSArray*)inTypes withAttribute:(const char*)inAttrib value:(id)inValue matchType:(tDirPatternMatch)inMatchType allowBinary:(BOOL)inAllowBinary
{
return [self _findRecordsOfTypes: inTypes
withAttribute: inAttrib
value: inValue
matchType: inMatchType
retrieveAttributes: [NSArray arrayWithObject:@kDSAttributesAll]
allowBinary: inAllowBinary];
}
- (NSArray*) findRecordsOfTypes:(NSArray*)inTypes withAttribute:(const char*)inAttrib value:(id)inValue matchType:(tDirPatternMatch)inMatchType
{
return [self _findRecordsOfTypes: inTypes
withAttribute: inAttrib
value: inValue
matchType: inMatchType
retrieveAttributes: [NSArray arrayWithObject:@kDSAttributesAll]
allowBinary: NO];
}
- (NSArray*) findRecordsOfTypes:(NSArray*)inTypes withAttribute:(const char*)inAttrib value:(id)inValue matchType:(tDirPatternMatch)inMatchType retrieveAttributes:(NSArray*)inAttribsToRetrieve allowBinary:(BOOL)inAllowBinary
{
return [self _findRecordsOfTypes: inTypes
withAttribute: inAttrib
value: inValue
matchType: inMatchType
retrieveAttributes: inAttribsToRetrieve
allowBinary: inAllowBinary];
}
- (NSArray*) findRecordsOfTypes:(NSArray*)inTypes withAttribute:(const char*)inAttrib value:(id)inValue matchType:(tDirPatternMatch)inMatchType retrieveAttributes:(NSArray*)inAttribsToRetrieve
{
return [self _findRecordsOfTypes: inTypes
withAttribute: inAttrib
value: inValue
matchType: inMatchType
retrieveAttributes: inAttribsToRetrieve
allowBinary: NO];
}
- (DSoRecord*) findRecord:(NSString*)inName ofType:(const char*)inType
{
RecID rec = [self _findRecord:inName ofType:inType];
return [[[DSoRecord alloc] initInNode:rec.node recordRef:rec.recordRef type:inType] autorelease] ;
}
- (DSoUser*) findUser:(NSString*)inName
{
RecID rec = [self _findRecord:inName ofType:kDSStdRecordTypeUsers];
return [[[DSoUser alloc] initInNode:rec.node recordRef:rec.recordRef type:kDSStdRecordTypeUsers] autorelease] ;
}
- (DSoGroup*) findGroup:(NSString*)inName
{
RecID rec = [self _findRecord:inName ofType:kDSStdRecordTypeGroups];
return [[[DSoGroup alloc] initInNode:rec.node recordRef:rec.recordRef type:kDSStdRecordTypeGroups] autorelease] ;
}
- (DSoGroup*) adminGroup
{
return [self findGroup:@"admin"];
}
// ---------------------------------------------------------------
// ¥ Write Methods
// ---------------------------------------------------------------
#pragma mark ** Write Methods **
- (DSoRecord*) newRecord:(NSString*)inName ofType:(const char*)inType
{
return [[[DSoRecord alloc] initInNode:self type:inType name:inName] autorelease];
}
// ---------------------------------------------------------------
// ¥ Other Methods
// ---------------------------------------------------------------
#pragma mark ** Other Methods **
- (tDirStatus) authenticateName:(NSString*)inName
withPassword: (NSString*)inPasswd
{
return [self authenticateName:inName withPassword:inPasswd authOnly:YES];
}
- (tDirStatus) authenticateName:(NSString*)inName
withPassword: (NSString*)inPasswd
authOnly: (BOOL)inAuthOnly
{
NSArray *nameAndPassword = [[NSArray alloc] initWithObjects:inName, inPasswd, nil];
tDirStatus status = [self authenticateWithBufferItems: nameAndPassword
authType: kDSStdAuthNodeNativeClearTextOK
authOnly: inAuthOnly];
[nameAndPassword release];
return status;
}
- (tDirStatus) authenticateWithBufferItems: (NSArray*)inBufferItems
authType: (const char*)inAuthType
authOnly: (BOOL)inAuthOnly
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UInt32 ulCurrentLength = 0;
DSoBuffer *dbAuth = nil;
DSoBuffer *dbStep = [[DSoBuffer alloc] initWithDir:mDirectory bufferSize:2048] ;
DSoDataNode *dnAuthType = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory cString:inAuthType] ;
NSEnumerator *enumBuffer = [inBufferItems objectEnumerator];
NSString *enumItem = nil;
tDirStatus authStatus = eDSNoErr;
unsigned int itemCount = [inBufferItems count];
register char *cpBuff = nil;
// Calculate the sum of the lengths of all the string items in the buffer list
while (enumItem = (NSString*)[enumBuffer nextObject])
ulCurrentLength += strlen([enumItem UTF8String]);
// Now allocate a buffer big enough to pack all the items into
dbAuth = [[DSoBuffer alloc] initWithDir:mDirectory bufferSize:(itemCount*4 + ulCurrentLength)];
// Get ready to pack the buffer using a direct memcpy() method
// instead of the DSoBuffer accessor methods to optimize the operation
cpBuff = ([dbAuth dsDataBuffer])->fBufferData;
enumBuffer = [inBufferItems objectEnumerator];
// Pack the Buffer with the items in the list
while (enumItem = (NSString*)[enumBuffer nextObject])
{
const char *utf8String = [enumItem UTF8String];
ulCurrentLength = strlen( utf8String );
memcpy (cpBuff, &ulCurrentLength, sizeof (ulCurrentLength)) ;
cpBuff += sizeof (ulCurrentLength) ;
memcpy (cpBuff, utf8String, ulCurrentLength) ;
cpBuff += ulCurrentLength ;
}
// Since we precalculated the size of the buffer to be the exact necessary
// length, set the length to the size.
[dbAuth setDataLength:[dbAuth getBufferSize]];
// Authenticate the user.
authStatus = dsDoDirNodeAuth (mNodeRef, [dnAuthType dsDataNode], inAuthOnly, [dbAuth dsDataBuffer], [dbStep dsDataBuffer], 0);
if (authStatus == eDSNoErr && !inAuthOnly)
mIsAuthenticated = YES ;
[dnAuthType release];
[dbStep release];
[dbAuth release];
[pool drain];
return authStatus;
}
- (tDirStatus) authenticateWithBufferItems: (NSArray*)inBufferItems
authType: (const char*)inAuthType
authOnly: (BOOL)inAuthOnly
responseBufferItems: (NSArray**)outBufferItems
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UInt32 ulCurrentLength = 0;
DSoBuffer *dbAuth = nil;
DSoBuffer *dbStep = [[DSoBuffer alloc] initWithDir:mDirectory bufferSize:2048] ;
DSoDataNode *dnAuthType = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory cString:inAuthType] ;
NSEnumerator *enumBuffer = [inBufferItems objectEnumerator];
NSString *enumItem = nil;
tDirStatus authStatus = eDSNoErr;
unsigned int itemCount = [inBufferItems count];
register char *cpBuff = nil;
unsigned int offset = 0, buffLen = 0;
tDirStatus tResult = eDSNoErr;
// Calculate the sum of the lengths of all the string items in the buffer list
while (enumItem = (NSString*)[enumBuffer nextObject])
ulCurrentLength += strlen([enumItem UTF8String]);
// Now allocate a buffer big enough to pack all the items into
dbAuth = [[DSoBuffer alloc] initWithDir:mDirectory bufferSize:(itemCount*4 + ulCurrentLength)];
// Get ready to pack the buffer using a direct memcpy() method
// instead of the DSoBuffer accessor methods to optimize the operation
cpBuff = ([dbAuth dsDataBuffer])->fBufferData;
enumBuffer = [inBufferItems objectEnumerator];
// Pack the Buffer with the items in the list
while (enumItem = (NSString*)[enumBuffer nextObject])
{
const char *utf8String = [enumItem UTF8String];
ulCurrentLength = strlen( utf8String );
memcpy (cpBuff, &ulCurrentLength, sizeof (ulCurrentLength)) ;
cpBuff += sizeof (ulCurrentLength) ;
memcpy (cpBuff, utf8String, ulCurrentLength) ;
cpBuff += ulCurrentLength ;
}
// Since we precalculated the size of the buffer to be the exact necessary
// length, set the length to the size.
[dbAuth setDataLength:[dbAuth getBufferSize]];
// Authenticate the user.
authStatus = dsDoDirNodeAuth (mNodeRef, [dnAuthType dsDataNode], inAuthOnly, [dbAuth dsDataBuffer], [dbStep dsDataBuffer], 0);
if (authStatus == eDSNoErr && !inAuthOnly)
mIsAuthenticated = YES ;
if (outBufferItems != nil) {
NSMutableArray* array = [NSMutableArray new];
NSString* string = nil;
cpBuff = ([dbStep dsDataBuffer])->fBufferData;
buffLen = ([dbStep dsDataBuffer])->fBufferLength;
while ( (offset < buffLen) && (tResult == eDSNoErr) )
{
if (offset + sizeof(ulCurrentLength) > buffLen)
{
tResult = eDSInvalidBuffFormat;
break;
}
memcpy( &ulCurrentLength, cpBuff, sizeof(ulCurrentLength) );
cpBuff += sizeof( ulCurrentLength );
offset += sizeof( ulCurrentLength );
if (ulCurrentLength + offset > buffLen)
{
tResult = eDSInvalidBuffFormat;
break;
}
// Node size is: struct size + string length + null term byte
string = (NSString*)CFMakeCollectable(CFStringCreateWithBytes(NULL,(const UInt8 *)cpBuff,(CFIndex)ulCurrentLength, kCFStringEncodingUTF8,false));
cpBuff += ulCurrentLength;
offset += ulCurrentLength;
if (string == nil) {
tResult = eDSInvalidBuffFormat;
break;
}
[array addObject:string];
}
if (tResult == eDSNoErr)
*outBufferItems = array;
else
[array release];
}
[dnAuthType release];
[dbStep release];
[dbAuth release];
[pool drain];
return authStatus;
}
- (tDirStatus)customCall:(int)number inputData:(NSData*)inputData
outputData:(NSMutableData*)outputData
{
long status = eDSNoErr;
tDataBuffer* customBuff1 = NULL;
tDataBuffer* customBuff2 = NULL;
do {
customBuff1 = dsDataNodeAllocateBlock( 0, [inputData length] + 1,
[inputData length], (char*)[inputData bytes]);
if (customBuff1 == 0) break;
customBuff2 = dsDataBufferAllocate( 0, [outputData length] + 1 );
if (customBuff2 == 0) break;
status = dsDoPlugInCustomCall( mNodeRef, number, customBuff1, customBuff2 );
if (status != eDSNoErr) break;
if (outputData != nil) {
[outputData setData:[NSData dataWithBytes:customBuff2->fBufferData
length:customBuff2->fBufferLength]];
}
} while (false);
//clean up allocations
if (customBuff1 != NULL)
{
dsDataBufferDeAllocate( 0, customBuff1 );
customBuff1 = NULL;
}
if (customBuff2 != NULL)
{
dsDataBufferDeAllocate( 0, customBuff2 );
customBuff2 = NULL;
}
return status;
}
- (tDirStatus)customCall:(int)number
sendPropertyList:(id)propList
withAuthorization:(void*)authExternalForm
{
NSData* data = (NSData*)CFPropertyListCreateXMLData(kCFAllocatorDefault,
(CFPropertyListRef)propList);
tDirStatus dsStatus = [self customCall:number sendData:data
withAuthorization:authExternalForm];
[data release];
return dsStatus;
}
- (tDirStatus)customCall:(int)number
withAuthorization:(void*)authExternalForm
{
return [self customCall:number sendData:nil withAuthorization:authExternalForm];
}
- (tDirStatus)customCall:(int)number
sendData:(NSData*)data
withAuthorization:(void*)authExternalForm
{
tDirStatus dsStatus = eDSNoErr;
NSMutableData* inputData = [[NSMutableData alloc]
initWithCapacity:[data length] + sizeof(AuthorizationExternalForm)];
[inputData appendBytes:authExternalForm length:sizeof(AuthorizationExternalForm)];
if (data != nil)
{
[inputData appendData:data];
}
dsStatus = [self customCall:number inputData:inputData outputData:nil];
[inputData release];
return dsStatus;
}
- (tDirStatus)customCall:(int)number
sendItems:(NSArray*)items
outputData:(NSMutableData*)outputData
{
tDirStatus dsStatus = eDSNoErr;
UInt32 itemSize = 0;
NSMutableData* inputData = [NSMutableData new];
NSEnumerator* itemEnum = [items objectEnumerator];
NSObject* item = nil;
while (item = [itemEnum nextObject])
{
if ([item isKindOfClass:[NSString class]])
{
const char* utf8String = [(NSString*)item UTF8String];
if (utf8String != nil)
{
itemSize = strlen(utf8String);
}
[inputData appendBytes:&itemSize length:sizeof(itemSize)];
if (itemSize > 0)
{
[inputData appendBytes:utf8String length:itemSize];
}
}
else if ([item isKindOfClass:[NSData class]])
{
itemSize = [(NSData*)item length];
[inputData appendBytes:&itemSize length:sizeof(itemSize)];
if (itemSize > 0)
{
[inputData appendData:(NSData*)item];
}
}
}
dsStatus = [self customCall:number inputData:inputData outputData:outputData];
[inputData release];
return dsStatus;
}
- (tDirStatus)customCall:(int)number
receiveData:(NSMutableData*)outputData
withAuthorization:(void*)authExternalForm
sizeCall:(int)sizeNumber
{
UInt32 bufferSize = 0;
NSData* inputData = [[NSData alloc] initWithBytes:authExternalForm
length:sizeof(AuthorizationExternalForm)];
NSMutableData* sizeData = [NSMutableData new];
[sizeData appendBytes:&bufferSize length:sizeof(bufferSize)];
tDirStatus dsStatus = [self customCall:sizeNumber inputData:inputData
outputData:sizeData];
if (dsStatus == eDSNoErr)
{
memcpy(&bufferSize,[sizeData bytes],sizeof(bufferSize));
[outputData setLength:bufferSize];
dsStatus = [self customCall:number inputData:inputData
outputData:outputData];
}
[sizeData release];
[inputData release];
return dsStatus;
}
- (NSString*)getName
{
return mNodeName;
}
- (DSoDirectory*) directory
{
return mDirectory;
}
- (tDirNodeReference)dsNodeReference
{
return mNodeRef;
}
- (BOOL)usesMultiThreaded
{
return NO;
}
- (void)setUsesMultiThreaded:(BOOL)inValue
{
}
- (BOOL)supportsSetAttributeValues
{
return mSupportsSetAttributeValues;
}
- (void)setSupportsSetAttributeValues:(BOOL)inValue
{
mSupportsSetAttributeValues = inValue;
}
- (NSString*)description
{
return [NSString stringWithFormat:@"[super description], mNodeName, mNodeRef, mIsAuthenticated ? "yes" : "no"];
}
@end
@implementation DSoSearchNode
// ----------------------------------------------------------------------------
// ¥ DSoSearchNode Protected Instance Methods
// ----------------------------------------------------------------------------
#pragma mark **** DSoSearchNode Protected Instance Methods ****
/* be careful that this method ONLY retrieves a single record (the first found)*/
- (RecID) _findRecord: (NSString*)inName
ofType: (const char*)inType
{
DSoDataList *dlNames = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory string:inName];
DSoDataList *dlTypes = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory cString:inType];
DSoDataList *dlAttrs = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory
cStrings:kDSNAttrMetaNodeLocation, kDSNAttrRecordName, 0] ;
DSoBuffer *dbData = [[DSoBuffer alloc] initWithDir:mDirectory bufferSize:1024];
tDirStatus nError = eDSNoErr;
UInt32 ulCount = 1 ;
tAttributeListRef refAttrs = 0 ;
tRecordEntryPtr recInfo = NULL ;
NSString *sNode = nil;
NSString *sName = nil;
DSoNode *nodeHome = nil;
DSoDataNode *dnName = nil;
DSoDataNode *dnType = nil;
tRecordReference rrTemp = 0;
RecID retValue;
tContextData context = 0;
do
{
nError = dsGetRecordList (mNodeRef, [dbData dsDataBuffer], [dlNames dsDataList], eDSiExact,
[dlTypes dsDataList], [dlAttrs dsDataList], false, &ulCount, &context);
if (nError == eDSBufferTooSmall)
[dbData grow:[dbData getBufferSize]*2];
} while ( (nError == eDSBufferTooSmall) || (nError == eDSNoErr && ulCount == 0 && context != 0) );
[dlNames release];
[dlTypes release];
[dlAttrs release];
if (nError == eDSNoErr && ulCount == 0)
nError = eDSRecordNotFound;
if (nError)
{
[dbData release];
[DSoException raiseWithStatus:nError];
}
// Get the attribute values for the first record from the data buffer.
if (nError = dsGetRecordEntry(mNodeRef, [dbData dsDataBuffer], 1, &refAttrs, &recInfo))
{
[dbData release];
[DSoException raiseWithStatus:nError];
}
ulCount = recInfo->fRecordAttributeCount ; // Re-purpose the use of ulCount
dsDeallocRecordEntry([mDirectory dsDirRef] , recInfo);
recInfo = nil;
for ( ; ulCount ; ulCount--)
{
tAttributeValueListRef refValue = 0 ;
tAttributeEntryPtr attrInfo = NULL ;
tAttributeValueEntryPtr value = NULL ;
unsigned long ulUsed = 0;
const char *szpData = nil;
if (nError = dsGetAttributeEntry (mNodeRef, [dbData dsDataBuffer],
refAttrs, ulCount, &refValue, &attrInfo))
{
[dbData release];
[DSoException raiseWithStatus:nError];
}
if (attrInfo != NULL)
{
if (nError = dsGetAttributeValue (mNodeRef, [dbData dsDataBuffer],
1, refValue, &value))
{
[dbData release];
dsCloseAttributeValueList(refValue);
dsDeallocAttributeEntry([mDirectory dsDirRef], attrInfo);
attrInfo = NULL;
[DSoException raiseWithStatus:nError];
}
if (value == NULL)
{
[dbData release];
dsCloseAttributeValueList(refValue);
dsDeallocAttributeEntry([mDirectory dsDirRef], attrInfo);
attrInfo = NULL;
[DSoException raiseWithStatus:eDSAttributeDoesNotExist];
}
// Break the attribute into manageable variables.
ulUsed = value->fAttributeValueData.fBufferLength + 1 ;
szpData = value->fAttributeValueData.fBufferData ;
// Match the attribute type so we can associate it properly.
if (!strcmp (attrInfo->fAttributeSignature.fBufferData,
kDSNAttrMetaNodeLocation)) {
sNode = [[NSString alloc] initWithUTF8String:szpData];
} else if (!strcmp (attrInfo->fAttributeSignature.fBufferData,
kDSNAttrRecordName)) {
sName = [[NSString alloc] initWithUTF8String:szpData];
}
dsDeallocAttributeValueEntry([mDirectory dsDirRef], value);
value = NULL;
dsCloseAttributeValueList(refValue);
dsDeallocAttributeEntry([mDirectory dsDirRef], attrInfo);
attrInfo = NULL;
}
}
if (refAttrs != 0)
{
dsCloseAttributeList(refAttrs);
}
[dbData release];
nError = eDSRecordNotFound; //reset below if dsOpenRecord succeeds
if (sNode != NULL)
{
nodeHome = [mDirectory findNode:sNode]; // already autoreleased
[sNode release];
}
if (sName != NULL)
{
dnName = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory string:sName];
[sName release];
}
if (inType != nil)
{
dnType = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory cString:inType];
}
if ( (nodeHome != NULL) && (dnName != NULL) && (dnType != NULL) )
{
nError = dsOpenRecord ([nodeHome dsNodeReference], [dnType dsDataNode], [dnName dsDataNode], &rrTemp);
[dnName release];
dnName = NULL;
[dnType release];
dnType = NULL;
}
if (nError)
{
if (dnName != NULL)
{
[dnName release];
}
if (dnType != NULL)
{
[dnType release];
}
[DSoException raiseWithStatus:nError];
}
retValue.node = nodeHome;
retValue.recordRef = rrTemp;
return retValue;
}
@end
@implementation DSoNode (DSoNodePrivate)
- (NSArray*) _findRecordsOfTypes: (NSArray*)inTypes
withAttribute: (const char*)inAttrib
value: (id)inValue
matchType: (tDirPatternMatch)inMatchType
retrieveAttributes: (NSArray*)inAttribsToRetrieve
allowBinary: (BOOL)inAllowBinary;
{
DSoBuffer *dbData = [[DSoBuffer alloc] initWithDir:mDirectory bufferSize:4096];
DSoDataList *dlRecordTypes = [[DSoDataList alloc] initWithDir:mDirectory strings:inTypes];
DSoDataNode *dnAttribType = nil;
id dnSearchValue = nil;
DSoDataList *dlAttribsToRetrieve = nil;
tContextData context = 0;
tDirStatus status = eDSNoErr;
tAttributeListRef refAttrs = 0;
tRecordEntryPtr recInfo = nil ;
unsigned long i = 0;
UInt32 ulCount = 0;
NSMutableArray *results = [NSMutableArray array];
BOOL shouldAddRecName = NO;
if (inAttrib != nil)
{
dnAttribType = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory cString:inAttrib];
dnSearchValue = [(DSoDataNode*)[DSoDataNode alloc] initWithDir:mDirectory value:inValue];
}
else
dnSearchValue = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory value:inValue];
if (inAttribsToRetrieve == nil)
dlAttribsToRetrieve = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory cString:kDSNAttrRecordName];
else
dlAttribsToRetrieve = [[DSoDataList alloc] initWithDir:mDirectory strings:inAttribsToRetrieve];
shouldAddRecName = ( inAttribsToRetrieve == nil
|| [inAttribsToRetrieve containsObject:@kDSNAttrRecordName]
|| [inAttribsToRetrieve containsObject:@kDSAttributesAll]
|| [inAttribsToRetrieve containsObject:@kDSAttributesStandardAll] );
@try
{
do {
if (inAttrib == nil)
status = dsGetRecordList(mNodeRef, [dbData dsDataBuffer], [dnSearchValue dsDataList], inMatchType,
[dlRecordTypes dsDataList], [dlAttribsToRetrieve dsDataList], (inAttribsToRetrieve == nil),
(UInt32 *)&ulCount, (tContextData*) &context);
else if (dlAttribsToRetrieve == nil)
status = dsDoAttributeValueSearch(mNodeRef, [dbData dsDataBuffer], [dlRecordTypes dsDataList],
[dnAttribType dsDataNode], inMatchType, [dnSearchValue dsDataNode],
(UInt32 *)&ulCount, (tContextData*) &context);
else
status = dsDoAttributeValueSearchWithData(mNodeRef, [dbData dsDataBuffer], [dlRecordTypes dsDataList],
[dnAttribType dsDataNode], inMatchType, [dnSearchValue dsDataNode],
[dlAttribsToRetrieve dsDataList], NO, (UInt32 *)&ulCount, (tContextData*) &context);
if (status == eDSBufferTooSmall)
{
[dbData grow:[dbData getBufferSize]*2];
continue;
}
else if (status != eDSNoErr)
{
[DSoException raiseWithStatus:status];
}
for (i = 1; i <= ulCount; i++)
{
status = dsGetRecordEntry (mNodeRef, [dbData dsDataBuffer], i, (tAttributeListRef *)&refAttrs, (tRecordEntryPtr *)&recInfo);
if (status == eDSNoErr)
{
@try
{
if (inAttribsToRetrieve == nil)
{
char *recName = nil;
dsGetRecordNameFromEntry(recInfo, &recName);
[results addObject:[NSString stringWithUTF8String:recName]];
free(recName);
}
else
{
NSDictionary *attribsAndValues = nil;
attribsAndValues = [DSoAttributeUtils getAttributesAndValuesInNode:self fromBuffer:dbData
listReference:refAttrs count:recInfo->fRecordAttributeCount allowBinary:inAllowBinary];
if ([inAttribsToRetrieve containsObject:kDSOAttrRecordType])
{
char *cRecType = nil;
NSString *recType = nil;
dsGetRecordTypeFromEntry(recInfo, &cRecType);
recType = [[NSString alloc] initWithCString:cRecType];
attribsAndValues = [[attribsAndValues mutableCopy] autorelease];
[(NSMutableDictionary*)attribsAndValues setObject:recType forKey:kDSOAttrRecordType];
[recType release];
free(cRecType);
}
if (shouldAddRecName && [attribsAndValues objectForKey:@kDSNAttrRecordName] == nil) {
char *recName = nil;
dsGetRecordNameFromEntry(recInfo, &recName);
[(NSMutableDictionary*)attribsAndValues setObject:[NSArray arrayWithObject:[NSString stringWithUTF8String:recName]]
forKey:@kDSNAttrRecordName];
free(recName);
}
[results addObject:attribsAndValues];
}
} @catch( NSException *exception ) {
@throw;
} @finally {
dsDeallocRecordEntry([mDirectory dsDirRef], recInfo);
recInfo = NULL;
dsCloseAttributeList(refAttrs);
refAttrs = 0;
}
}
else
[DSoException raiseWithStatus:status];
}
} while ((status == eDSBufferTooSmall) || (status == eDSNoErr && context != 0));
} @catch( NSException *exception ) {
@throw;
} @finally {
if (context != 0)
{
dsReleaseContinueData(mNodeRef, context);
context = 0;
}
[dbData release];
[dlRecordTypes release];
[dnAttribType release];
[dnSearchValue release];
[dlAttribsToRetrieve release];
}
return results;
}
- (BOOL) _hasRecordsOfType:(const char*)inType
{
DSoDataList *dlNames = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory cString:kDSRecordsAll];
DSoDataList *dlTypes = nil;
DSoDataList *dlAttrs = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory cString:kDSNAttrRecordName] ;
DSoBuffer *dbData = [(DSoBuffer*)[DSoBuffer alloc] initWithDir:mDirectory bufferSize:256];
tDirStatus nError = eDSNoErr;
UInt32 ulCount = 1;
tContextData context = 0;
BOOL bHasType = NO;
@try
{
dlTypes = [(DSoDataList*)[DSoDataList alloc] initWithDir:mDirectory cString:inType];
do {
nError = dsGetRecordList (mNodeRef, [dbData dsDataBuffer], [dlNames dsDataList], eDSExact,
[dlTypes dsDataList], [dlAttrs dsDataList], TRUE, (UInt32 *)&ulCount, (tContextData *)&context);
if (nError == eDSBufferTooSmall)
{
[dbData grow:[dbData getBufferSize]*2];
}
else if (nError != eDSNoErr)
{
if (nError != eDSInvalidRecordType)
[DSoException raiseWithStatus:nError];
}
else if (ulCount > 0)
{
bHasType = YES;
break;
}
} while ( (nError == eDSBufferTooSmall) || (context != 0) );
} @catch( NSException *exception ) {
@throw;
} @finally {
if (context != 0)
{
dsReleaseContinueData(mNodeRef, context);
context = 0;
}
[dlAttrs release];
[dlNames release];
[dlTypes release];
[dbData release];
}
return bHasType;
}
- (void) reopen
{
tDirStatus nError = eDSNoErr;
DSoDataList *dlNodeName = [[DSoDataList alloc] initWithDir:mDirectory separator:'/' pattern:mNodeName];
dsCloseDirNode(mNodeRef);
mNodeRef = 0;
nError = dsOpenDirNode ([mDirectory dsDirRef], [dlNodeName dsDataList], &mNodeRef);
[dlNodeName release];
if (nError)
[DSoException raiseWithStatus:nError];
}
@end