]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/cfmunge.cpp
Security-57740.60.18.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 #include <utilities/SecCFRelease.h>
33
34 namespace Security {
35
36
37 //
38 // Format codes for consistency
39 //
40 #define F_ARRAY 'A'
41 #define F_BOOLEAN 'B'
42 #define F_DATA 'X'
43 #define F_DICTIONARY 'D'
44 #define F_OBJECT 'O'
45 #define F_STRING 'S'
46 #define F_NUMBER 'N'
47
48
49 //
50 // Initialize a CFMunge. We start out with the default CFAllocator, and
51 // we do not throw errors.
52 //
53 CFMunge::CFMunge(const char *fmt, va_list arg)
54 : format(fmt), allocator(NULL), error(errSecSuccess)
55 {
56 va_copy(args, arg);
57 }
58
59 CFMunge::~CFMunge()
60 {
61 va_end(args);
62 }
63
64
65 //
66 // Skip whitespace and other fluff and deliver the next significant character.
67 //
68 char CFMunge::next()
69 {
70 while (*format && (isspace(*format) || *format == ',')) ++format;
71 return *format;
72 }
73
74
75 //
76 // Locate and consume an optional character
77 //
78 bool CFMunge::next(char c)
79 {
80 if (next() == c) {
81 ++format;
82 return true;
83 } else
84 return false;
85 }
86
87
88 //
89 // Process @? parameter specifications.
90 // The @ operator is used for side effects, and does not return a value.
91 //
92 bool CFMunge::parameter()
93 {
94 switch (*++format) {
95 case 'A':
96 ++format;
97 allocator = va_arg(args, CFAllocatorRef);
98 return true;
99 case 'E':
100 ++format;
101 error = va_arg(args, OSStatus);
102 return true;
103 default:
104 return false;
105 }
106 }
107
108
109 //
110 // The top constructor.
111 //
112 CFTypeRef CFMake::make()
113 {
114 while (next() == '@')
115 parameter();
116 switch (next()) {
117 case '\0':
118 return NULL;
119 case '{':
120 return makedictionary();
121 case '[':
122 return makearray();
123 case '\'':
124 return makestring();
125 case '%':
126 return makeformat();
127 case '#':
128 return makespecial();
129 case ']':
130 case '}':
131 return NULL; // error
132 default:
133 if (isdigit(*format) || *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 CFReleaseSafe(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 = NULL;
294 if (next('+')) { // {+%O, => copy array argument, then proceed
295 if (next('%') && next('O')) {
296 CFArrayRef source = va_arg(args, CFArrayRef);
297 array = CFArrayCreateMutableCopy(allocator, 0, source);
298 if (next('}'))
299 return array;
300 } else
301 return NULL; // bad syntax
302 } else {
303 array = makeCFMutableArray(0);
304 }
305 while (next() != ']') {
306 CFTypeRef value = make();
307 if (value == NULL) {
308 CFRelease(array);
309 return NULL;
310 }
311 CFArrayAppendValue(array, value);
312 CFRelease(value);
313 }
314 ++format;
315 return array;
316 }
317
318
319 //
320 // A CFScan processes its format by parsing through an existing CF object
321 // structure, matching and extracting values as directed. Note that CFScan
322 // is a structure (tree) scanner rather than a linear parser, and will happily
323 // parse out a subset of the input object graph.
324 //
325 class CFScan : public CFMake {
326 public:
327 CFScan(const char *format, va_list args)
328 : CFMake(format, args), suppress(false) { }
329
330 bool scan(CFTypeRef obj);
331 CFTypeRef dictpath(CFTypeRef obj);
332
333 protected:
334 bool scandictionary(CFDictionaryRef obj);
335 bool scanarray(CFArrayRef obj);
336 bool scanformat(CFTypeRef obj);
337
338 enum Typescan { fail = -1, more = 0, done = 1 };
339 Typescan typescan(CFTypeRef obj, CFTypeID type);
340
341 template <class Value>
342 bool scannumber(CFTypeRef obj);
343
344 template <class Type>
345 void store(Type value);
346
347 bool suppress; // output suppression
348 };
349
350
351 //
352 // Master scan function
353 //
354 bool CFScan::scan(CFTypeRef obj)
355 {
356 while (next() == '@')
357 parameter();
358 switch (next()) {
359 case '\0':
360 return true; // done, okay
361 case '{':
362 if (obj && CFGetTypeID(obj) != CFDictionaryGetTypeID())
363 return false;
364 return scandictionary(CFDictionaryRef(obj));
365 case '[':
366 if (obj && CFGetTypeID(obj) != CFArrayGetTypeID())
367 return false;
368 return scanarray(CFArrayRef(obj));
369 case '%': // return this value in some form
370 return scanformat(obj);
371 case '=': // match value
372 {
373 ++format;
374 CFTypeRef match = make();
375 bool rc = CFEqual(obj, match);
376 CFRelease(match);
377 return rc;
378 }
379 case ']':
380 case '}':
381 assert(false); // unexpected
382 return false;
383 default:
384 assert(false);
385 return false;
386 }
387 }
388
389
390 //
391 // Primitive type-match helper.
392 // Ensures the object has the CF runtime type required, and processes
393 // the %?o format (return CFTypeRef) and %?n format (ignore value).
394 //
395 CFScan::Typescan CFScan::typescan(CFTypeRef obj, CFTypeID type)
396 {
397 if (obj && CFGetTypeID(obj) != type)
398 return fail;
399 switch (*++format) {
400 case F_OBJECT: // return CFTypeRef
401 ++format;
402 store<CFTypeRef>(obj);
403 return done;
404 case 'n': // suppress assignment
405 ++format;
406 return done;
407 default:
408 return more;
409 }
410 }
411
412
413 //
414 // Store a value into the next varargs slot, unless output suppression is on.
415 //
416 template <class Type>
417 void CFScan::store(Type value)
418 {
419 if (!suppress)
420 *va_arg(args, Type *) = value;
421 }
422
423
424 //
425 // Convert a CFNumber to an external numeric form
426 //
427 template <class Value>
428 bool CFScan::scannumber(CFTypeRef obj)
429 {
430 ++format; // consume format code
431 if (!obj)
432 return true; // suppressed, okay
433 if (CFGetTypeID(obj) != CFNumberGetTypeID())
434 return false;
435 store<Value>(cfNumber<Value>(CFNumberRef(obj)));
436 return true;
437 }
438
439
440 //
441 // Process % scan forms.
442 // This delivers the object value, scanf-style, somehow.
443 //
444 bool CFScan::scanformat(CFTypeRef obj)
445 {
446 switch (*++format) {
447 case F_OBJECT:
448 store<CFTypeRef>(obj);
449 return true;
450 case F_ARRAY: // %a*
451 return typescan(obj, CFArrayGetTypeID()) == done;
452 case F_BOOLEAN:
453 if (Typescan rc = typescan(obj, CFBooleanGetTypeID()))
454 return rc == done;
455 switch (*format) {
456 case 'f': // %Bf - two arguments (value, &variable)
457 {
458 unsigned flag = va_arg(args, unsigned);
459 unsigned *value = va_arg(args, unsigned *);
460 if (obj == kCFBooleanTrue && !suppress)
461 *value |= flag;
462 return true;
463 }
464 default: // %b - CFBoolean as int boolean
465 store<int>(obj == kCFBooleanTrue);
466 return true;
467 }
468 case F_DICTIONARY:
469 return typescan(obj, CFDictionaryGetTypeID()) == done;
470 case 'd': // %d - int
471 return scannumber<int>(obj);
472 case F_NUMBER:
473 return typescan(obj, CFNumberGetTypeID()) == done;
474 case F_STRING:
475 case 's':
476 if (Typescan rc = typescan(obj, CFStringGetTypeID()))
477 return rc == done;
478 // %s
479 store<std::string>(cfString(CFStringRef(obj)));
480 return true;
481 case 'u':
482 return scannumber<unsigned int>(obj);
483 case F_DATA:
484 return typescan(obj, CFDataGetTypeID()) == done;
485 default:
486 assert(false);
487 return false;
488 }
489 }
490
491
492 bool CFScan::scandictionary(CFDictionaryRef obj)
493 {
494 ++format; // skip '{'
495 while (next() != '}') {
496 bool optional = next('?');
497 if (CFTypeRef key = make()) {
498 bool oldSuppress = suppress;
499 CFTypeRef elem = obj ? CFDictionaryGetValue(obj, key) : NULL;
500 if (elem || optional) {
501 suppress |= (elem == NULL);
502 if (next('=')) {
503 if (scan(elem)) {
504 suppress = oldSuppress; // restore
505 CFRelease(key);
506 continue;
507 }
508 }
509 }
510 CFRelease(key);
511 return false;
512 } else {
513 assert(false); // bad format
514 return false;
515 }
516 }
517 return true;
518 }
519
520
521 bool CFScan::scanarray(CFArrayRef obj)
522 {
523 ++format; // skip '['
524 CFIndex length = CFArrayGetCount(obj);
525 for (int pos = 0; pos < length; ++pos) {
526 if (next() == ']')
527 return true;
528 if (!scan(CFArrayGetValueAtIndex(obj, pos)))
529 return false;
530 }
531 return false; // array length exceeded
532 }
533
534
535 //
536 // Run down a "dictionary path", validating heavily.
537 //
538 CFTypeRef CFScan::dictpath(CFTypeRef obj)
539 {
540 while (next()) { // while we've got more text
541 next('.'); // optional
542 if (obj == NULL || CFGetTypeID(obj) != CFDictionaryGetTypeID())
543 return NULL;
544 CFTypeRef key = make();
545 obj = CFDictionaryGetValue(CFDictionaryRef(obj), key);
546 CFRelease(key);
547 }
548 return obj;
549 }
550
551
552 //
553 // The public functions
554 //
555 CFTypeRef cfmake(const char *format, ...)
556 {
557 va_list args;
558 va_start(args, format);
559 CFTypeRef result = CFMake(format, args).make();
560 va_end(args);
561 return result;
562 }
563
564 CFTypeRef vcfmake(const char *format, va_list args)
565 {
566 return CFMake(format, args).make();
567 }
568
569 CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...)
570 {
571 va_list args;
572 va_start(args, format);
573 CFDictionaryRef result = CFMake(format, args).addto(dict);
574 va_end(args);
575 return result;
576 }
577
578
579 bool cfscan(CFTypeRef obj, const char *format, ...)
580 {
581 va_list args;
582 va_start(args, format);
583 bool result = vcfscan(obj, format, args);
584 va_end(args);
585 return result;
586 }
587
588 bool vcfscan(CFTypeRef obj, const char *format, va_list args)
589 {
590 return CFScan(format, args).scan(obj);
591 }
592
593
594 CFTypeRef cfget(CFTypeRef obj, const char *format, ...)
595 {
596 va_list args;
597 va_start(args, format);
598 CFTypeRef result = vcfget(obj, format, args);
599 va_end(args);
600 return result;
601 }
602
603 CFTypeRef vcfget(CFTypeRef obj, const char *format, va_list args)
604 {
605 return CFScan(format, args).dictpath(obj);
606 }
607
608 } // end namespace Security