]> git.saurik.com Git - apple/security.git/blob - libsecurity_utilities/lib/cfmunge.cpp
Security-55471.tar.gz
[apple/security.git] / libsecurity_utilities / lib / cfmunge.cpp
1 /*
2 * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 //
24 // CoreFoundation building and parsing functions.
25 //
26 // These classes provide a printf/scanf-like interface to nested data structures
27 // of the Property List Subset of CoreFoundation.
28 //
29 #include "cfmunge.h"
30 #include <security_utilities/cfutilities.h>
31 #include <security_utilities/errors.h>
32
33 namespace Security {
34
35
36 //
37 // Format codes for consistency
38 //
39 #define F_ARRAY 'A'
40 #define F_BOOLEAN 'B'
41 #define F_DATA 'X'
42 #define F_DICTIONARY 'D'
43 #define F_OBJECT 'O'
44 #define F_STRING 'S'
45 #define F_NUMBER 'N'
46
47
48 //
49 // Initialize a CFMunge. We start out with the default CFAllocator, and
50 // we do not throw errors.
51 //
52 CFMunge::CFMunge(const char *fmt, va_list arg)
53 : format(fmt), allocator(NULL), error(errSecSuccess)
54 {
55 va_copy(args, arg);
56 }
57
58 CFMunge::~CFMunge()
59 {
60 va_end(args);
61 }
62
63
64 //
65 // Skip whitespace and other fluff and deliver the next significant character.
66 //
67 char CFMunge::next()
68 {
69 while (*format && (isspace(*format) || *format == ',')) ++format;
70 return *format;
71 }
72
73
74 //
75 // Locate and consume an optional character
76 //
77 bool CFMunge::next(char c)
78 {
79 if (next() == c) {
80 ++format;
81 return true;
82 } else
83 return false;
84 }
85
86
87 //
88 // Process @? parameter specifications.
89 // The @ operator is used for side effects, and does not return a value.
90 //
91 bool CFMunge::parameter()
92 {
93 switch (*++format) {
94 case 'A':
95 ++format;
96 allocator = va_arg(args, CFAllocatorRef);
97 return true;
98 case 'E':
99 ++format;
100 error = va_arg(args, OSStatus);
101 return true;
102 default:
103 return false;
104 }
105 }
106
107
108 //
109 // The top constructor.
110 //
111 CFTypeRef CFMake::make()
112 {
113 while (next() == '@')
114 parameter();
115 switch (next()) {
116 case '\0':
117 return NULL;
118 case '{':
119 return makedictionary();
120 case '[':
121 return makearray();
122 case '\'':
123 return makestring();
124 case '%':
125 return makeformat();
126 case '#':
127 return makespecial();
128 case ']':
129 case '}':
130 assert(false); // unexpected
131 return NULL; // error
132 default:
133 if (isdigit(*format))
134 return makenumber();
135 else if (isalpha(*format))
136 return makestring();
137 else {
138 assert(false);
139 return NULL;
140 }
141 }
142 }
143
144
145 CFTypeRef CFMake::makeformat()
146 {
147 ++format;
148 switch (*format++) {
149 case 'b': // blob (pointer, length)
150 {
151 const void *data = va_arg(args, const void *);
152 size_t length = va_arg(args, size_t);
153 return CFDataCreate(allocator, (const UInt8 *)data, length);
154 }
155 case F_BOOLEAN: // boolean (with int promotion)
156 return va_arg(args, int) ? kCFBooleanTrue : kCFBooleanFalse;
157 case 'd':
158 return makeCFNumber(va_arg(args, int));
159 case 's':
160 return CFStringCreateWithCString(allocator, va_arg(args, const char *),
161 kCFStringEncodingUTF8);
162 case F_OBJECT:
163 return CFRetain(va_arg(args, CFTypeRef));
164 case 'u':
165 return makeCFNumber(va_arg(args, unsigned int));
166 default:
167 assert(false);
168 return NULL;
169 }
170 }
171
172
173 CFTypeRef CFMake::makespecial()
174 {
175 ++format;
176 switch (*format++) {
177 case 'N':
178 return kCFNull;
179 case 't':
180 case 'T':
181 return kCFBooleanTrue;
182 case 'f':
183 case 'F':
184 return kCFBooleanFalse;
185 default:
186 assert(false);
187 return NULL;
188 }
189 }
190
191
192 CFTypeRef CFMake::makenumber()
193 {
194 double value = strtod(format, (char **)&format);
195 return CFNumberCreate(allocator, kCFNumberDoubleType, &value);
196 }
197
198
199 //
200 // Embedded strings can either be alphanumeric (only), or delimited with single quotes ''.
201 // No escapes are processed within such quotes. If you want arbitrary string values, use %s.
202 //
203 CFTypeRef CFMake::makestring()
204 {
205 const char *start, *end;
206 if (*format == '\'') {
207 start = ++format; // next quote
208 if (!(end = strchr(format, '\''))) {
209 assert(false);
210 return NULL;
211 }
212 format = end + 1;
213 } else {
214 start = format;
215 for (end = start + 1; isalnum(*end); ++end) ;
216 format = end;
217 }
218 return CFStringCreateWithBytes(allocator,
219 (const UInt8 *)start, end - start,
220 kCFStringEncodingUTF8, false);
221 }
222
223
224 //
225 // Construct a CFDictionary
226 //
227 CFTypeRef CFMake::makedictionary()
228 {
229 ++format; // next '{'
230 next('!'); // indicates mutable (currently always true)
231 CFMutableDictionaryRef dict;
232 if (next('+')) { // {+%O, => copy dictionary argument, then proceed
233 if (next('%') && next('O')) {
234 CFDictionaryRef source = va_arg(args, CFDictionaryRef);
235 dict = CFDictionaryCreateMutableCopy(allocator, NULL, source);
236 if (next('}'))
237 return dict;
238 } else
239 return NULL; // bad syntax
240 } else
241 dict = CFDictionaryCreateMutable(allocator, 0,
242 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
243 if (add(dict))
244 return dict;
245 else {
246 CFRelease(dict);
247 return NULL;
248 }
249 }
250
251 CFDictionaryRef CFMake::add(CFMutableDictionaryRef dict)
252 {
253 while (next() != '}') {
254 CFTypeRef key = make();
255 if (key == NULL)
256 return NULL;
257 if (!next('=')) {
258 CFRelease(key);
259 return NULL;
260 }
261 if (CFTypeRef value = make()) {
262 CFDictionaryAddValue(dict, key, value);
263 CFRelease(key);
264 CFRelease(value);
265 } else {
266 CFRelease(key);
267 return NULL;
268 }
269 }
270 ++format;
271 return dict;
272 }
273
274
275 CFDictionaryRef CFMake::addto(CFMutableDictionaryRef dict)
276 {
277 if (next('{'))
278 return add(dict);
279 else {
280 assert(false);
281 return NULL;
282 }
283 }
284
285
286 //
287 // Construct a CFArray
288 //
289 CFTypeRef CFMake::makearray()
290 {
291 ++format; // next '['
292 next('!'); // indicates mutable (currently always)
293 CFMutableArrayRef array = makeCFMutableArray(0);
294 while (next() != ']') {
295 CFTypeRef value = make();
296 if (value == NULL) {
297 CFRelease(array);
298 return NULL;
299 }
300 CFArrayAppendValue(array, value);
301 CFRelease(value);
302 }
303 ++format;
304 return array;
305 }
306
307
308 //
309 // A CFScan processes its format by parsing through an existing CF object
310 // structure, matching and extracting values as directed. Note that CFScan
311 // is a structure (tree) scanner rather than a linear parser, and will happily
312 // parse out a subset of the input object graph.
313 //
314 class CFScan : public CFMake {
315 public:
316 CFScan(const char *format, va_list args)
317 : CFMake(format, args), suppress(false) { }
318
319 bool scan(CFTypeRef obj);
320 CFTypeRef dictpath(CFTypeRef obj);
321
322 protected:
323 bool scandictionary(CFDictionaryRef obj);
324 bool scanarray(CFArrayRef obj);
325 bool scanformat(CFTypeRef obj);
326
327 enum Typescan { fail = -1, more = 0, done = 1 };
328 Typescan typescan(CFTypeRef obj, CFTypeID type);
329
330 template <class Value>
331 bool scannumber(CFTypeRef obj);
332
333 template <class Type>
334 void store(Type value);
335
336 bool suppress; // output suppression
337 };
338
339
340 //
341 // Master scan function
342 //
343 bool CFScan::scan(CFTypeRef obj)
344 {
345 while (next() == '@')
346 parameter();
347 switch (next()) {
348 case '\0':
349 return true; // done, okay
350 case '{':
351 if (obj && CFGetTypeID(obj) != CFDictionaryGetTypeID())
352 return false;
353 return scandictionary(CFDictionaryRef(obj));
354 case '[':
355 if (obj && CFGetTypeID(obj) != CFArrayGetTypeID())
356 return false;
357 return scanarray(CFArrayRef(obj));
358 case '%': // return this value in some form
359 return scanformat(obj);
360 case '=': // match value
361 {
362 ++format;
363 CFTypeRef match = make();
364 bool rc = CFEqual(obj, match);
365 CFRelease(match);
366 return rc;
367 }
368 case ']':
369 case '}':
370 assert(false); // unexpected
371 return false;
372 default:
373 assert(false);
374 return false;
375 }
376 }
377
378
379 //
380 // Primitive type-match helper.
381 // Ensures the object has the CF runtime type required, and processes
382 // the %?o format (return CFTypeRef) and %?n format (ignore value).
383 //
384 CFScan::Typescan CFScan::typescan(CFTypeRef obj, CFTypeID type)
385 {
386 if (obj && CFGetTypeID(obj) != type)
387 return fail;
388 switch (*++format) {
389 case F_OBJECT: // return CFTypeRef
390 ++format;
391 store<CFTypeRef>(obj);
392 return done;
393 case 'n': // suppress assignment
394 ++format;
395 return done;
396 default:
397 return more;
398 }
399 }
400
401
402 //
403 // Store a value into the next varargs slot, unless output suppression is on.
404 //
405 template <class Type>
406 void CFScan::store(Type value)
407 {
408 if (!suppress)
409 *va_arg(args, Type *) = value;
410 }
411
412
413 //
414 // Convert a CFNumber to an external numeric form
415 //
416 template <class Value>
417 bool CFScan::scannumber(CFTypeRef obj)
418 {
419 ++format; // consume format code
420 if (!obj)
421 return true; // suppressed, okay
422 if (CFGetTypeID(obj) != CFNumberGetTypeID())
423 return false;
424 store<Value>(cfNumber<Value>(CFNumberRef(obj)));
425 return true;
426 }
427
428
429 //
430 // Process % scan forms.
431 // This delivers the object value, scanf-style, somehow.
432 //
433 bool CFScan::scanformat(CFTypeRef obj)
434 {
435 switch (*++format) {
436 case F_OBJECT:
437 store<CFTypeRef>(obj);
438 return true;
439 case F_ARRAY: // %a*
440 return typescan(obj, CFArrayGetTypeID()) == done;
441 case F_BOOLEAN:
442 if (Typescan rc = typescan(obj, CFBooleanGetTypeID()))
443 return rc == done;
444 switch (*format) {
445 case 'f': // %Bf - two arguments (value, &variable)
446 {
447 unsigned flag = va_arg(args, unsigned);
448 unsigned *value = va_arg(args, unsigned *);
449 if (obj == kCFBooleanTrue && !suppress)
450 *value |= flag;
451 return true;
452 }
453 default: // %b - CFBoolean as int boolean
454 store<int>(obj == kCFBooleanTrue);
455 return true;
456 }
457 case F_DICTIONARY:
458 return typescan(obj, CFDictionaryGetTypeID()) == done;
459 case 'd': // %d - int
460 return scannumber<int>(obj);
461 case F_NUMBER:
462 return typescan(obj, CFNumberGetTypeID()) == done;
463 case F_STRING:
464 case 's':
465 if (Typescan rc = typescan(obj, CFStringGetTypeID()))
466 return rc == done;
467 // %s
468 store<std::string>(cfString(CFStringRef(obj)));
469 return true;
470 case 'u':
471 return scannumber<unsigned int>(obj);
472 case F_DATA:
473 return typescan(obj, CFDataGetTypeID()) == done;
474 default:
475 assert(false);
476 return false;
477 }
478 }
479
480
481 bool CFScan::scandictionary(CFDictionaryRef obj)
482 {
483 ++format; // skip '{'
484 while (next() != '}') {
485 bool optional = next('?');
486 if (CFTypeRef key = make()) {
487 bool oldSuppress = suppress;
488 CFTypeRef elem = obj ? CFDictionaryGetValue(obj, key) : NULL;
489 if (elem || optional) {
490 suppress |= (elem == NULL);
491 if (next('=')) {
492 if (scan(elem)) {
493 suppress = oldSuppress; // restore
494 CFRelease(key);
495 continue;
496 }
497 }
498 }
499 CFRelease(key);
500 return false;
501 } else {
502 assert(false); // bad format
503 return false;
504 }
505 }
506 return true;
507 }
508
509
510 bool CFScan::scanarray(CFArrayRef obj)
511 {
512 ++format; // skip '['
513 CFIndex length = CFArrayGetCount(obj);
514 for (int pos = 0; pos < length; ++pos) {
515 if (next() == ']')
516 return true;
517 if (!scan(CFArrayGetValueAtIndex(obj, pos)))
518 return false;
519 }
520 return false; // array length exceeded
521 }
522
523
524 //
525 // Run down a "dictionary path", validating heavily.
526 //
527 CFTypeRef CFScan::dictpath(CFTypeRef obj)
528 {
529 while (next()) { // while we've got more text
530 next('.'); // optional
531 if (obj == NULL || CFGetTypeID(obj) != CFDictionaryGetTypeID())
532 return NULL;
533 CFTypeRef key = make();
534 obj = CFDictionaryGetValue(CFDictionaryRef(obj), key);
535 CFRelease(key);
536 }
537 return obj;
538 }
539
540
541 //
542 // The public functions
543 //
544 CFTypeRef cfmake(const char *format, ...)
545 {
546 va_list args;
547 va_start(args, format);
548 CFTypeRef result = CFMake(format, args).make();
549 va_end(args);
550 return result;
551 }
552
553 CFTypeRef vcfmake(const char *format, va_list args)
554 {
555 return CFMake(format, args).make();
556 }
557
558 CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...)
559 {
560 va_list args;
561 va_start(args, format);
562 CFDictionaryRef result = CFMake(format, args).addto(dict);
563 va_end(args);
564 return result;
565 }
566
567
568 bool cfscan(CFTypeRef obj, const char *format, ...)
569 {
570 va_list args;
571 va_start(args, format);
572 bool result = vcfscan(obj, format, args);
573 va_end(args);
574 return result;
575 }
576
577 bool vcfscan(CFTypeRef obj, const char *format, va_list args)
578 {
579 return CFScan(format, args).scan(obj);
580 }
581
582
583 CFTypeRef cfget(CFTypeRef obj, const char *format, ...)
584 {
585 va_list args;
586 va_start(args, format);
587 CFTypeRef result = vcfget(obj, format, args);
588 va_end(args);
589 return result;
590 }
591
592 CFTypeRef vcfget(CFTypeRef obj, const char *format, va_list args)
593 {
594 return CFScan(format, args).dictpath(obj);
595 }
596
597 } // end namespace Security