1 #include <CoreFoundation/CoreFoundation.h>
3 #include "SecureDownloadInternal.h"
4 #include "SecCFRelease.h"
6 #include "simulatecrash_assert.h"
9 // SecureDownloadXML: SecureDownloadXML.c
10 // cc -g -framework CoreFoundation -o $@ $^
16 extern CFDataRef
read_data(char* path
);
19 #define SD_XML_NAMESPACE CFSTR("http://www.apple.com/2006/SecureDownload/1")
20 #define SD_XML_ROOT CFSTR("SecureDownload")
21 #define SD_XML_RESOURCE CFSTR("resource")
22 #define SD_XML_SITES CFSTR("sites")
23 #define SD_XML_VERIFICATION CFSTR("verification")
24 #define SD_XML_ATTR_ALGORITHM CFSTR("algorithm")
27 CFDictionaryRef namespaces
; // array of dictionaries of namespace declarations
31 CFMutableArrayRef plists
; // array of all resource plists
32 CFMutableDictionaryRef plist
; // most recent entry in the plists array
36 static inline unsigned char decode64(unsigned char c
)
107 // Decodes base64 data into a binary CFData object
108 // If first character on a line is not in the base64 alphabet, the line
110 static CF_RETURNS_RETAINED CFDataRef
decodeBase64Data(const UInt8
* ptr
, size_t len
) {
111 CFMutableDataRef result
= CFDataCreateMutable(NULL
, len
); // data can't exceed len bytes
112 if (!result
) return NULL
;
118 UInt8 triplet
[3] = {0, 0, 0};
121 http://www.faqs.org/rfcs/rfc3548.html
122 +--first octet--+-second octet--+--third octet--+
123 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
124 +-----------+---+-------+-------+---+-----------+
125 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
126 +--1.index--+--2.index--+--3.index--+--4.index--+
129 for (i
= 0, j
= 0; i
< len
; ++i
) {
130 unsigned char c
= ptr
[i
];
131 if (c
== ' ') { continue; }
132 if (c
== '\t') { continue; }
133 if (c
== '\r') { continue; }
134 if (c
== '\n') { skip
= 0; continue; }
135 if (skip
) { continue; }
136 if (!skip
&& c
== '=') { --j
; skip
= 1; continue; }
137 unsigned char x
= decode64(c
);
138 if (x
== 255) { skip
= 1; continue; }
141 triplet
[0] |= ((x
<< 2) & 0xFC);
144 triplet
[0] |= ((x
>> 4) & 0x03);
145 triplet
[1] |= ((x
<< 4) & 0xF0);
148 triplet
[1] |= ((x
>> 2) & 0x0F);
149 triplet
[2] |= ((x
<< 6) & 0xC0);
152 triplet
[2] |= ((x
) & 0x3F);
153 CFDataAppendBytes(result
, triplet
, j
);
154 memset(triplet
, 0, sizeof(triplet
));
159 CFDataAppendBytes(result
, triplet
, j
);
164 // Returns a CFString containing the base64 representation of the data.
165 // boolean argument for whether to line wrap at 64 columns or not.
166 static CF_RETURNS_RETAINED CFStringRef
encodeBase64String(const UInt8
* ptr
, size_t len
, int wrap
) {
167 const char* alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
168 "abcdefghijklmnopqrstuvwxyz"
171 // base64 encoded data uses 4 ASCII characters to represent 3 octets.
172 // There can be up to two == at the end of the base64 data for padding.
173 // If we are line wrapping then we need space for one newline character
174 // every 64 characters of output.
175 // Rounded 4/3 up to 2 to avoid floating point math.
177 //CFIndex max_len = (2*len) + 2;
178 //if (wrap) len = len + ((2*len) / 64) + 1;
180 CFMutableStringRef string
= CFStringCreateMutable(NULL
, 0);
181 if (!string
) return NULL
;
184 http://www.faqs.org/rfcs/rfc3548.html
185 +--first octet--+-second octet--+--third octet--+
186 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
187 +-----------+---+-------+-------+---+-----------+
188 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
189 +--1.index--+--2.index--+--3.index--+--4.index--+
191 int i
= 0; // octet offset into input data
192 int column
= 0; // output column number (used for line wrapping)
194 UniChar c
[16]; // buffer of characters to add to output
195 int j
= 0; // offset to place next character in buffer
196 int index
; // index into output alphabet
198 #define ADDCHAR(_X_) do { c[j++] = _X_; if (wrap && (++column == 64)) { column = 0; c[j++] = '\n'; } } while (0);
201 index
= (ptr
[i
] >> 2) & 0x3F;
202 ADDCHAR(alphabet
[index
]);
205 index
= (ptr
[i
] << 4) & 0x30;
207 index
= index
| ((ptr
[i
+1] >> 4) & 0x0F);
208 ADDCHAR(alphabet
[index
]);
209 } else { // end of input, pad as necessary
210 ADDCHAR(alphabet
[index
]);
217 index
= (ptr
[i
+1] << 2) & 0x3C;
219 index
= index
| ((ptr
[i
+2] >> 6) & 0x03);
220 ADDCHAR(alphabet
[index
]);
221 } else { // end of input, pad as necessary
222 ADDCHAR(alphabet
[index
]);
229 index
= (ptr
[i
+2]) & 0x3F;
230 ADDCHAR(alphabet
[index
]);
233 CFStringAppendCharacters(string
, c
, j
);
234 i
+= 3; // we processed 3 bytes of input
236 // end of data, append newline if we haven't already
237 if (wrap
&& c
[j
-1] != '\n') {
239 CFStringAppendCharacters(string
, c
, 1);
248 // makes a copy of the current namespaces dictionary, adding in any
249 // namespaces defined by the current node.
250 static CFDictionaryRef
copyNamespacesForNode(CFDictionaryRef namespaces
, CFXMLNodeRef node
) {
251 CFMutableDictionaryRef result
= NULL
;
253 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
255 // careful, don't use the info unless we ensure type == kCFXMLNodeTypeElement
256 CFXMLElementInfo
* info
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(node
);
259 // create our result dictionary
260 // there are four possible configurations:
261 // 1. previous dictionary exists, this is an element, and has attributes:
262 // clone existing dictionary, we may be adding to it
263 // 2. previous dictionary exists, not an element or no attributes:
264 // retain existing dictionary and return
265 // 3. no previous dictionary, this is an element, and has attributes:
266 // create new dictionary, we may be adding to it
267 // 4. no previous dictionary, not an element or no attributes:
268 // create new dictionary and return
270 if (namespaces
&& type
== kCFXMLNodeTypeElement
&& info
->attributes
&& info
->attributeOrder
) {
271 result
= CFDictionaryCreateMutableCopy(NULL
, 0, namespaces
);
272 } else if (namespaces
) {
273 result
= (CFMutableDictionaryRef
)CFRetain(namespaces
);
275 } else if (type
== kCFXMLNodeTypeElement
&& info
->attributes
&& info
->attributeOrder
) {
276 result
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
277 if (result
) CFDictionarySetValue(result
, CFSTR(""), CFSTR(""));
279 result
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
280 if (result
) CFDictionarySetValue(result
, CFSTR(""), CFSTR(""));
283 if (!result
) return NULL
;
286 // if we got this far, we're dealing with an XML element with
287 // attributes. check to see if any are xml namespace attributes.
289 CFArrayRef attrs
= info
->attributeOrder
;
290 CFIndex i
, count
= CFArrayGetCount(attrs
);
291 for (i
= 0; i
< count
; ++i
) {
292 CFStringRef attr
= CFArrayGetValueAtIndex(attrs
, i
);
294 if (CFEqual(CFSTR("xmlns"), attr
)) {
296 CFStringRef value
= CFDictionaryGetValue(info
->attributes
, attr
);
298 CFDictionarySetValue(result
, CFSTR(""), value
);
301 // if the attribute is in the "xmlns" namespace, then it's
302 // really a declaration of a new namespace. record it in our dictionary.
303 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, attr
, CFSTR(":"));
304 CFIndex numparts
= parts
? CFArrayGetCount(parts
) : 0;
306 CFStringRef prefix
= CFArrayGetValueAtIndex(parts
, 0);
307 CFStringRef suffix
= CFArrayGetValueAtIndex(parts
, 1);
308 if (CFEqual(CFSTR("xmlns"), prefix
)) {
309 CFStringRef value
= CFDictionaryGetValue(info
->attributes
, attr
);
311 CFDictionarySetValue(result
, suffix
, value
);
315 if (parts
) CFRelease(parts
);
321 // returns the current node's element name and namespace URI
322 // based on the currently defined namespaces.
323 static void copyNodeNamespaceAndName(CFDictionaryRef namespaces
, CFXMLNodeRef node
, CFStringRef
* namespace, CFStringRef
* name
) {
324 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
327 if (type
== kCFXMLNodeTypeElement
) {
328 CFStringRef qname
= CFXMLNodeGetString(node
);
329 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, qname
, CFSTR(":"));
330 CFIndex numparts
= parts
? CFArrayGetCount(parts
) : 0;
333 *namespace = CFRetain(CFDictionaryGetValue(namespaces
, CFSTR("")));
334 *name
= CFRetain(CFArrayGetValueAtIndex(parts
, 0));
335 } else if (numparts
== 2) {
336 CFStringRef prefix
= CFArrayGetValueAtIndex(parts
, 0);
337 CFStringRef ns
= CFDictionaryGetValue(namespaces
, prefix
);
338 *namespace = ns
? CFRetain(ns
) : NULL
;
339 *name
= CFRetain(CFArrayGetValueAtIndex(parts
, 1));
343 if (parts
) CFRelease(parts
);
347 // helper function for copyTreeString() below
348 // appends text nodes to the mutable string context
349 static void _appendTreeString(const void *value
, void *context
) {
350 CFXMLTreeRef tree
= (CFXMLTreeRef
)value
;
351 CFMutableStringRef result
= (CFMutableStringRef
)context
;
353 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
354 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
355 if (type
== kCFXMLNodeTypeElement
) {
356 CFTreeApplyFunctionToChildren(tree
, _appendTreeString
, result
);
357 } else if (type
== kCFXMLNodeTypeText
) {
358 CFStringRef str
= CFXMLNodeGetString(node
);
359 if (str
) CFStringAppend(result
, str
);
363 // equivalent to the XPATH string() function
364 // concatenates all text nodes into a single string
365 static CFMutableStringRef
copyTreeString(CFXMLTreeRef tree
) {
366 CFMutableStringRef result
= CFStringCreateMutable(NULL
, 0);
367 CFTreeApplyFunctionToChildren(tree
, _appendTreeString
, result
);
372 // returns an array of CFXMLTreeRef objects that are immediate
373 // children of the context node that match the specified element
374 // name and namespace URI
375 static CFArrayRef
copyChildrenWithName(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
376 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
377 tree
= CFTreeGetFirstChild(tree
);
379 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
380 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
381 if (type
== kCFXMLNodeTypeElement
) {
382 CFDictionaryRef namespaces
= copyNamespacesForNode(inNamespaces
, node
);
385 copyNodeNamespaceAndName(namespaces
, node
, &ns
, &n
);
387 if (ns
&& n
&& CFEqual(ns
, namespace) && CFEqual(n
, name
)) {
388 CFArrayAppendValue(result
, tree
);
390 if (ns
) CFRelease(ns
);
393 CFRelease(namespaces
);
396 tree
= CFTreeGetNextSibling(tree
);
401 // convenience function to find the first child element
402 // with the given element name and namespace URI
403 static CFXMLTreeRef
getChildWithName(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
404 CFXMLTreeRef result
= NULL
;
405 CFArrayRef array
= copyChildrenWithName(tree
, inNamespaces
, namespace, name
);
406 if (array
&& CFArrayGetCount(array
) > 0) {
407 result
= (CFXMLTreeRef
)CFArrayGetValueAtIndex(array
, 0);
409 if (array
) CFRelease(array
);
413 // returns the string value of the specified child node
414 static CFStringRef
copyChildWithNameAsString(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
415 CFStringRef result
= NULL
;
416 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
417 if (child
) result
= copyTreeString(child
);
421 // returns the integer value of the specified child node
422 static CFNumberRef
copyChildWithNameAsInteger(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
423 CFNumberRef result
= NULL
;
424 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
426 CFStringRef str
= copyTreeString(child
);
428 SInt32 size
= CFStringGetIntValue(str
);
429 result
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &size
);
436 // returns an array of URLs aggregated from the child
437 // nodes matching the given name and namespace URI.
438 static CFArrayRef
copyChildrenWithNameAsURLs(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
439 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
440 CFArrayRef children
= copyChildrenWithName(tree
, inNamespaces
, namespace, name
);
442 CFIndex i
, count
= CFArrayGetCount(children
);
443 for (i
= 0; i
< count
; ++i
) {
444 CFXMLTreeRef child
= (CFXMLTreeRef
)CFArrayGetValueAtIndex(children
, i
);
445 CFStringRef str
= copyTreeString(child
);
447 CFURLRef url
= CFURLCreateWithString(NULL
, str
, NULL
);
449 CFArrayAppendValue(result
, url
);
460 // returns the base 64 decoded value of the specified child node
461 static CFDataRef
copyChildWithNameAsData(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
462 CFDataRef result
= NULL
;
463 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
465 CFStringRef str
= copyTreeString(child
);
467 CFIndex len
= CFStringGetLength(str
);
469 UInt8
* buffer
= malloc(len
);
470 // ASCII is one byte per character. Any inconvertible characters
471 // are assigned to whitespace and skipped.
473 if (CFStringGetBytes(str
, CFRangeMake(0, len
), kCFStringEncodingASCII
, ' ', 0, buffer
, len
, &used
)) {
474 result
= decodeBase64Data(buffer
, used
);
484 // returns the CFDate value of the specified child node
485 // whose string value is interpreted in W3C DateTime format
486 static CFDateRef
copyChildWithNameAsDate(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
487 CFDateRef result
= NULL
;
488 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
490 CFMutableStringRef str
= copyTreeString(child
);
492 CFStringTrimWhitespace(str
);
493 if (CFStringGetLength(str
) > 21) {
494 CFStringRef year
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(0, 4));
495 CFStringRef month
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(5, 2));
496 CFStringRef day
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(8, 2));
497 CFStringRef hour
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(11, 2));
498 CFStringRef minute
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(14, 2));
499 CFStringRef second
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(17, 2));
500 CFStringRef tenth
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(20, 1));
502 CFGregorianDate gregory
;
503 memset(&gregory
, 0, sizeof(gregory
));
504 if (year
) { gregory
.year
= CFStringGetIntValue(year
); CFRelease(year
); }
505 if (month
) { gregory
.month
= CFStringGetIntValue(month
); CFRelease(month
); }
506 if (day
) { gregory
.day
= CFStringGetIntValue(day
); CFRelease(day
); }
507 if (hour
) { gregory
.hour
= CFStringGetIntValue(hour
); CFRelease(hour
); }
508 if (minute
) { gregory
.minute
= CFStringGetIntValue(minute
); CFRelease(minute
); }
509 if (second
) { gregory
.second
= (double)CFStringGetIntValue(second
); CFRelease(second
); }
510 if (tenth
) { gregory
.second
+= ((double)CFStringGetIntValue(tenth
)/(double)10.0); CFRelease(tenth
); }
512 CFTimeZoneRef tz
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
514 CFAbsoluteTime at
= CFGregorianDateGetAbsoluteTime(gregory
, tz
);
515 result
= CFDateCreate(NULL
, at
);
526 // generic parser for XML nodes in our ticket format
527 static void _parseXMLNode(const void *value
, void *context
) {
528 CFXMLTreeRef tree
= (CFXMLTreeRef
)value
;
529 struct parseState
* state
= (struct parseState
*)context
;
531 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
534 CFDictionaryRef namespaces
= copyNamespacesForNode(state
->namespaces
, node
);
536 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
540 if (type
== kCFXMLNodeTypeElement
) {
541 CFStringRef ns
, name
;
542 copyNodeNamespaceAndName(namespaces
, node
, &ns
, &name
);
546 if (CFEqual(ns
, SD_XML_NAMESPACE
)) {
547 if (CFEqual(name
, SD_XML_ROOT
)) {
551 } else if (CFEqual(name
, SD_XML_RESOURCE
)) {
553 state
->plist
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
555 CFArrayAppendValue(state
->plists
, state
->plist
);
556 CFRelease(state
->plist
);
559 CFStringRef name
= copyChildWithNameAsString(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_NAME
);
560 if (name
&& state
->plist
) {
561 CFDictionarySetValue(state
->plist
, SD_XML_NAME
, name
);
565 CFNumberRef size
= copyChildWithNameAsInteger(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_SIZE
);
566 if (size
&& state
->plist
) {
567 CFDictionarySetValue(state
->plist
, SD_XML_SIZE
, size
);
571 CFDateRef created
= copyChildWithNameAsDate(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_CREATED
);
572 if (created
&& state
->plist
) {
573 CFDictionarySetValue(state
->plist
, SD_XML_CREATED
, created
);
575 CFReleaseNull(created
);
579 } else if (CFEqual(name
, SD_XML_SITES
)) {
580 CFArrayRef urls
= copyChildrenWithNameAsURLs(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_URL
);
581 if (urls
&& state
->plist
) {
582 CFDictionarySetValue(state
->plist
, SD_XML_URL
, urls
);
586 } else if (CFEqual(name
, SD_XML_VERIFICATIONS
)) {
587 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
588 if (dict
&& state
->plist
) {
589 CFDictionarySetValue(state
->plist
, SD_XML_VERIFICATIONS
, dict
);
594 } else if (CFEqual(name
, SD_XML_VERIFICATION
) && state
->plist
) {
595 CFMutableDictionaryRef verifications
= (CFMutableDictionaryRef
)CFDictionaryGetValue(state
->plist
, SD_XML_VERIFICATIONS
);
596 CFXMLElementInfo
* info
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(node
);
597 if (verifications
&& info
&& info
->attributes
) {
598 CFStringRef algorithm
= CFDictionaryGetValue(info
->attributes
, SD_XML_ATTR_ALGORITHM
);
600 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
603 #pragma unused(child)
605 CFNumberRef sector_size
= copyChildWithNameAsInteger(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_SECTOR_SIZE
);
607 CFDictionarySetValue(dict
, SD_XML_SECTOR_SIZE
, sector_size
);
608 CFRelease(sector_size
);
611 CFDataRef digest
= copyChildWithNameAsData(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_DIGEST
);
613 CFDictionarySetValue(dict
, SD_XML_DIGEST
, digest
);
617 CFDataRef digests
= copyChildWithNameAsData(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_DIGESTS
);
619 CFDictionarySetValue(dict
, SD_XML_DIGESTS
, digests
);
623 CFDictionarySetValue(verifications
, algorithm
, dict
);
631 cfprintf(stderr
, "%sELEM:\t%@\t[%@]\n", state
->prefix
, name
, ns
);
634 if (ns
) CFRelease(ns
);
635 if (name
) CFRelease(name
);
636 } else if (type
== kCFXMLNodeTypeWhitespace
) {
640 CFStringRef str
= CFXMLNodeGetString(node
);
641 cfprintf(stderr
, "%s% 4d:\t%@\n", state
->prefix
, type
, str
);
645 // only recurse further if we have been specifically instructed to
648 struct parseState local
;
649 memcpy(&local
, state
, sizeof(struct parseState
));
650 local
.namespaces
= namespaces
;
652 asprintf(&local
.prefix
, "%s ", state
->prefix
);
654 CFTreeApplyFunctionToChildren(tree
, _parseXMLNode
, &local
);
659 if (namespaces
) CFRelease(namespaces
);
662 CFPropertyListRef
_SecureDownloadParseTicketXML(CFDataRef xmlData
) {
663 if (!xmlData
) return NULL
;
666 CFXMLTreeRef tree
= CFXMLTreeCreateFromData(NULL
, xmlData
, url
, kCFXMLParserNoOptions
, kCFXMLNodeCurrentVersion
);
667 if (!tree
) return NULL
;
669 struct parseState state
;
670 memset(&state
, 0, sizeof(state
));
674 state
.plists
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
676 CFTreeApplyFunctionToChildren(tree
, _parseXMLNode
, &state
);
680 CFPropertyListRef result
= NULL
;
681 // For now, only return the first resource encountered
682 if (state
.plists
&& CFArrayGetCount(state
.plists
) > 0) {
683 result
= CFArrayGetValueAtIndex(state
.plists
, 0);
686 if (state
.plists
) CFRelease(state
.plists
);
690 static void _appendCString(CFMutableDataRef data
, const char* cstring
) {
691 CFDataAppendBytes(data
, (UInt8
*)cstring
, strlen(cstring
));
694 static void _appendCFString(CFMutableDataRef data
, CFStringRef string
) {
695 CFDataRef utf8
= CFStringCreateExternalRepresentation(NULL
, string
, kCFStringEncodingUTF8
, '?');
697 CFDataAppendBytes(data
, CFDataGetBytePtr(utf8
), CFDataGetLength(utf8
));
702 static void _appendCFNumber(CFMutableDataRef data
, CFNumberRef number
) {
703 CFStringRef str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), number
);
705 _appendCFString(data
, str
);
710 static void _appendCFURL(CFMutableDataRef data
, CFURLRef url
) {
711 CFURLRef abs
= CFURLCopyAbsoluteURL(url
);
713 CFStringRef str
= CFURLGetString(abs
);
715 _appendCFString(data
, str
);
721 // appends data in base64 encoded form
722 static void _appendCFData(CFMutableDataRef data
, CFDataRef moreData
) {
723 CFStringRef str
= encodeBase64String(CFDataGetBytePtr(moreData
), CFDataGetLength(moreData
), 0);
725 _appendCFString(data
, str
);
730 // appends a date in W3C DateTime format
731 static void _appendCFDate(CFMutableDataRef data
, CFDateRef date
) {
732 CFLocaleRef locale
= CFLocaleCreate(NULL
, CFSTR("en_US"));
734 CFDateFormatterRef formatter
= CFDateFormatterCreate(NULL
, locale
, kCFDateFormatterNoStyle
, kCFDateFormatterNoStyle
);
736 CFDateFormatterSetFormat(formatter
, CFSTR("yyyy-MM-dd'T'HH:mm:ss.S'Z'"));
737 CFTimeZoneRef tz
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
739 CFDateFormatterSetProperty(formatter
, kCFDateFormatterTimeZone
, tz
);
740 CFStringRef str
= CFDateFormatterCreateStringWithDate(NULL
, formatter
, date
);
742 _appendCFString(data
, str
);
747 CFRelease(formatter
);
753 static CFArrayRef
dictionaryGetSortedKeys(CFDictionaryRef dictionary
) {
754 CFIndex count
= CFDictionaryGetCount(dictionary
);
756 const void** keys
= malloc(sizeof(CFTypeRef
) * count
);
757 CFDictionaryGetKeysAndValues(dictionary
, keys
, NULL
);
758 CFArrayRef keysArray
= CFArrayCreate(NULL
, keys
, count
, &kCFTypeArrayCallBacks
);
759 CFMutableArrayRef sortedKeys
= CFArrayCreateMutableCopy(NULL
, count
, keysArray
);
760 CFRelease(keysArray
);
763 CFArraySortValues(sortedKeys
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, 0);
767 CFDataRef
_SecureDownloadCreateTicketXML(CFPropertyListRef plist
) {
768 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
769 if (!data
) return NULL
;
771 _appendCString(data
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
772 _appendCString(data
, "<SecureDownload xmlns=\"http://www.apple.com/2006/SecureDownload/1\">\n");
773 _appendCString(data
, " <resource>\n");
775 CFStringRef name
= CFDictionaryGetValue(plist
, SD_XML_NAME
);
777 _appendCString(data
, "\t<name>");
778 _appendCFString(data
, name
);
779 _appendCString(data
, "</name>\n");
782 CFNumberRef num
= CFDictionaryGetValue(plist
, SD_XML_SIZE
);
784 _appendCString(data
, "\t<size>");
785 _appendCFNumber(data
, num
);
786 _appendCString(data
, "</size>\n");
789 CFDateRef created
= CFDictionaryGetValue(plist
, SD_XML_CREATED
);
791 _appendCString(data
, "\t<created>");
792 _appendCFDate(data
, created
);
793 _appendCString(data
, "</created>\n");
796 _appendCString(data
, "\t<sites>\n");
797 CFArrayRef urls
= CFDictionaryGetValue(plist
, SD_XML_URL
);
799 CFIndex i
, count
= CFArrayGetCount(urls
);
800 for (i
= 0; i
< count
; ++i
) {
801 _appendCString(data
, "\t\t<url>");
802 _appendCFURL(data
, CFArrayGetValueAtIndex(urls
, i
));
803 _appendCString(data
, "</url>\n");
806 _appendCString(data
, "\t</sites>\n");
808 CFDictionaryRef verifications
= CFDictionaryGetValue(plist
, SD_XML_VERIFICATIONS
);
810 _appendCString(data
, "\t<verifications>\n");
811 CFArrayRef algorithms
= dictionaryGetSortedKeys(verifications
);
813 CFIndex i
, count
= CFArrayGetCount(algorithms
);
814 for (i
= 0; i
< count
; ++i
) {
815 CFStringRef algorithm
= CFArrayGetValueAtIndex(algorithms
, i
);
817 _appendCString(data
, "\t\t<verification algorithm=\"");
818 _appendCFString(data
, algorithm
);
819 _appendCString(data
, "\">\n");
820 CFDictionaryRef dict
= CFDictionaryGetValue(verifications
, algorithm
);
822 CFDataRef digest
= CFDictionaryGetValue(dict
, SD_XML_DIGEST
);
824 _appendCString(data
, "\t\t\t<digest>");
825 _appendCFData(data
, digest
);
826 _appendCString(data
, "</digest>\n");
829 CFNumberRef sector_size
= CFDictionaryGetValue(dict
, SD_XML_SECTOR_SIZE
);
831 _appendCString(data
, "\t\t\t<sector_size>");
832 _appendCFNumber(data
, sector_size
);
833 _appendCString(data
, "</sector_size>\n");
836 CFDataRef digests
= CFDictionaryGetValue(dict
, SD_XML_DIGESTS
);
838 _appendCString(data
, "\t\t\t<digests>");
839 _appendCFData(data
, digests
);
840 _appendCString(data
, "</digests>\n");
843 _appendCString(data
, "\t\t</verification>\n");
846 CFRelease(algorithms
);
848 _appendCString(data
, "\t</verifications>\n");
851 _appendCString(data
, " </resource>\n");
852 _appendCString(data
, "</SecureDownload>\n");
859 int main(int argc
, char* argv
[]) {
861 CFDataRef data
= read_data("/Users/kevin/Desktop/SecureDownloadXML/SecureDownload.xml");
863 CFPropertyListRef plist
= _SecureDownloadParseTicketXML(data
);
866 CFDataRef output
= _SecureDownloadCreateTicketXML(plist
);
868 write(STDOUT_FILENO
, CFDataGetBytePtr(output
), CFDataGetLength(output
));
876 // help look for leaks
877 cfprintf(stderr
, "pid = %d\n", getpid());