]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/cfmunge.cpp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / cfmunge.cpp
1 /*
2 * Copyright (c) 2006-2007,2011,2013-2014 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 return NULL; // error
131 default:
132 if (isdigit(*format) || *format == '-')
133 return makenumber();
134 else if (isalpha(*format))
135 return makestring();
136 else {
137 assert(false);
138 return NULL;
139 }
140 }
141 }
142
143
144 CFTypeRef CFMake::makeformat()
145 {
146 ++format;
147 switch (*format++) {
148 case 'b': // blob (pointer, length)
149 {
150 const void *data = va_arg(args, const void *);
151 size_t length = va_arg(args, size_t);
152 return CFDataCreate(allocator, (const UInt8 *)data, length);
153 }
154 case F_BOOLEAN: // boolean (with int promotion)
155 return va_arg(args, int) ? kCFBooleanTrue : kCFBooleanFalse;
156 case 'd':
157 return makeCFNumber(va_arg(args, int));
158 case 's':
159 return CFStringCreateWithCString(allocator, va_arg(args, const char *),
160 kCFStringEncodingUTF8);
161 case F_OBJECT:
162 return CFRetain(va_arg(args, CFTypeRef));
163 case 'u':
164 return makeCFNumber(va_arg(args, unsigned int));
165 default:
166 assert(false);
167 return NULL;
168 }
169 }
170
171
172 CFTypeRef CFMake::makespecial()
173 {
174 ++format;
175 switch (*format++) {
176 case 'N':
177 return kCFNull;
178 case 't':
179 case 'T':
180 return kCFBooleanTrue;
181 case 'f':
182 case 'F':
183 return kCFBooleanFalse;
184 default:
185 assert(false);
186 return NULL;
187 }
188 }
189
190
191 CFTypeRef CFMake::makenumber()
192 {
193 double value = strtod(format, (char **)&format);
194 return CFNumberCreate(allocator, kCFNumberDoubleType, &value);
195 }
196
197
198 //
199 // Embedded strings can either be alphanumeric (only), or delimited with single quotes ''.
200 // No escapes are processed within such quotes. If you want arbitrary string values, use %s.
201 //
202 CFTypeRef CFMake::makestring()
203 {
204 const char *start, *end;
205 if (*format == '\'') {
206 start = ++format; // next quote
207 if (!(end = strchr(format, '\''))) {
208 assert(false);
209 return NULL;
210 }
211 format = end + 1;
212 } else {
213 start = format;
214 for (end = start + 1; isalnum(*end); ++end) ;
215 format = end;
216 }
217 return CFStringCreateWithBytes(allocator,
218 (const UInt8 *)start, end - start,
219 kCFStringEncodingUTF8, false);
220 }
221
222
223 //
224 // Construct a CFDictionary
225 //
226 CFTypeRef CFMake::makedictionary()
227 {
228 ++format; // next '{'
229 next('!'); // indicates mutable (currently always true)
230 CFMutableDictionaryRef dict;
231 if (next('+')) { // {+%O, => copy dictionary argument, then proceed
232 if (next('%') && next('O')) {
233 CFDictionaryRef source = va_arg(args, CFDictionaryRef);
234 dict = CFDictionaryCreateMutableCopy(allocator, NULL, source);
235 if (next('}'))
236 return dict;
237 } else
238 return NULL; // bad syntax
239 } else
240 dict = CFDictionaryCreateMutable(allocator, 0,
241 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
242 if (add(dict))
243 return dict;
244 else {
245 CFRelease(dict);
246 return NULL;
247 }
248 }
249
250 CFDictionaryRef CFMake::add(CFMutableDictionaryRef dict)
251 {
252 while (next() != '}') {
253 CFTypeRef key = make();
254 if (key == NULL)
255 return NULL;
256 if (!next('=')) {
257 CFRelease(key);
258 return NULL;
259 }
260 if (CFTypeRef value = make()) {
261 CFDictionaryAddValue(dict, key, value);
262 CFRelease(key);
263 CFRelease(value);
264 } else {
265 CFRelease(key);
266 return NULL;
267 }
268 }
269 ++format;
270 return dict;
271 }
272
273
274 CFDictionaryRef CFMake::addto(CFMutableDictionaryRef dict)
275 {
276 if (next('{'))
277 return add(dict);
278 else {
279 assert(false);
280 return NULL;
281 }
282 }
283
284
285 //
286 // Construct a CFArray
287 //
288 CFTypeRef CFMake::makearray()
289 {
290 ++format; // next '['
291 next('!'); // indicates mutable (currently always)
292 CFMutableArrayRef array = NULL;
293 if (next('+')) { // {+%O, => copy array argument, then proceed
294 if (next('%') && next('O')) {
295 CFArrayRef source = va_arg(args, CFArrayRef);
296 array = CFArrayCreateMutableCopy(allocator, 0, source);
297 if (next('}'))
298 return array;
299 } else
300 return NULL; // bad syntax
301 } else {
302 array = makeCFMutableArray(0);
303 }
304 while (next() != ']') {
305 CFTypeRef value = make();
306 if (value == NULL) {
307 CFRelease(array);
308 return NULL;
309 }
310 CFArrayAppendValue(array, value);
311 CFRelease(value);
312 }
313 ++format;
314 return array;
315 }
316
317
318 //
319 // A CFScan processes its format by parsing through an existing CF object
320 // structure, matching and extracting values as directed. Note that CFScan
321 // is a structure (tree) scanner rather than a linear parser, and will happily
322 // parse out a subset of the input object graph.
323 //
324 class CFScan : public CFMake {
325 public:
326 CFScan(const char *format, va_list args)
327 : CFMake(format, args), suppress(false) { }
328
329 bool scan(CFTypeRef obj);
330 CFTypeRef dictpath(CFTypeRef obj);
331
332 protected:
333 bool scandictionary(CFDictionaryRef obj);
334 bool scanarray(CFArrayRef obj);
335 bool scanformat(CFTypeRef obj);
336
337 enum Typescan { fail = -1, more = 0, done = 1 };
338 Typescan typescan(CFTypeRef obj, CFTypeID type);
339
340 template <class Value>
341 bool scannumber(CFTypeRef obj);
342
343 template <class Type>
344 void store(Type value);
345
346 bool suppress; // output suppression
347 };
348
349
350 //
351 // Master scan function
352 //
353 bool CFScan::scan(CFTypeRef obj)
354 {
355 while (next() == '@')
356 parameter();
357 switch (next()) {
358 case '\0':
359 return true; // done, okay
360 case '{':
361 if (obj && CFGetTypeID(obj) != CFDictionaryGetTypeID())
362 return false;
363 return scandictionary(CFDictionaryRef(obj));
364 case '[':
365 if (obj && CFGetTypeID(obj) != CFArrayGetTypeID())
366 return false;
367 return scanarray(CFArrayRef(obj));
368 case '%': // return this value in some form
369 return scanformat(obj);
370 case '=': // match value
371 {
372 ++format;
373 CFTypeRef match = make();
374 bool rc = CFEqual(obj, match);
375 CFRelease(match);
376 return rc;
377 }
378 case ']':
379 case '}':
380 assert(false); // unexpected
381 return false;
382 default:
383 assert(false);
384 return false;
385 }
386 }
387
388
389 //
390 // Primitive type-match helper.
391 // Ensures the object has the CF runtime type required, and processes
392 // the %?o format (return CFTypeRef) and %?n format (ignore value).
393 //
394 CFScan::Typescan CFScan::typescan(CFTypeRef obj, CFTypeID type)
395 {
396 if (obj && CFGetTypeID(obj) != type)
397 return fail;
398 switch (*++format) {
399 case F_OBJECT: // return CFTypeRef
400 ++format;
401 store<CFTypeRef>(obj);
402 return done;
403 case 'n': // suppress assignment
404 ++format;
405 return done;
406 default:
407 return more;
408 }
409 }
410
411
412 //
413 // Store a value into the next varargs slot, unless output suppression is on.
414 //
415 template <class Type>
416 void CFScan::store(Type value)
417 {
418 if (!suppress)
419 *va_arg(args, Type *) = value;
420 }
421
422
423 //
424 // Convert a CFNumber to an external numeric form
425 //
426 template <class Value>
427 bool CFScan::scannumber(CFTypeRef obj)
428 {
429 ++format; // consume format code
430 if (!obj)
431 return true; // suppressed, okay
432 if (CFGetTypeID(obj) != CFNumberGetTypeID())
433 return false;
434 store<Value>(cfNumber<Value>(CFNumberRef(obj)));
435 return true;
436 }
437
438
439 //
440 // Process % scan forms.
441 // This delivers the object value, scanf-style, somehow.
442 //
443 bool CFScan::scanformat(CFTypeRef obj)
444 {
445 switch (*++format) {
446 case F_OBJECT:
447 store<CFTypeRef>(obj);
448 return true;
449 case F_ARRAY: // %a*
450 return typescan(obj, CFArrayGetTypeID()) == done;
451 case F_BOOLEAN:
452 if (Typescan rc = typescan(obj, CFBooleanGetTypeID()))
453 return rc == done;
454 switch (*format) {
455 case 'f': // %Bf - two arguments (value, &variable)
456 {
457 unsigned flag = va_arg(args, unsigned);
458 unsigned *value = va_arg(args, unsigned *);
459 if (obj == kCFBooleanTrue && !suppress)
460 *value |= flag;
461 return true;
462 }
463 default: // %b - CFBoolean as int boolean
464 store<int>(obj == kCFBooleanTrue);
465 return true;
466 }
467 case F_DICTIONARY:
468 return typescan(obj, CFDictionaryGetTypeID()) == done;
469 case 'd': // %d - int
470 return scannumber<int>(obj);
471 case F_NUMBER:
472 return typescan(obj, CFNumberGetTypeID()) == done;
473 case F_STRING:
474 case 's':
475 if (Typescan rc = typescan(obj, CFStringGetTypeID()))
476 return rc == done;
477 // %s
478 store<std::string>(cfString(CFStringRef(obj)));
479 return true;
480 case 'u':
481 return scannumber<unsigned int>(obj);
482 case F_DATA:
483 return typescan(obj, CFDataGetTypeID()) == done;
484 default:
485 assert(false);
486 return false;
487 }
488 }
489
490
491 bool CFScan::scandictionary(CFDictionaryRef obj)
492 {
493 ++format; // skip '{'
494 while (next() != '}') {
495 bool optional = next('?');
496 if (CFTypeRef key = make()) {
497 bool oldSuppress = suppress;
498 CFTypeRef elem = obj ? CFDictionaryGetValue(obj, key) : NULL;
499 if (elem || optional) {
500 suppress |= (elem == NULL);
501 if (next('=')) {
502 if (scan(elem)) {
503 suppress = oldSuppress; // restore
504 CFRelease(key);
505 continue;
506 }
507 }
508 }
509 CFRelease(key);
510 return false;
511 } else {
512 assert(false); // bad format
513 return false;
514 }
515 }
516 return true;
517 }
518
519
520 bool CFScan::scanarray(CFArrayRef obj)
521 {
522 ++format; // skip '['
523 CFIndex length = CFArrayGetCount(obj);
524 for (int pos = 0; pos < length; ++pos) {
525 if (next() == ']')
526 return true;
527 if (!scan(CFArrayGetValueAtIndex(obj, pos)))
528 return false;
529 }
530 return false; // array length exceeded
531 }
532
533
534 //
535 // Run down a "dictionary path", validating heavily.
536 //
537 CFTypeRef CFScan::dictpath(CFTypeRef obj)
538 {
539 while (next()) { // while we've got more text
540 next('.'); // optional
541 if (obj == NULL || CFGetTypeID(obj) != CFDictionaryGetTypeID())
542 return NULL;
543 CFTypeRef key = make();
544 obj = CFDictionaryGetValue(CFDictionaryRef(obj), key);
545 CFRelease(key);
546 }
547 return obj;
548 }
549
550
551 //
552 // The public functions
553 //
554 CFTypeRef cfmake(const char *format, ...)
555 {
556 va_list args;
557 va_start(args, format);
558 CFTypeRef result = CFMake(format, args).make();
559 va_end(args);
560 return result;
561 }
562
563 CFTypeRef vcfmake(const char *format, va_list args)
564 {
565 return CFMake(format, args).make();
566 }
567
568 CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...)
569 {
570 va_list args;
571 va_start(args, format);
572 CFDictionaryRef result = CFMake(format, args).addto(dict);
573 va_end(args);
574 return result;
575 }
576
577
578 bool cfscan(CFTypeRef obj, const char *format, ...)
579 {
580 va_list args;
581 va_start(args, format);
582 bool result = vcfscan(obj, format, args);
583 va_end(args);
584 return result;
585 }
586
587 bool vcfscan(CFTypeRef obj, const char *format, va_list args)
588 {
589 return CFScan(format, args).scan(obj);
590 }
591
592
593 CFTypeRef cfget(CFTypeRef obj, const char *format, ...)
594 {
595 va_list args;
596 va_start(args, format);
597 CFTypeRef result = vcfget(obj, format, args);
598 va_end(args);
599 return result;
600 }
601
602 CFTypeRef vcfget(CFTypeRef obj, const char *format, va_list args)
603 {
604 return CFScan(format, args).dictpath(obj);
605 }
606
607 } // end namespace Security