]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/cfmunge.cpp
Security-59754.41.1.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 }
228
229 if (dict == NULL) {
230 return dict;
231 }
232
233 if (add(dict)) {
234 return dict;
235 } else {
236 CFReleaseSafe(dict);
237 return NULL;
238 }
239 }
240
241 CFDictionaryRef CFMake::add(CFMutableDictionaryRef dict)
242 {
243 while (next() != '}') {
244 CFTypeRef key = make();
245 if (key == NULL)
246 return NULL;
247 if (!next('=')) {
248 CFRelease(key);
249 return NULL;
250 }
251 if (CFTypeRef value = make()) {
252 CFDictionaryAddValue(dict, key, value);
253 CFRelease(key);
254 CFRelease(value);
255 } else {
256 CFRelease(key);
257 return NULL;
258 }
259 }
260 ++format;
261 return dict;
262 }
263
264
265 CFDictionaryRef CFMake::addto(CFMutableDictionaryRef dict)
266 {
267 if (next('{'))
268 return add(dict);
269 else {
270 assert(false);
271 return NULL;
272 }
273 }
274
275
276 //
277 // Construct a CFArray
278 //
279 CFTypeRef CF_RETURNS_RETAINED CFMake::makearray()
280 {
281 ++format; // next '['
282 next('!'); // indicates mutable (currently always)
283 CFMutableArrayRef array = NULL;
284 if (next('+')) { // {+%O, => copy array argument, then proceed
285 if (next('%') && next('O')) {
286 CFArrayRef source = va_arg(*args, CFArrayRef);
287 array = CFArrayCreateMutableCopy(allocator, 0, source);
288 if (next('}'))
289 return array;
290 } else
291 return NULL; // bad syntax
292 } else {
293 array = makeCFMutableArray(0);
294 }
295 while (next() != ']') {
296 CFTypeRef value = make();
297 if (value == NULL) {
298 CFRelease(array);
299 return NULL;
300 }
301 CFArrayAppendValue(array, value);
302 CFRelease(value);
303 }
304 ++format;
305 return array;
306 }
307
308
309 //
310 // A CFScan processes its format by parsing through an existing CF object
311 // structure, matching and extracting values as directed. Note that CFScan
312 // is a structure (tree) scanner rather than a linear parser, and will happily
313 // parse out a subset of the input object graph.
314 //
315 class CFScan : public CFMake {
316 public:
317 CFScan(const char *format, va_list *args)
318 : CFMake(format, args), suppress(false) { }
319
320 bool scan(CFTypeRef obj);
321 CFTypeRef dictpath(CFTypeRef obj);
322
323 protected:
324 bool scandictionary(CFDictionaryRef obj);
325 bool scanarray(CFArrayRef obj);
326 bool scanformat(CFTypeRef obj);
327
328 enum Typescan { fail = -1, more = 0, done = 1 };
329 Typescan typescan(CFTypeRef obj, CFTypeID type);
330
331 template <class Value>
332 bool scannumber(CFTypeRef obj);
333
334 template <class Type>
335 void store(Type value);
336
337 bool suppress; // output suppression
338 };
339
340
341 //
342 // Master scan function
343 //
344 bool CFScan::scan(CFTypeRef obj)
345 {
346 while (next() == '@')
347 parameter();
348 switch (next()) {
349 case '\0':
350 return true; // done, okay
351 case '{':
352 if (obj && CFGetTypeID(obj) != CFDictionaryGetTypeID())
353 return false;
354 return scandictionary(CFDictionaryRef(obj));
355 case '[':
356 if (obj && CFGetTypeID(obj) != CFArrayGetTypeID())
357 return false;
358 return scanarray(CFArrayRef(obj));
359 case '%': // return this value in some form
360 return scanformat(obj);
361 case '=': // match value
362 {
363 ++format;
364 CFTypeRef match = make();
365 bool rc = CFEqual(obj, match);
366 CFRelease(match);
367 return rc;
368 }
369 case ']':
370 case '}':
371 assert(false); // unexpected
372 return false;
373 default:
374 assert(false);
375 return false;
376 }
377 }
378
379
380 //
381 // Primitive type-match helper.
382 // Ensures the object has the CF runtime type required, and processes
383 // the %?o format (return CFTypeRef) and %?n format (ignore value).
384 //
385 CFScan::Typescan CFScan::typescan(CFTypeRef obj, CFTypeID type)
386 {
387 if (obj && CFGetTypeID(obj) != type)
388 return fail;
389 switch (*++format) {
390 case F_OBJECT: // return CFTypeRef
391 ++format;
392 store<CFTypeRef>(obj);
393 return done;
394 case 'n': // suppress assignment
395 ++format;
396 return done;
397 default:
398 return more;
399 }
400 }
401
402
403 //
404 // Store a value into the next varargs slot, unless output suppression is on.
405 //
406 template <class Type>
407 void CFScan::store(Type value)
408 {
409 if (!suppress)
410 *va_arg(*args, Type *) = value;
411 }
412
413
414 //
415 // Convert a CFNumber to an external numeric form
416 //
417 template <class Value>
418 bool CFScan::scannumber(CFTypeRef obj)
419 {
420 ++format; // consume format code
421 if (!obj)
422 return true; // suppressed, okay
423 if (CFGetTypeID(obj) != CFNumberGetTypeID())
424 return false;
425 store<Value>(cfNumber<Value>(CFNumberRef(obj)));
426 return true;
427 }
428
429
430 //
431 // Process % scan forms.
432 // This delivers the object value, scanf-style, somehow.
433 //
434 bool CFScan::scanformat(CFTypeRef obj)
435 {
436 switch (*++format) {
437 case F_OBJECT:
438 store<CFTypeRef>(obj);
439 return true;
440 case F_ARRAY: // %a*
441 return typescan(obj, CFArrayGetTypeID()) == done;
442 case F_BOOLEAN:
443 if (Typescan rc = typescan(obj, CFBooleanGetTypeID()))
444 return rc == done;
445 switch (*format) {
446 case 'f': // %Bf - two arguments (value, &variable)
447 {
448 unsigned flag = va_arg(*args, unsigned);
449 unsigned *value = va_arg(*args, unsigned *);
450 if (obj == kCFBooleanTrue && !suppress)
451 *value |= flag;
452 return true;
453 }
454 default: // %b - CFBoolean as int boolean
455 store<int>(obj == kCFBooleanTrue);
456 return true;
457 }
458 case F_DICTIONARY:
459 return typescan(obj, CFDictionaryGetTypeID()) == done;
460 case 'd': // %d - int
461 return scannumber<int>(obj);
462 case F_NUMBER:
463 return typescan(obj, CFNumberGetTypeID()) == done;
464 case F_STRING:
465 case 's':
466 if (Typescan rc = typescan(obj, CFStringGetTypeID()))
467 return rc == done;
468 // %s
469 store<std::string>(cfString(CFStringRef(obj)));
470 return true;
471 case 'u':
472 return scannumber<unsigned int>(obj);
473 case F_DATA:
474 return typescan(obj, CFDataGetTypeID()) == done;
475 default:
476 assert(false);
477 return false;
478 }
479 }
480
481
482 bool CFScan::scandictionary(CFDictionaryRef obj)
483 {
484 ++format; // skip '{'
485 while (next() != '}') {
486 bool optional = next('?');
487 if (CFTypeRef key = make()) {
488 bool oldSuppress = suppress;
489 CFTypeRef elem = obj ? CFDictionaryGetValue(obj, key) : NULL;
490 if (elem || optional) {
491 suppress |= (elem == NULL);
492 if (next('=')) {
493 if (scan(elem)) {
494 suppress = oldSuppress; // restore
495 CFRelease(key);
496 continue;
497 }
498 }
499 }
500 CFRelease(key);
501 return false;
502 } else {
503 assert(false); // bad format
504 return false;
505 }
506 }
507 return true;
508 }
509
510
511 bool CFScan::scanarray(CFArrayRef obj)
512 {
513 ++format; // skip '['
514 CFIndex length = CFArrayGetCount(obj);
515 for (int pos = 0; pos < length; ++pos) {
516 if (next() == ']')
517 return true;
518 if (!scan(CFArrayGetValueAtIndex(obj, pos)))
519 return false;
520 }
521 return false; // array length exceeded
522 }
523
524
525 //
526 // Run down a "dictionary path", validating heavily.
527 //
528 CFTypeRef CFScan::dictpath(CFTypeRef obj)
529 {
530 while (next()) { // while we've got more text
531 next('.'); // optional
532 if (obj == NULL || CFGetTypeID(obj) != CFDictionaryGetTypeID())
533 return NULL;
534 CFTypeRef key = make();
535 obj = CFDictionaryGetValue(CFDictionaryRef(obj), key);
536 CFRelease(key);
537 }
538 return obj;
539 }
540
541
542 //
543 // The public functions
544 //
545 CFTypeRef CF_RETURNS_RETAINED cfmake(const char *format, ...)
546 {
547 va_list args;
548 va_start(args, format);
549 CFTypeRef result = CFMake(format, &args).make();
550 va_end(args);
551 return result;
552 }
553
554 CFTypeRef CF_RETURNS_RETAINED vcfmake(const char *format, va_list *args)
555 {
556 return CFMake(format, args).make();
557 }
558
559 CFDictionaryRef cfadd(CFMutableDictionaryRef dict, const char *format, ...)
560 {
561 va_list args;
562 va_start(args, format);
563 CFDictionaryRef result = CFMake(format, &args).addto(dict);
564 va_end(args);
565 return result;
566 }
567
568
569 bool cfscan(CFTypeRef obj, const char *format, ...)
570 {
571 va_list args;
572 va_start(args, format);
573 bool result = vcfscan(obj, format, &args);
574 va_end(args);
575 return result;
576 }
577
578 bool vcfscan(CFTypeRef obj, const char *format, va_list *args)
579 {
580 return CFScan(format, args).scan(obj);
581 }
582
583
584 CFTypeRef cfget(CFTypeRef obj, const char *format, ...)
585 {
586 va_list args;
587 va_start(args, format);
588 CFTypeRef result = vcfget(obj, format, &args);
589 va_end(args);
590 return result;
591 }
592
593 CFTypeRef vcfget(CFTypeRef obj, const char *format, va_list *args)
594 {
595 return CFScan(format, args).dictpath(obj);
596 }
597
598 } // end namespace Security