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(
136 CFStringRef cfKey
= CFStringCreateWithCStringNoCopy(NULL
,
138 CFStringGetSystemEncoding(),
141 CFStringRef cfKey
= CFStringCreateWithCString(NULL
,
143 CFStringGetSystemEncoding());
146 MPDebug("MDSDictionary::lookup: error creating CFString for key");
149 const void *rtn
= lookup(cfKey
, checkType
, type
);
155 const void *MDSDictionary::lookup(
160 assert(mDict
!= NULL
);
161 const void *rtn
= CFDictionaryGetValue(mDict
, key
);
162 if(rtn
&& checkType
) {
163 if(CFGetTypeID((CFTypeRef
)rtn
) != type
) {
171 * Common means to perform a lookup in a dictionary given a C-string key and
172 * placing the value - if present - in a CSSM_DB_ATTRIBUTE_DATA. Any errors
173 * are only logged via MPDebug. Returns true if the value was found and
174 * successfully placed in supplied CSSM_DB_ATTRIBUTE_DATA.
176 * For now we assume that the key in the dictionary is the same as the key
177 * in the DB to which we're writing.
179 * We're also assuming that all DB keys are of format CSSM_DB_ATTRIBUTE_NAME_AS_STRING.
181 bool MDSDictionary::lookupToDbAttr(
183 CSSM_DB_ATTRIBUTE_DATA
&attr
,
184 CSSM_DB_ATTRIBUTE_FORMAT attrFormat
,
185 const MDSNameValuePair
*nameValues
) // optional for converting strings to numbers
187 assert(mDict
!= NULL
);
188 assert(&attr
!= NULL
);
190 CFTypeRef value
; // polymorphic dictionary value
192 const void *srcPtr
= NULL
; // polymorphic raw source bytes
196 uint32
*ivalArray
= NULL
;
197 uint32 numValues
= 1; // the default for MDSRawValueToDbAttr
199 value
= (CFTypeRef
)lookup(key
);
202 * Special case here: we implicitly provide a value for the "Path" key
203 * if it's not in the dictionary and we have it.
205 if((attrFormat
== CSSM_DB_ATTRIBUTE_FORMAT_STRING
) &&
206 !strcmp(key
, "Path") &&
207 (mUrlPath
!= NULL
)) {
208 MDSRawValueToDbAttr(mUrlPath
,
209 strlen(mUrlPath
) + 1,
220 CFTypeID valueType
= CFGetTypeID(value
);
223 * We have the value; could be any type. Handle it based on caller's
224 * CSSM_DB_ATTRIBUTE_FORMAT.
227 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
231 if(valueType
!= CFStringGetTypeID()) {
232 MPDebug("lookupToDbAttr: string format mismatch");
235 brtn
= CFStringGetCString((CFStringRef
)value
, cstr
,
236 CSSM_MODULE_STRING_SIZE
, CFStringGetSystemEncoding());
238 /* this could be "string too large for a CSSM_STRING" */
239 MPDebug("lookupToDbAttr: CFStringGetCString error");
243 srcLen
= strlen(cstr
) + 1;
248 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
250 bool brtn
= MDSCfTypeToInt(value
, nameValues
, key
, ival
);
252 MPDebug("MDS lookupToDbAttr: Bad number conversion");
256 srcLen
= sizeof(uint32
);
260 case CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
:
263 * This is expressed in the dictionary as an array of numbers.
264 * as in CSSM_DB_ATTRIBUTE_FORMAT_UINT32, each number can be
265 * expressed as either a string or a number.
267 if(valueType
!= CFArrayGetTypeID()) {
269 * Let's be extremely slick and allow one number here, either
270 * in string or number form....
272 bool brtn
= MDSCfTypeToInt(value
, nameValues
, key
, ival
);
274 MPDebug("MDS lookupToDbAttr: Bad array element");
278 srcLen
= sizeof(uint32
);
282 CFArrayRef cfArray
= (CFArrayRef
)value
;
283 numValues
= CFArrayGetCount(cfArray
);
285 /* degenerate case, legal - right? Can AppleDatabase do this? */
293 * malloc an array of uint32s
294 * convert each element in cfArray to a uint32
295 * store as CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
297 * Note this does not have to be endian independent; the MDS DBs
298 * are not portable across machines let alone platforms.
300 ivalArray
= new uint32
[numValues
];
303 for(dex
=0; dex
<numValues
; dex
++) {
304 CFTypeRef elmt
= (CFTypeRef
)CFArrayGetValueAtIndex(cfArray
, dex
);
306 MPDebug("MDS lookupToDbAttr: key %s: Bad array element (1)", key
);
310 brtn
= MDSCfTypeToInt(elmt
, nameValues
, key
, ivalArray
[dex
]);
312 MPDebug("MDS lookupToDbAttr: key %s Bad element at index %d",
319 srcLen
= sizeof(uint32
) * numValues
;
322 * FIXME - numValues as used by MDSRawValueToDbAttr and placed in
323 * CSSM_DB_ATTRIBUTE_DATA.NumberOfValues, appears to need to be
324 * one even for MULTI_UINT32 format; the number of ints in inferred
325 * from Value.Length....
330 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
: // CFData
332 if(valueType
!= CFDataGetTypeID()) {
333 MPDebug("lookupToDbAttr: blob/CFData format mismatch");
336 CFDataRef cfData
= (CFDataRef
)value
;
337 srcLen
= CFDataGetLength(cfData
);
338 srcPtr
= CFDataGetBytePtr(cfData
);
342 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
: // I don't think we support this
344 MPDebug("lookupToDbAttr: bad attrForm(%d)", (int)attrFormat
);
348 MDSRawValueToDbAttr(srcPtr
, srcLen
, attrFormat
, key
, attr
, numValues
);
357 * Given a RelationInfo and an array of CSSM_DB_ATTRIBUTE_DATAs, fill in
358 * the CSSM_DB_ATTRIBUTE_DATA array with as many fields as we can find in
359 * the dictionary. All fields are treated as optional.
361 void MDSDictionary::lookupAttributes(
362 const RelationInfo
*relInfo
,
363 CSSM_DB_ATTRIBUTE_DATA_PTR outAttrs
, // filled in on return
364 uint32
&numAttrs
) // RETURNED
367 const CSSM_DB_ATTRIBUTE_INFO
*inAttr
= relInfo
->AttributeInfo
;
368 const MDSNameValuePair
**nameValues
= relInfo
->nameValues
;
370 assert(relInfo
!= NULL
);
372 for(dex
=0; dex
<relInfo
->NumberOfAttributes
; dex
++) {
374 const MDSNameValuePair
*nvp
;
376 /* the array itself, or any element in it, can be NULL */
377 if(nameValues
!= NULL
) {
378 nvp
= nameValues
[dex
];
383 brtn
= lookupToDbAttr(inAttr
->Label
.AttributeName
,
385 inAttr
->AttributeFormat
,
388 /* successfully added to dbAttrs */
392 inAttr
++; // regardless
397 * Lookup with file-based indirection. Allows multiple mdsinfo files to share commmon
398 * info from a separate plist file.
400 * Do a lookup for specified key. If not found, return NULL. If found:
402 * if type of value matches desiredType {
405 * else if type of value is string {
406 * if string starts with "file:" {
407 * attempt to read property list with that filename relative to
409 * if CFType of that propList matches desiredType {
410 * return newly read propList;
414 * ...else return error;
416 const CFPropertyListRef
MDSDictionary::lookupWithIndirect(
419 CFTypeID desiredType
,
420 bool &fetchedFromDisk
) // true --> caller must CFRelease the returned
422 // false -> it's part of this dictionary
424 CFPropertyListRef ourRtn
= NULL
;
425 CFDataRef dictData
= NULL
;
426 CFStringRef cfErr
= NULL
;
432 assert(bundle
!= NULL
);
434 fetchedFromDisk
= false;
436 /* basic local lookup */
437 CFStringRef cfKey
= CFStringCreateWithCString(NULL
,
439 CFStringGetSystemEncoding());
441 MPDebug("CFStringCreateWithCString error");
444 const void *rtn
= CFDictionaryGetValue(mDict
, cfKey
);
449 CFTypeID foundType
= CFGetTypeID((CFTypeRef
)rtn
);
450 if(foundType
== desiredType
) {
451 /* found what we're looking for; done */
452 return (CFPropertyListRef
)rtn
;
455 /* is it a string which starts with "file:"? */
456 if(foundType
!= CFStringGetTypeID()) {
459 const char *cVal
= MDSCFStringToCString((CFStringRef
)rtn
);
461 MPDebug("MDSCFStringToCString error in lookupWithIndirect");
464 if(strstr(cVal
, "file:") != cVal
) {
468 /* delete [] cval on return */
470 /* OK, this specifies a resource file in the bundle. Fetch it. */
471 CFStringRef cfFileName
= CFStringCreateWithCString(NULL
,
473 CFStringGetSystemEncoding());
474 if(cfFileName
== NULL
) {
475 MPDebug("lookupWithIndirect: bad file name spec");
479 fileUrl
= CFBundleCopyResourceURL(bundle
,
483 if(fileUrl
== NULL
) {
484 MPDebug("lookupWithIndirect: file %s not found", cVal
);
488 MPDebug("Fetching indirect resource %s", cVal
);
490 /* Load data from URL */
491 brtn
= CFURLCreateDataAndPropertiesFromResource(
496 NULL
, // desiredProperties
499 MPDebug("lookupWithIndirect: error %d reading %s", (int)uerr
, cVal
);
503 /* if it's not a property list, we don't want it */
504 ourRtn
= CFPropertyListCreateFromXMLData(NULL
,
506 kCFPropertyListImmutable
,
509 MPDebug("lookupWithIndirect: %s malformed (not a prop list)", cVal
);
513 /* if it doesn't match the caller's spec, we don't want it */
514 if(CFGetTypeID(ourRtn
) != desiredType
) {
515 MPDebug("lookupWithIndirect: %s malformed (mismatch)", cVal
);
521 MPDebug("lookupWithIndirect: resource %s FOUND", cVal
);
522 fetchedFromDisk
= true;
526 CF_RELEASE(cfFileName
);
528 CF_RELEASE(dictData
);
533 } // end namespace Security