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