1 #include <CoreFoundation/CoreFoundation.h>
3 #include "SecureDownloadInternal.h"
6 // SecureDownloadXML: SecureDownloadXML.c
7 // cc -g -framework CoreFoundation -o $@ $^
13 extern CFDataRef
read_data(char* path
);
16 #define SD_XML_NAMESPACE CFSTR("http://www.apple.com/2006/SecureDownload/1")
17 #define SD_XML_ROOT CFSTR("SecureDownload")
18 #define SD_XML_RESOURCE CFSTR("resource")
19 #define SD_XML_SITES CFSTR("sites")
20 #define SD_XML_VERIFICATION CFSTR("verification")
21 #define SD_XML_ATTR_ALGORITHM CFSTR("algorithm")
24 CFDictionaryRef namespaces
; // array of dictionaries of namespace declarations
28 CFMutableArrayRef plists
; // array of all resource plists
29 CFMutableDictionaryRef plist
; // most recent entry in the plists array
33 static inline unsigned char decode64(unsigned char c
)
104 // Decodes base64 data into a binary CFData object
105 // If first character on a line is not in the base64 alphabet, the line
107 static CFDataRef
decodeBase64Data(const UInt8
* ptr
, size_t len
) {
108 CFMutableDataRef result
= CFDataCreateMutable(NULL
, len
); // data can't exceed len bytes
109 if (!result
) return NULL
;
115 UInt8 triplet
[3] = {0, 0, 0};
118 http://www.faqs.org/rfcs/rfc3548.html
119 +--first octet--+-second octet--+--third octet--+
120 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
121 +-----------+---+-------+-------+---+-----------+
122 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
123 +--1.index--+--2.index--+--3.index--+--4.index--+
126 for (i
= 0, j
= 0; i
< len
; ++i
) {
127 unsigned char c
= ptr
[i
];
128 if (c
== ' ') { continue; }
129 if (c
== '\t') { continue; }
130 if (c
== '\r') { continue; }
131 if (c
== '\n') { skip
= 0; continue; }
132 if (skip
) { continue; }
133 if (!skip
&& c
== '=') { --j
; skip
= 1; continue; }
134 unsigned char x
= decode64(c
);
135 if (x
== 255) { skip
= 1; continue; }
138 triplet
[0] |= ((x
<< 2) & 0xFC);
141 triplet
[0] |= ((x
>> 4) & 0x03);
142 triplet
[1] |= ((x
<< 4) & 0xF0);
145 triplet
[1] |= ((x
>> 2) & 0x0F);
146 triplet
[2] |= ((x
<< 6) & 0xC0);
149 triplet
[2] |= ((x
) & 0x3F);
150 CFDataAppendBytes(result
, triplet
, j
);
151 memset(triplet
, 0, sizeof(triplet
));
156 CFDataAppendBytes(result
, triplet
, j
);
161 // Returns a CFString containing the base64 representation of the data.
162 // boolean argument for whether to line wrap at 64 columns or not.
163 static CFStringRef
encodeBase64String(const UInt8
* ptr
, size_t len
, int wrap
) {
164 const char* alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
165 "abcdefghijklmnopqrstuvwxyz"
168 // base64 encoded data uses 4 ASCII characters to represent 3 octets.
169 // There can be up to two == at the end of the base64 data for padding.
170 // If we are line wrapping then we need space for one newline character
171 // every 64 characters of output.
172 // Rounded 4/3 up to 2 to avoid floating point math.
174 //CFIndex max_len = (2*len) + 2;
175 //if (wrap) len = len + ((2*len) / 64) + 1;
177 CFMutableStringRef string
= CFStringCreateMutable(NULL
, 0);
178 if (!string
) return NULL
;
181 http://www.faqs.org/rfcs/rfc3548.html
182 +--first octet--+-second octet--+--third octet--+
183 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
184 +-----------+---+-------+-------+---+-----------+
185 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
186 +--1.index--+--2.index--+--3.index--+--4.index--+
188 int i
= 0; // octet offset into input data
189 int column
= 0; // output column number (used for line wrapping)
191 UniChar c
[16]; // buffer of characters to add to output
192 int j
= 0; // offset to place next character in buffer
193 int index
; // index into output alphabet
195 #define ADDCHAR(_X_) do { c[j++] = _X_; if (wrap && (++column == 64)) { column = 0; c[j++] = '\n'; } } while (0);
198 index
= (ptr
[i
] >> 2) & 0x3F;
199 ADDCHAR(alphabet
[index
]);
202 index
= (ptr
[i
] << 4) & 0x30;
204 index
= index
| ((ptr
[i
+1] >> 4) & 0x0F);
205 ADDCHAR(alphabet
[index
]);
206 } else { // end of input, pad as necessary
207 ADDCHAR(alphabet
[index
]);
214 index
= (ptr
[i
+1] << 2) & 0x3C;
216 index
= index
| ((ptr
[i
+2] >> 6) & 0x03);
217 ADDCHAR(alphabet
[index
]);
218 } else { // end of input, pad as necessary
219 ADDCHAR(alphabet
[index
]);
226 index
= (ptr
[i
+2]) & 0x3F;
227 ADDCHAR(alphabet
[index
]);
230 CFStringAppendCharacters(string
, c
, j
);
231 i
+= 3; // we processed 3 bytes of input
233 // end of data, append newline if we haven't already
234 if (wrap
&& c
[j
-1] != '\n') {
236 CFStringAppendCharacters(string
, c
, 1);
245 // makes a copy of the current namespaces dictionary, adding in any
246 // namespaces defined by the current node.
247 static CFDictionaryRef
copyNamespacesForNode(CFDictionaryRef namespaces
, CFXMLNodeRef node
) {
248 CFMutableDictionaryRef result
= NULL
;
250 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
252 // careful, don't use the info unless we ensure type == kCFXMLNodeTypeElement
253 CFXMLElementInfo
* info
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(node
);
256 // create our result dictionary
257 // there are four possible configurations:
258 // 1. previous dictionary exists, this is an element, and has attributes:
259 // clone existing dictionary, we may be adding to it
260 // 2. previous dictionary exists, not an element or no attributes:
261 // retain existing dictionary and return
262 // 3. no previous dictionary, this is an element, and has attributes:
263 // create new dictionary, we may be adding to it
264 // 4. no previous dictionary, not an element or no attributes:
265 // create new dictionary and return
267 if (namespaces
&& type
== kCFXMLNodeTypeElement
&& info
->attributes
&& info
->attributeOrder
) {
268 result
= CFDictionaryCreateMutableCopy(NULL
, 0, namespaces
);
269 } else if (namespaces
) {
270 result
= (CFMutableDictionaryRef
)CFRetain(namespaces
);
272 } else if (type
== kCFXMLNodeTypeElement
&& info
->attributes
&& info
->attributeOrder
) {
273 result
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
274 if (result
) CFDictionarySetValue(result
, CFSTR(""), CFSTR(""));
276 result
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
277 if (result
) CFDictionarySetValue(result
, CFSTR(""), CFSTR(""));
280 if (!result
) return NULL
;
283 // if we got this far, we're dealing with an XML element with
284 // attributes. check to see if any are xml namespace attributes.
286 CFArrayRef attrs
= info
->attributeOrder
;
287 CFIndex i
, count
= CFArrayGetCount(attrs
);
288 for (i
= 0; i
< count
; ++i
) {
289 CFStringRef attr
= CFArrayGetValueAtIndex(attrs
, i
);
291 if (CFEqual(CFSTR("xmlns"), attr
)) {
293 CFStringRef value
= CFDictionaryGetValue(info
->attributes
, attr
);
295 CFDictionarySetValue(result
, CFSTR(""), value
);
298 // if the attribute is in the "xmlns" namespace, then it's
299 // really a declaration of a new namespace. record it in our dictionary.
300 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, attr
, CFSTR(":"));
301 CFIndex numparts
= parts
? CFArrayGetCount(parts
) : 0;
303 CFStringRef prefix
= CFArrayGetValueAtIndex(parts
, 0);
304 CFStringRef suffix
= CFArrayGetValueAtIndex(parts
, 1);
305 if (CFEqual(CFSTR("xmlns"), prefix
)) {
306 CFStringRef value
= CFDictionaryGetValue(info
->attributes
, attr
);
308 CFDictionarySetValue(result
, suffix
, value
);
312 if (parts
) CFRelease(parts
);
318 // returns the current node's element name and namespace URI
319 // based on the currently defined namespaces.
320 static void copyNodeNamespaceAndName(CFDictionaryRef namespaces
, CFXMLNodeRef node
, CFStringRef
* namespace, CFStringRef
* name
) {
321 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
324 if (type
== kCFXMLNodeTypeElement
) {
325 CFStringRef qname
= CFXMLNodeGetString(node
);
326 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, qname
, CFSTR(":"));
327 CFIndex numparts
= parts
? CFArrayGetCount(parts
) : 0;
330 *namespace = CFRetain(CFDictionaryGetValue(namespaces
, CFSTR("")));
331 *name
= CFRetain(CFArrayGetValueAtIndex(parts
, 0));
332 } else if (numparts
== 2) {
333 CFStringRef prefix
= CFArrayGetValueAtIndex(parts
, 0);
334 CFStringRef ns
= CFDictionaryGetValue(namespaces
, prefix
);
335 *namespace = ns
? CFRetain(ns
) : NULL
;
336 *name
= CFRetain(CFArrayGetValueAtIndex(parts
, 1));
340 if (parts
) CFRelease(parts
);
344 // helper function for copyTreeString() below
345 // appends text nodes to the mutable string context
346 static void _appendTreeString(const void *value
, void *context
) {
347 CFXMLTreeRef tree
= (CFXMLTreeRef
)value
;
348 CFMutableStringRef result
= (CFMutableStringRef
)context
;
350 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
351 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
352 if (type
== kCFXMLNodeTypeElement
) {
353 CFTreeApplyFunctionToChildren(tree
, _appendTreeString
, result
);
354 } else if (type
== kCFXMLNodeTypeText
) {
355 CFStringRef str
= CFXMLNodeGetString(node
);
356 if (str
) CFStringAppend(result
, str
);
360 // equivalent to the XPATH string() function
361 // concatenates all text nodes into a single string
362 static CFMutableStringRef
copyTreeString(CFXMLTreeRef tree
) {
363 CFMutableStringRef result
= CFStringCreateMutable(NULL
, 0);
364 CFTreeApplyFunctionToChildren(tree
, _appendTreeString
, result
);
369 // returns an array of CFXMLTreeRef objects that are immediate
370 // children of the context node that match the specified element
371 // name and namespace URI
372 static CFArrayRef
copyChildrenWithName(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
373 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
374 tree
= CFTreeGetFirstChild(tree
);
376 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
377 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
378 if (type
== kCFXMLNodeTypeElement
) {
379 CFDictionaryRef namespaces
= copyNamespacesForNode(inNamespaces
, node
);
382 copyNodeNamespaceAndName(namespaces
, node
, &ns
, &n
);
384 if (ns
&& n
&& CFEqual(ns
, namespace) && CFEqual(n
, name
)) {
385 CFArrayAppendValue(result
, tree
);
387 if (ns
) CFRelease(ns
);
390 CFRelease(namespaces
);
393 tree
= CFTreeGetNextSibling(tree
);
398 // convenience function to find the first child element
399 // with the given element name and namespace URI
400 static CFXMLTreeRef
getChildWithName(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
401 CFXMLTreeRef result
= NULL
;
402 CFArrayRef array
= copyChildrenWithName(tree
, inNamespaces
, namespace, name
);
403 if (array
&& CFArrayGetCount(array
) > 0) {
404 result
= (CFXMLTreeRef
)CFArrayGetValueAtIndex(array
, 0);
406 if (array
) CFRelease(array
);
410 // returns the string value of the specified child node
411 static CFStringRef
copyChildWithNameAsString(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
412 CFStringRef result
= NULL
;
413 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
414 if (child
) result
= copyTreeString(child
);
418 // returns the integer value of the specified child node
419 static CFNumberRef
copyChildWithNameAsInteger(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
420 CFNumberRef result
= NULL
;
421 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
423 CFStringRef str
= copyTreeString(child
);
425 SInt32 size
= CFStringGetIntValue(str
);
426 result
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &size
);
433 // returns an array of URLs aggregated from the child
434 // nodes matching the given name and namespace URI.
435 static CFArrayRef
copyChildrenWithNameAsURLs(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
436 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
437 CFArrayRef children
= copyChildrenWithName(tree
, inNamespaces
, namespace, name
);
439 CFIndex i
, count
= CFArrayGetCount(children
);
440 for (i
= 0; i
< count
; ++i
) {
441 CFXMLTreeRef child
= (CFXMLTreeRef
)CFArrayGetValueAtIndex(children
, i
);
442 CFStringRef str
= copyTreeString(child
);
444 CFURLRef url
= CFURLCreateWithString(NULL
, str
, NULL
);
446 CFArrayAppendValue(result
, url
);
457 // returns the base 64 decoded value of the specified child node
458 static CFDataRef
copyChildWithNameAsData(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
459 CFDataRef result
= NULL
;
460 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
462 CFStringRef str
= copyTreeString(child
);
464 CFIndex len
= CFStringGetLength(str
);
466 UInt8
* buffer
= malloc(len
);
467 // ASCII is one byte per character. Any inconvertible characters
468 // are assigned to whitespace and skipped.
470 if (CFStringGetBytes(str
, CFRangeMake(0, len
), kCFStringEncodingASCII
, ' ', 0, buffer
, len
, &used
)) {
471 result
= decodeBase64Data(buffer
, used
);
481 // returns the CFDate value of the specified child node
482 // whose string value is interpreted in W3C DateTime format
483 static CFDateRef
copyChildWithNameAsDate(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
484 CFDateRef result
= NULL
;
485 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
487 CFMutableStringRef str
= copyTreeString(child
);
489 CFStringTrimWhitespace(str
);
490 if (CFStringGetLength(str
) > 21) {
491 CFStringRef year
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(0, 4));
492 CFStringRef month
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(5, 2));
493 CFStringRef day
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(8, 2));
494 CFStringRef hour
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(11, 2));
495 CFStringRef minute
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(14, 2));
496 CFStringRef second
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(17, 2));
497 CFStringRef tenth
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(20, 1));
499 CFGregorianDate gregory
;
500 memset(&gregory
, 0, sizeof(gregory
));
501 if (year
) { gregory
.year
= CFStringGetIntValue(year
); CFRelease(year
); }
502 if (month
) { gregory
.month
= CFStringGetIntValue(month
); CFRelease(month
); }
503 if (day
) { gregory
.day
= CFStringGetIntValue(day
); CFRelease(day
); }
504 if (hour
) { gregory
.hour
= CFStringGetIntValue(hour
); CFRelease(hour
); }
505 if (minute
) { gregory
.minute
= CFStringGetIntValue(minute
); CFRelease(minute
); }
506 if (second
) { gregory
.second
= (double)CFStringGetIntValue(second
); CFRelease(second
); }
507 if (tenth
) { gregory
.second
+= ((double)CFStringGetIntValue(tenth
)/(double)10.0); CFRelease(tenth
); }
509 CFTimeZoneRef tz
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
511 CFAbsoluteTime at
= CFGregorianDateGetAbsoluteTime(gregory
, tz
);
512 result
= CFDateCreate(NULL
, at
);
523 // generic parser for XML nodes in our ticket format
524 static void _parseXMLNode(const void *value
, void *context
) {
525 CFXMLTreeRef tree
= (CFXMLTreeRef
)value
;
526 struct parseState
* state
= (struct parseState
*)context
;
528 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
531 CFDictionaryRef namespaces
= copyNamespacesForNode(state
->namespaces
, node
);
533 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
537 if (type
== kCFXMLNodeTypeElement
) {
538 CFStringRef ns
, name
;
539 copyNodeNamespaceAndName(namespaces
, node
, &ns
, &name
);
543 if (CFEqual(ns
, SD_XML_NAMESPACE
)) {
544 if (CFEqual(name
, SD_XML_ROOT
)) {
548 } else if (CFEqual(name
, SD_XML_RESOURCE
)) {
550 state
->plist
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
552 CFArrayAppendValue(state
->plists
, state
->plist
);
553 CFRelease(state
->plist
);
556 CFStringRef name
= copyChildWithNameAsString(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_NAME
);
557 if (name
&& state
->plist
) {
558 CFDictionarySetValue(state
->plist
, SD_XML_NAME
, name
);
562 CFNumberRef size
= copyChildWithNameAsInteger(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_SIZE
);
563 if (size
&& state
->plist
) {
564 CFDictionarySetValue(state
->plist
, SD_XML_SIZE
, size
);
568 CFDateRef created
= copyChildWithNameAsDate(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_CREATED
);
569 if (created
&& state
->plist
) {
570 CFDictionarySetValue(state
->plist
, SD_XML_CREATED
, created
);
576 } else if (CFEqual(name
, SD_XML_SITES
)) {
577 CFArrayRef urls
= copyChildrenWithNameAsURLs(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_URL
);
578 if (urls
&& state
->plist
) {
579 CFDictionarySetValue(state
->plist
, SD_XML_URL
, urls
);
583 } else if (CFEqual(name
, SD_XML_VERIFICATIONS
)) {
584 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
585 if (dict
&& state
->plist
) {
586 CFDictionarySetValue(state
->plist
, SD_XML_VERIFICATIONS
, dict
);
591 } else if (CFEqual(name
, SD_XML_VERIFICATION
) && state
->plist
) {
592 CFMutableDictionaryRef verifications
= (CFMutableDictionaryRef
)CFDictionaryGetValue(state
->plist
, SD_XML_VERIFICATIONS
);
593 CFXMLElementInfo
* info
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(node
);
594 if (verifications
&& info
&& info
->attributes
) {
595 CFStringRef algorithm
= CFDictionaryGetValue(info
->attributes
, SD_XML_ATTR_ALGORITHM
);
597 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
600 #pragma unused(child)
602 CFNumberRef sector_size
= copyChildWithNameAsInteger(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_SECTOR_SIZE
);
604 CFDictionarySetValue(dict
, SD_XML_SECTOR_SIZE
, sector_size
);
605 CFRelease(sector_size
);
608 CFDataRef digest
= copyChildWithNameAsData(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_DIGEST
);
610 CFDictionarySetValue(dict
, SD_XML_DIGEST
, digest
);
614 CFDataRef digests
= copyChildWithNameAsData(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_DIGESTS
);
616 CFDictionarySetValue(dict
, SD_XML_DIGESTS
, digests
);
620 CFDictionarySetValue(verifications
, algorithm
, dict
);
628 cfprintf(stderr
, "%sELEM:\t%@\t[%@]\n", state
->prefix
, name
, ns
);
631 if (ns
) CFRelease(ns
);
632 if (name
) CFRelease(name
);
633 } else if (type
== kCFXMLNodeTypeWhitespace
) {
637 CFStringRef str
= CFXMLNodeGetString(node
);
638 cfprintf(stderr
, "%s% 4d:\t%@\n", state
->prefix
, type
, str
);
642 // only recurse further if we have been specifically instructed to
645 struct parseState local
;
646 memcpy(&local
, state
, sizeof(struct parseState
));
647 local
.namespaces
= namespaces
;
649 asprintf(&local
.prefix
, "%s ", state
->prefix
);
651 CFTreeApplyFunctionToChildren(tree
, _parseXMLNode
, &local
);
656 if (namespaces
) CFRelease(namespaces
);
659 CFPropertyListRef
_SecureDownloadParseTicketXML(CFDataRef xmlData
) {
660 if (!xmlData
) return NULL
;
663 CFXMLTreeRef tree
= CFXMLTreeCreateFromData(NULL
, xmlData
, url
, kCFXMLParserNoOptions
, kCFXMLNodeCurrentVersion
);
664 if (!tree
) return NULL
;
666 struct parseState state
;
667 memset(&state
, 0, sizeof(state
));
671 state
.plists
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
673 CFTreeApplyFunctionToChildren(tree
, _parseXMLNode
, &state
);
677 CFPropertyListRef result
= NULL
;
678 // For now, only return the first resource encountered
679 if (state
.plists
&& CFArrayGetCount(state
.plists
) > 0) {
680 result
= CFArrayGetValueAtIndex(state
.plists
, 0);
683 if (state
.plists
) CFRelease(state
.plists
);
687 static void _appendCString(CFMutableDataRef data
, const char* cstring
) {
688 CFDataAppendBytes(data
, (UInt8
*)cstring
, strlen(cstring
));
691 static void _appendCFString(CFMutableDataRef data
, CFStringRef string
) {
692 CFDataRef utf8
= CFStringCreateExternalRepresentation(NULL
, string
, kCFStringEncodingUTF8
, '?');
694 CFDataAppendBytes(data
, CFDataGetBytePtr(utf8
), CFDataGetLength(utf8
));
699 static void _appendCFNumber(CFMutableDataRef data
, CFNumberRef number
) {
700 CFStringRef str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), number
);
702 _appendCFString(data
, str
);
707 static void _appendCFURL(CFMutableDataRef data
, CFURLRef url
) {
708 CFURLRef abs
= CFURLCopyAbsoluteURL(url
);
710 CFStringRef str
= CFURLGetString(abs
);
712 _appendCFString(data
, str
);
718 // appends data in base64 encoded form
719 static void _appendCFData(CFMutableDataRef data
, CFDataRef moreData
) {
720 CFStringRef str
= encodeBase64String(CFDataGetBytePtr(moreData
), CFDataGetLength(moreData
), 0);
722 _appendCFString(data
, str
);
727 // appends a date in W3C DateTime format
728 static void _appendCFDate(CFMutableDataRef data
, CFDateRef date
) {
729 CFLocaleRef locale
= CFLocaleCreate(NULL
, CFSTR("en_US"));
731 CFDateFormatterRef formatter
= CFDateFormatterCreate(NULL
, locale
, kCFDateFormatterNoStyle
, kCFDateFormatterNoStyle
);
733 CFDateFormatterSetFormat(formatter
, CFSTR("yyyy-MM-dd'T'HH:mm:ss.S'Z'"));
734 CFTimeZoneRef tz
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
736 CFDateFormatterSetProperty(formatter
, kCFDateFormatterTimeZone
, tz
);
737 CFStringRef str
= CFDateFormatterCreateStringWithDate(NULL
, formatter
, date
);
739 _appendCFString(data
, str
);
744 CFRelease(formatter
);
750 static CFArrayRef
dictionaryGetSortedKeys(CFDictionaryRef dictionary
) {
751 CFIndex count
= CFDictionaryGetCount(dictionary
);
753 const void** keys
= malloc(sizeof(CFStringRef
) * count
);
754 CFDictionaryGetKeysAndValues(dictionary
, keys
, NULL
);
755 CFArrayRef keysArray
= CFArrayCreate(NULL
, keys
, count
, &kCFTypeArrayCallBacks
);
756 CFMutableArrayRef sortedKeys
= CFArrayCreateMutableCopy(NULL
, count
, keysArray
);
757 CFRelease(keysArray
);
760 CFArraySortValues(sortedKeys
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, 0);
764 CFDataRef
_SecureDownloadCreateTicketXML(CFPropertyListRef plist
) {
765 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
766 if (!data
) return NULL
;
768 _appendCString(data
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
769 _appendCString(data
, "<SecureDownload xmlns=\"http://www.apple.com/2006/SecureDownload/1\">\n");
770 _appendCString(data
, " <resource>\n");
772 CFStringRef name
= CFDictionaryGetValue(plist
, SD_XML_NAME
);
774 _appendCString(data
, "\t<name>");
775 _appendCFString(data
, name
);
776 _appendCString(data
, "</name>\n");
779 CFNumberRef num
= CFDictionaryGetValue(plist
, SD_XML_SIZE
);
781 _appendCString(data
, "\t<size>");
782 _appendCFNumber(data
, num
);
783 _appendCString(data
, "</size>\n");
786 CFDateRef created
= CFDictionaryGetValue(plist
, SD_XML_CREATED
);
788 _appendCString(data
, "\t<created>");
789 _appendCFDate(data
, created
);
790 _appendCString(data
, "</created>\n");
793 _appendCString(data
, "\t<sites>\n");
794 CFArrayRef urls
= CFDictionaryGetValue(plist
, SD_XML_URL
);
796 CFIndex i
, count
= CFArrayGetCount(urls
);
797 for (i
= 0; i
< count
; ++i
) {
798 _appendCString(data
, "\t\t<url>");
799 _appendCFURL(data
, CFArrayGetValueAtIndex(urls
, i
));
800 _appendCString(data
, "</url>\n");
803 _appendCString(data
, "\t</sites>\n");
805 CFDictionaryRef verifications
= CFDictionaryGetValue(plist
, SD_XML_VERIFICATIONS
);
807 _appendCString(data
, "\t<verifications>\n");
808 CFArrayRef algorithms
= dictionaryGetSortedKeys(verifications
);
810 CFIndex i
, count
= CFArrayGetCount(algorithms
);
811 for (i
= 0; i
< count
; ++i
) {
812 CFStringRef algorithm
= CFArrayGetValueAtIndex(algorithms
, i
);
814 _appendCString(data
, "\t\t<verification algorithm=\"");
815 _appendCFString(data
, algorithm
);
816 _appendCString(data
, "\">\n");
817 CFDictionaryRef dict
= CFDictionaryGetValue(verifications
, algorithm
);
819 CFDataRef digest
= CFDictionaryGetValue(dict
, SD_XML_DIGEST
);
821 _appendCString(data
, "\t\t\t<digest>");
822 _appendCFData(data
, digest
);
823 _appendCString(data
, "</digest>\n");
826 CFNumberRef sector_size
= CFDictionaryGetValue(dict
, SD_XML_SECTOR_SIZE
);
828 _appendCString(data
, "\t\t\t<sector_size>");
829 _appendCFNumber(data
, sector_size
);
830 _appendCString(data
, "</sector_size>\n");
833 CFDataRef digests
= CFDictionaryGetValue(dict
, SD_XML_DIGESTS
);
835 _appendCString(data
, "\t\t\t<digests>");
836 _appendCFData(data
, digests
);
837 _appendCString(data
, "</digests>\n");
840 _appendCString(data
, "\t\t</verification>\n");
843 CFRelease(algorithms
);
845 _appendCString(data
, "\t</verifications>\n");
848 _appendCString(data
, " </resource>\n");
849 _appendCString(data
, "</SecureDownload>\n");
856 int main(int argc
, char* argv
[]) {
858 CFDataRef data
= read_data("/Users/kevin/Desktop/SecureDownloadXML/SecureDownload.xml");
860 CFPropertyListRef plist
= _SecureDownloadParseTicketXML(data
);
863 CFDataRef output
= _SecureDownloadCreateTicketXML(plist
);
865 write(STDOUT_FILENO
, CFDataGetBytePtr(output
), CFDataGetLength(output
));
873 // help look for leaks
874 cfprintf(stderr
, "pid = %d\n", getpid());