2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 File: MDSDictionary.cpp
22 Contains: Internal representation of one MDS info file in the form of
25 Copyright: (c) 2001 Apple Computer, Inc., all rights reserved.
28 #include "MDSDictionary.h"
29 #include "MDSAttrParser.h"
30 #include "MDSAttrUtils.h"
31 #include <Security/cssmerrno.h>
32 #include <Security/utilities.h>
33 #include <Security/logging.h>
38 /* heavyweight constructor from file */
39 MDSDictionary::MDSDictionary(
41 const char *fullPath
) // could get from fileUrl, but very messy!
47 CFDataRef dictData
= NULL
;
48 CFStringRef cfErr
= NULL
;
50 assert(fileUrl
!= NULL
);
51 mUrlPath
= MDSCopyCstring(fullPath
);
52 MPDebug("Creating MDSDictionary from %s", mUrlPath
);
54 /* Load data from URL */
56 Boolean brtn
= CFURLCreateDataAndPropertiesFromResource(
61 NULL
, // desiredProperties
64 Syslog::alert("Error reading MDS file %s: %d", mUrlPath
, uerr
);
65 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
68 /* if it's not a dictionary, we don't want it */
69 mDict
= reinterpret_cast<CFDictionaryRef
>(
70 CFPropertyListCreateFromXMLData(NULL
,
72 kCFPropertyListImmutable
,
76 Syslog::alert("Malformed MDS file %s (1)", mUrlPath
);
77 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
80 /* henceforth we must release this dictionary */
82 if(CFGetTypeID(mDict
) != CFDictionaryGetTypeID()) {
83 Syslog::alert("Malformed MDS file %s (2)", mUrlPath
);
84 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
88 /* get file description for error logging and debugging */
89 CFStringRef cfStr
= (CFStringRef
)lookup(CFSTR(MDS_INFO_FILE_DESC
),
90 true, CFStringGetTypeID());
92 unsigned len
= CFStringGetLength(cfStr
) + 1;
93 mFileDesc
= new char[len
];
95 CFStringGetCString(cfStr
, mFileDesc
, len
,
96 CFStringGetSystemEncoding());
101 /* lightweight constructor from existing CFDictionary */
102 MDSDictionary::MDSDictionary(CFDictionaryRef theDict
)
108 /* note caller owns and releases the dictionary */
110 MPDebug("Malformed MDS file (3)");
111 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
113 if(CFGetTypeID(mDict
) != CFDictionaryGetTypeID()) {
114 MPDebug("Malformed MDS file (4)");
115 CssmError::throwMe(CSSMERR_CSSM_MDS_ERROR
);
119 MDSDictionary::~MDSDictionary()
129 /* lookup by either C string or CFStringRef - returns NULL on error */
130 const void *MDSDictionary::lookup(
135 CFStringRef cfKey
= CFStringCreateWithCString(NULL
,
137 CFStringGetSystemEncoding());
139 MPDebug("MDSDictionary::lookup: error creating CFString for key");
142 const void *rtn
= lookup(cfKey
, checkType
, type
);
148 const void *MDSDictionary::lookup(
153 assert(mDict
!= NULL
);
154 const void *rtn
= CFDictionaryGetValue(mDict
, key
);
155 if(rtn
&& checkType
) {
156 if(CFGetTypeID((CFTypeRef
)rtn
) != type
) {
164 * Common means to perform a lookup in a dictionary given a C-string key and
165 * placing the value - if present - in a CSSM_DB_ATTRIBUTE_DATA. Any errors
166 * are only logged via MPDebug. Returns true if the value was found and
167 * successfully placed in supplied CSSM_DB_ATTRIBUTE_DATA.
169 * For now we assume that the key in the dictionary is the same as the key
170 * in the DB to which we're writing.
172 * We're also assuming that all DB keys are of format CSSM_DB_ATTRIBUTE_NAME_AS_STRING.
174 bool MDSDictionary::lookupToDbAttr(
176 CSSM_DB_ATTRIBUTE_DATA
&attr
,
177 CSSM_DB_ATTRIBUTE_FORMAT attrFormat
,
178 const MDSNameValuePair
*nameValues
) // optional for converting strings to numbers
180 assert(mDict
!= NULL
);
181 assert(&attr
!= NULL
);
183 CFTypeRef value
; // polymorphic dictionary value
185 const void *srcPtr
= NULL
; // polymorphic raw source bytes
189 uint32
*ivalArray
= NULL
;
190 uint32 numValues
= 1; // the default for MDSRawValueToDbAttr
192 value
= (CFTypeRef
)lookup(key
);
195 * Special case here: we implicitly provide a value for the "Path" key
196 * if it's not in the dictionary and we have it.
198 if((attrFormat
== CSSM_DB_ATTRIBUTE_FORMAT_STRING
) &&
199 !strcmp(key
, "Path") &&
200 (mUrlPath
!= NULL
)) {
201 MDSRawValueToDbAttr(mUrlPath
,
202 strlen(mUrlPath
) + 1,
213 CFTypeID valueType
= CFGetTypeID(value
);
216 * We have the value; could be any type. Handle it based on caller's
217 * CSSM_DB_ATTRIBUTE_FORMAT.
220 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
224 if(valueType
!= CFStringGetTypeID()) {
225 MPDebug("lookupToDbAttr: string format mismatch");
228 brtn
= CFStringGetCString((CFStringRef
)value
, cstr
,
229 CSSM_MODULE_STRING_SIZE
, CFStringGetSystemEncoding());
231 /* this could be "string too large for a CSSM_STRING" */
232 MPDebug("lookupToDbAttr: CFStringGetCString error");
236 srcLen
= strlen(cstr
) + 1;
241 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
243 bool brtn
= MDSCfTypeToInt(value
, nameValues
, key
, ival
);
245 MPDebug("MDS lookupToDbAttr: Bad number conversion");
249 srcLen
= sizeof(uint32
);
253 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
256 * This is expressed in the dictionary as an array of numbers.
257 * as in CSSM_DB_ATTRIBUTE_FORMAT_UINT32, each number can be
258 * expressed as either a string or a number.
260 if(valueType
!= CFArrayGetTypeID()) {
262 * Let's be extremely slick and allow one number here, either
263 * in string or number form....
265 bool brtn
= MDSCfTypeToInt(value
, nameValues
, key
, ival
);
267 MPDebug("MDS lookupToDbAttr: Bad array element");
271 srcLen
= sizeof(uint32
);
275 CFArrayRef cfArray
= (CFArrayRef
)value
;
276 numValues
= CFArrayGetCount(cfArray
);
278 /* degenerate case, legal - right? Can AppleDatabase do this? */
286 * malloc an array of uint32s
287 * convert each element in cfArray to a uint32
288 * store as CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
290 * Note this does not have to be endian independent; the MDS DBs
291 * are not portable across machines let alone platforms.
293 ivalArray
= new uint32
[numValues
];
296 for(dex
=0; dex
<numValues
; dex
++) {
297 CFTypeRef elmt
= (CFTypeRef
)CFArrayGetValueAtIndex(cfArray
, dex
);
299 MPDebug("MDS lookupToDbAttr: key %s: Bad array element (1)", key
);
303 brtn
= MDSCfTypeToInt(elmt
, nameValues
, key
, ivalArray
[dex
]);
305 MPDebug("MDS lookupToDbAttr: key %s Bad element at index %d",
312 srcLen
= sizeof(uint32
) * numValues
;
315 * FIXME - numValues as used by MDSRawValueToDbAttr and placed in
316 * CSSM_DB_ATTRIBUTE_DATA.NumberOfValues, appears to need to be
317 * one even for MULTI_UINT32 format; the number of ints in inferred
318 * from Value.Length....
323 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
: // CFData
325 if(valueType
!= CFDataGetTypeID()) {
326 MPDebug("lookupToDbAttr: blob/CFData format mismatch");
329 CFDataRef cfData
= (CFDataRef
)value
;
330 srcLen
= CFDataGetLength(cfData
);
331 srcPtr
= CFDataGetBytePtr(cfData
);
335 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
: // I don't think we support this
337 MPDebug("lookupToDbAttr: bad attrForm(%d)", (int)attrFormat
);
341 MDSRawValueToDbAttr(srcPtr
, srcLen
, attrFormat
, key
, attr
, numValues
);
350 * Given a RelationInfo and an array of CSSM_DB_ATTRIBUTE_DATAs, fill in
351 * the CSSM_DB_ATTRIBUTE_DATA array with as many fields as we can find in
352 * the dictionary. All fields are treated as optional.
354 void MDSDictionary::lookupAttributes(
355 const RelationInfo
*relInfo
,
356 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
, // filled in on return
357 uint32
&numAttrs
) // RETURNED
360 const CSSM_DB_ATTRIBUTE_INFO
*inAttr
= relInfo
->AttributeInfo
;
361 const MDSNameValuePair
**nameValues
= relInfo
->nameValues
;
363 assert(relInfo
!= NULL
);
365 for(dex
=0; dex
<relInfo
->NumberOfAttributes
; dex
++) {
367 const MDSNameValuePair
*nvp
;
369 /* the array itself, or any element in it, can be NULL */
370 if(nameValues
!= NULL
) {
371 nvp
= nameValues
[dex
];
376 brtn
= lookupToDbAttr(inAttr
->Label
.AttributeName
,
378 inAttr
->AttributeFormat
,
381 /* successfully added to dbAttrs */
385 inAttr
++; // regardless
390 * Lookup with file-based indirection. Allows multiple mdsinfo files to share commmon
391 * info from a separate plist file.
393 * Do a lookup for specified key. If not found, return NULL. If found:
395 * if type of value matches desiredType {
398 * else if type of value is string {
399 * if string starts with "file:" {
400 * attempt to read property list with that filename relative to
402 * if CFType of that propList matches desiredType {
403 * return newly read propList;
407 * ...else return error;
409 const CFPropertyListRef
MDSDictionary::lookupWithIndirect(
412 CFTypeID desiredType
,
413 bool &fetchedFromDisk
) // true --> caller must CFRelease the returned
415 // false -> it's part of this dictionary
417 CFPropertyListRef ourRtn
= NULL
;
418 CFDataRef dictData
= NULL
;
419 CFStringRef cfErr
= NULL
;
425 assert(bundle
!= NULL
);
427 fetchedFromDisk
= false;
429 /* basic local lookup */
430 CFStringRef cfKey
= CFStringCreateWithCString(NULL
,
432 CFStringGetSystemEncoding());
434 MPDebug("CFStringCreateWithCString error");
437 const void *rtn
= CFDictionaryGetValue(mDict
, cfKey
);
442 CFTypeID foundType
= CFGetTypeID((CFTypeRef
)rtn
);
443 if(foundType
== desiredType
) {
444 /* found what we're looking for; done */
445 return (CFPropertyListRef
)rtn
;
448 /* is it a string which starts with "file:"? */
449 if(foundType
!= CFStringGetTypeID()) {
452 const char *cVal
= MDSCFStringToCString((CFStringRef
)rtn
);
454 MPDebug("MDSCFStringToCString error in lookupWithIndirect");
457 if(strstr(cVal
, "file:") != cVal
) {
461 /* delete [] cval on return */
463 /* OK, this specifies a resource file in the bundle. Fetch it. */
464 CFStringRef cfFileName
= CFStringCreateWithCString(NULL
,
466 CFStringGetSystemEncoding());
467 if(cfFileName
== NULL
) {
468 MPDebug("lookupWithIndirect: bad file name spec");
472 fileUrl
= CFBundleCopyResourceURL(bundle
,
476 if(fileUrl
== NULL
) {
477 MPDebug("lookupWithIndirect: file %s not found", cVal
);
481 MPDebug("Fetching indirect resource %s", cVal
);
483 /* Load data from URL */
484 brtn
= CFURLCreateDataAndPropertiesFromResource(
489 NULL
, // desiredProperties
492 MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr
, cVal
);
496 /* if it's not a property list, we don't want it */
497 ourRtn
= CFPropertyListCreateFromXMLData(NULL
,
499 kCFPropertyListImmutable
,
502 MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal
);
506 /* if it doesn't match the caller's spec, we don't want it */
507 if(CFGetTypeID(ourRtn
) != desiredType
) {
508 MPDebug("lookupWithIndirect: %s malformed (mismatch)", cVal
);
514 MPDebug("lookupWithIndirect: resource %s FOUND", cVal
);
515 fetchedFromDisk
= true;
519 CF_RELEASE(cfFileName
);
521 CF_RELEASE(dictData
);
526 } // end namespace Security