1 #include <CoreFoundation/CoreFoundation.h>
3 #include "SecureDownloadInternal.h"
4 #include "SecCFRelease.h"
7 // SecureDownloadXML: SecureDownloadXML.c
8 // cc -g -framework CoreFoundation -o $@ $^
14 extern CFDataRef
read_data(char* path
);
17 #define SD_XML_NAMESPACE CFSTR("http://www.apple.com/2006/SecureDownload/1")
18 #define SD_XML_ROOT CFSTR("SecureDownload")
19 #define SD_XML_RESOURCE CFSTR("resource")
20 #define SD_XML_SITES CFSTR("sites")
21 #define SD_XML_VERIFICATION CFSTR("verification")
22 #define SD_XML_ATTR_ALGORITHM CFSTR("algorithm")
25 CFDictionaryRef namespaces
; // array of dictionaries of namespace declarations
29 CFMutableArrayRef plists
; // array of all resource plists
30 CFMutableDictionaryRef plist
; // most recent entry in the plists array
34 static inline unsigned char decode64(unsigned char c
)
105 // Decodes base64 data into a binary CFData object
106 // If first character on a line is not in the base64 alphabet, the line
108 static CF_RETURNS_RETAINED CFDataRef
decodeBase64Data(const UInt8
* ptr
, size_t len
) {
109 CFMutableDataRef result
= CFDataCreateMutable(NULL
, len
); // data can't exceed len bytes
110 if (!result
) return NULL
;
116 UInt8 triplet
[3] = {0, 0, 0};
119 http://www.faqs.org/rfcs/rfc3548.html
120 +--first octet--+-second octet--+--third octet--+
121 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
122 +-----------+---+-------+-------+---+-----------+
123 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
124 +--1.index--+--2.index--+--3.index--+--4.index--+
127 for (i
= 0, j
= 0; i
< len
; ++i
) {
128 unsigned char c
= ptr
[i
];
129 if (c
== ' ') { continue; }
130 if (c
== '\t') { continue; }
131 if (c
== '\r') { continue; }
132 if (c
== '\n') { skip
= 0; continue; }
133 if (skip
) { continue; }
134 if (!skip
&& c
== '=') { --j
; skip
= 1; continue; }
135 unsigned char x
= decode64(c
);
136 if (x
== 255) { skip
= 1; continue; }
139 triplet
[0] |= ((x
<< 2) & 0xFC);
142 triplet
[0] |= ((x
>> 4) & 0x03);
143 triplet
[1] |= ((x
<< 4) & 0xF0);
146 triplet
[1] |= ((x
>> 2) & 0x0F);
147 triplet
[2] |= ((x
<< 6) & 0xC0);
150 triplet
[2] |= ((x
) & 0x3F);
151 CFDataAppendBytes(result
, triplet
, j
);
152 memset(triplet
, 0, sizeof(triplet
));
157 CFDataAppendBytes(result
, triplet
, j
);
162 // Returns a CFString containing the base64 representation of the data.
163 // boolean argument for whether to line wrap at 64 columns or not.
164 static CF_RETURNS_RETAINED CFStringRef
encodeBase64String(const UInt8
* ptr
, size_t len
, int wrap
) {
165 const char* alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
166 "abcdefghijklmnopqrstuvwxyz"
169 // base64 encoded data uses 4 ASCII characters to represent 3 octets.
170 // There can be up to two == at the end of the base64 data for padding.
171 // If we are line wrapping then we need space for one newline character
172 // every 64 characters of output.
173 // Rounded 4/3 up to 2 to avoid floating point math.
175 //CFIndex max_len = (2*len) + 2;
176 //if (wrap) len = len + ((2*len) / 64) + 1;
178 CFMutableStringRef string
= CFStringCreateMutable(NULL
, 0);
179 if (!string
) return NULL
;
182 http://www.faqs.org/rfcs/rfc3548.html
183 +--first octet--+-second octet--+--third octet--+
184 |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
185 +-----------+---+-------+-------+---+-----------+
186 |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
187 +--1.index--+--2.index--+--3.index--+--4.index--+
189 int i
= 0; // octet offset into input data
190 int column
= 0; // output column number (used for line wrapping)
192 UniChar c
[16]; // buffer of characters to add to output
193 int j
= 0; // offset to place next character in buffer
194 int index
; // index into output alphabet
196 #define ADDCHAR(_X_) do { c[j++] = _X_; if (wrap && (++column == 64)) { column = 0; c[j++] = '\n'; } } while (0);
199 index
= (ptr
[i
] >> 2) & 0x3F;
200 ADDCHAR(alphabet
[index
]);
203 index
= (ptr
[i
] << 4) & 0x30;
205 index
= index
| ((ptr
[i
+1] >> 4) & 0x0F);
206 ADDCHAR(alphabet
[index
]);
207 } else { // end of input, pad as necessary
208 ADDCHAR(alphabet
[index
]);
215 index
= (ptr
[i
+1] << 2) & 0x3C;
217 index
= index
| ((ptr
[i
+2] >> 6) & 0x03);
218 ADDCHAR(alphabet
[index
]);
219 } else { // end of input, pad as necessary
220 ADDCHAR(alphabet
[index
]);
227 index
= (ptr
[i
+2]) & 0x3F;
228 ADDCHAR(alphabet
[index
]);
231 CFStringAppendCharacters(string
, c
, j
);
232 i
+= 3; // we processed 3 bytes of input
234 // end of data, append newline if we haven't already
235 if (wrap
&& c
[j
-1] != '\n') {
237 CFStringAppendCharacters(string
, c
, 1);
246 // makes a copy of the current namespaces dictionary, adding in any
247 // namespaces defined by the current node.
248 static CFDictionaryRef
copyNamespacesForNode(CFDictionaryRef namespaces
, CFXMLNodeRef node
) {
249 CFMutableDictionaryRef result
= NULL
;
251 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
253 // careful, don't use the info unless we ensure type == kCFXMLNodeTypeElement
254 CFXMLElementInfo
* info
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(node
);
257 // create our result dictionary
258 // there are four possible configurations:
259 // 1. previous dictionary exists, this is an element, and has attributes:
260 // clone existing dictionary, we may be adding to it
261 // 2. previous dictionary exists, not an element or no attributes:
262 // retain existing dictionary and return
263 // 3. no previous dictionary, this is an element, and has attributes:
264 // create new dictionary, we may be adding to it
265 // 4. no previous dictionary, not an element or no attributes:
266 // create new dictionary and return
268 if (namespaces
&& type
== kCFXMLNodeTypeElement
&& info
->attributes
&& info
->attributeOrder
) {
269 result
= CFDictionaryCreateMutableCopy(NULL
, 0, namespaces
);
270 } else if (namespaces
) {
271 result
= (CFMutableDictionaryRef
)CFRetain(namespaces
);
273 } else if (type
== kCFXMLNodeTypeElement
&& info
->attributes
&& info
->attributeOrder
) {
274 result
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
275 if (result
) CFDictionarySetValue(result
, CFSTR(""), CFSTR(""));
277 result
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
278 if (result
) CFDictionarySetValue(result
, CFSTR(""), CFSTR(""));
281 if (!result
) return NULL
;
284 // if we got this far, we're dealing with an XML element with
285 // attributes. check to see if any are xml namespace attributes.
287 CFArrayRef attrs
= info
->attributeOrder
;
288 CFIndex i
, count
= CFArrayGetCount(attrs
);
289 for (i
= 0; i
< count
; ++i
) {
290 CFStringRef attr
= CFArrayGetValueAtIndex(attrs
, i
);
292 if (CFEqual(CFSTR("xmlns"), attr
)) {
294 CFStringRef value
= CFDictionaryGetValue(info
->attributes
, attr
);
296 CFDictionarySetValue(result
, CFSTR(""), value
);
299 // if the attribute is in the "xmlns" namespace, then it's
300 // really a declaration of a new namespace. record it in our dictionary.
301 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, attr
, CFSTR(":"));
302 CFIndex numparts
= parts
? CFArrayGetCount(parts
) : 0;
304 CFStringRef prefix
= CFArrayGetValueAtIndex(parts
, 0);
305 CFStringRef suffix
= CFArrayGetValueAtIndex(parts
, 1);
306 if (CFEqual(CFSTR("xmlns"), prefix
)) {
307 CFStringRef value
= CFDictionaryGetValue(info
->attributes
, attr
);
309 CFDictionarySetValue(result
, suffix
, value
);
313 if (parts
) CFRelease(parts
);
319 // returns the current node's element name and namespace URI
320 // based on the currently defined namespaces.
321 static void copyNodeNamespaceAndName(CFDictionaryRef namespaces
, CFXMLNodeRef node
, CFStringRef
* namespace, CFStringRef
* name
) {
322 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
325 if (type
== kCFXMLNodeTypeElement
) {
326 CFStringRef qname
= CFXMLNodeGetString(node
);
327 CFArrayRef parts
= CFStringCreateArrayBySeparatingStrings(NULL
, qname
, CFSTR(":"));
328 CFIndex numparts
= parts
? CFArrayGetCount(parts
) : 0;
331 *namespace = CFRetain(CFDictionaryGetValue(namespaces
, CFSTR("")));
332 *name
= CFRetain(CFArrayGetValueAtIndex(parts
, 0));
333 } else if (numparts
== 2) {
334 CFStringRef prefix
= CFArrayGetValueAtIndex(parts
, 0);
335 CFStringRef ns
= CFDictionaryGetValue(namespaces
, prefix
);
336 *namespace = ns
? CFRetain(ns
) : NULL
;
337 *name
= CFRetain(CFArrayGetValueAtIndex(parts
, 1));
341 if (parts
) CFRelease(parts
);
345 // helper function for copyTreeString() below
346 // appends text nodes to the mutable string context
347 static void _appendTreeString(const void *value
, void *context
) {
348 CFXMLTreeRef tree
= (CFXMLTreeRef
)value
;
349 CFMutableStringRef result
= (CFMutableStringRef
)context
;
351 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
352 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
353 if (type
== kCFXMLNodeTypeElement
) {
354 CFTreeApplyFunctionToChildren(tree
, _appendTreeString
, result
);
355 } else if (type
== kCFXMLNodeTypeText
) {
356 CFStringRef str
= CFXMLNodeGetString(node
);
357 if (str
) CFStringAppend(result
, str
);
361 // equivalent to the XPATH string() function
362 // concatenates all text nodes into a single string
363 static CFMutableStringRef
copyTreeString(CFXMLTreeRef tree
) {
364 CFMutableStringRef result
= CFStringCreateMutable(NULL
, 0);
365 CFTreeApplyFunctionToChildren(tree
, _appendTreeString
, result
);
370 // returns an array of CFXMLTreeRef objects that are immediate
371 // children of the context node that match the specified element
372 // name and namespace URI
373 static CFArrayRef
copyChildrenWithName(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
374 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
375 tree
= CFTreeGetFirstChild(tree
);
377 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
378 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
379 if (type
== kCFXMLNodeTypeElement
) {
380 CFDictionaryRef namespaces
= copyNamespacesForNode(inNamespaces
, node
);
383 copyNodeNamespaceAndName(namespaces
, node
, &ns
, &n
);
385 if (ns
&& n
&& CFEqual(ns
, namespace) && CFEqual(n
, name
)) {
386 CFArrayAppendValue(result
, tree
);
388 if (ns
) CFRelease(ns
);
391 CFRelease(namespaces
);
394 tree
= CFTreeGetNextSibling(tree
);
399 // convenience function to find the first child element
400 // with the given element name and namespace URI
401 static CFXMLTreeRef
getChildWithName(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
402 CFXMLTreeRef result
= NULL
;
403 CFArrayRef array
= copyChildrenWithName(tree
, inNamespaces
, namespace, name
);
404 if (array
&& CFArrayGetCount(array
) > 0) {
405 result
= (CFXMLTreeRef
)CFArrayGetValueAtIndex(array
, 0);
407 if (array
) CFRelease(array
);
411 // returns the string value of the specified child node
412 static CFStringRef
copyChildWithNameAsString(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
413 CFStringRef result
= NULL
;
414 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
415 if (child
) result
= copyTreeString(child
);
419 // returns the integer value of the specified child node
420 static CFNumberRef
copyChildWithNameAsInteger(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
421 CFNumberRef result
= NULL
;
422 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
424 CFStringRef str
= copyTreeString(child
);
426 SInt32 size
= CFStringGetIntValue(str
);
427 result
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &size
);
434 // returns an array of URLs aggregated from the child
435 // nodes matching the given name and namespace URI.
436 static CFArrayRef
copyChildrenWithNameAsURLs(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
437 CFMutableArrayRef result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
438 CFArrayRef children
= copyChildrenWithName(tree
, inNamespaces
, namespace, name
);
440 CFIndex i
, count
= CFArrayGetCount(children
);
441 for (i
= 0; i
< count
; ++i
) {
442 CFXMLTreeRef child
= (CFXMLTreeRef
)CFArrayGetValueAtIndex(children
, i
);
443 CFStringRef str
= copyTreeString(child
);
445 CFURLRef url
= CFURLCreateWithString(NULL
, str
, NULL
);
447 CFArrayAppendValue(result
, url
);
458 // returns the base 64 decoded value of the specified child node
459 static CFDataRef
copyChildWithNameAsData(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
460 CFDataRef result
= NULL
;
461 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
463 CFStringRef str
= copyTreeString(child
);
465 CFIndex len
= CFStringGetLength(str
);
467 UInt8
* buffer
= malloc(len
);
468 // ASCII is one byte per character. Any inconvertible characters
469 // are assigned to whitespace and skipped.
471 if (CFStringGetBytes(str
, CFRangeMake(0, len
), kCFStringEncodingASCII
, ' ', 0, buffer
, len
, &used
)) {
472 result
= decodeBase64Data(buffer
, used
);
482 // returns the CFDate value of the specified child node
483 // whose string value is interpreted in W3C DateTime format
484 static CFDateRef
copyChildWithNameAsDate(CFXMLTreeRef tree
, CFDictionaryRef inNamespaces
, CFStringRef
namespace, CFStringRef name
) {
485 CFDateRef result
= NULL
;
486 CFXMLTreeRef child
= getChildWithName(tree
, inNamespaces
, namespace, name
);
488 CFMutableStringRef str
= copyTreeString(child
);
490 CFStringTrimWhitespace(str
);
491 if (CFStringGetLength(str
) > 21) {
492 CFStringRef year
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(0, 4));
493 CFStringRef month
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(5, 2));
494 CFStringRef day
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(8, 2));
495 CFStringRef hour
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(11, 2));
496 CFStringRef minute
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(14, 2));
497 CFStringRef second
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(17, 2));
498 CFStringRef tenth
= CFStringCreateWithSubstring(NULL
, str
, CFRangeMake(20, 1));
500 CFGregorianDate gregory
;
501 memset(&gregory
, 0, sizeof(gregory
));
502 if (year
) { gregory
.year
= CFStringGetIntValue(year
); CFRelease(year
); }
503 if (month
) { gregory
.month
= CFStringGetIntValue(month
); CFRelease(month
); }
504 if (day
) { gregory
.day
= CFStringGetIntValue(day
); CFRelease(day
); }
505 if (hour
) { gregory
.hour
= CFStringGetIntValue(hour
); CFRelease(hour
); }
506 if (minute
) { gregory
.minute
= CFStringGetIntValue(minute
); CFRelease(minute
); }
507 if (second
) { gregory
.second
= (double)CFStringGetIntValue(second
); CFRelease(second
); }
508 if (tenth
) { gregory
.second
+= ((double)CFStringGetIntValue(tenth
)/(double)10.0); CFRelease(tenth
); }
510 CFTimeZoneRef tz
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
512 CFAbsoluteTime at
= CFGregorianDateGetAbsoluteTime(gregory
, tz
);
513 result
= CFDateCreate(NULL
, at
);
524 // generic parser for XML nodes in our ticket format
525 static void _parseXMLNode(const void *value
, void *context
) {
526 CFXMLTreeRef tree
= (CFXMLTreeRef
)value
;
527 struct parseState
* state
= (struct parseState
*)context
;
529 CFXMLNodeRef node
= CFXMLTreeGetNode(tree
);
532 CFDictionaryRef namespaces
= copyNamespacesForNode(state
->namespaces
, node
);
534 CFXMLNodeTypeCode type
= CFXMLNodeGetTypeCode(node
);
538 if (type
== kCFXMLNodeTypeElement
) {
539 CFStringRef ns
, name
;
540 copyNodeNamespaceAndName(namespaces
, node
, &ns
, &name
);
544 if (CFEqual(ns
, SD_XML_NAMESPACE
)) {
545 if (CFEqual(name
, SD_XML_ROOT
)) {
549 } else if (CFEqual(name
, SD_XML_RESOURCE
)) {
551 state
->plist
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
553 CFArrayAppendValue(state
->plists
, state
->plist
);
554 CFRelease(state
->plist
);
557 CFStringRef name
= copyChildWithNameAsString(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_NAME
);
558 if (name
&& state
->plist
) {
559 CFDictionarySetValue(state
->plist
, SD_XML_NAME
, name
);
563 CFNumberRef size
= copyChildWithNameAsInteger(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_SIZE
);
564 if (size
&& state
->plist
) {
565 CFDictionarySetValue(state
->plist
, SD_XML_SIZE
, size
);
569 CFDateRef created
= copyChildWithNameAsDate(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_CREATED
);
570 if (created
&& state
->plist
) {
571 CFDictionarySetValue(state
->plist
, SD_XML_CREATED
, created
);
573 CFReleaseNull(created
);
577 } else if (CFEqual(name
, SD_XML_SITES
)) {
578 CFArrayRef urls
= copyChildrenWithNameAsURLs(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_URL
);
579 if (urls
&& state
->plist
) {
580 CFDictionarySetValue(state
->plist
, SD_XML_URL
, urls
);
584 } else if (CFEqual(name
, SD_XML_VERIFICATIONS
)) {
585 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
586 if (dict
&& state
->plist
) {
587 CFDictionarySetValue(state
->plist
, SD_XML_VERIFICATIONS
, dict
);
592 } else if (CFEqual(name
, SD_XML_VERIFICATION
) && state
->plist
) {
593 CFMutableDictionaryRef verifications
= (CFMutableDictionaryRef
)CFDictionaryGetValue(state
->plist
, SD_XML_VERIFICATIONS
);
594 CFXMLElementInfo
* info
= (CFXMLElementInfo
*)CFXMLNodeGetInfoPtr(node
);
595 if (verifications
&& info
&& info
->attributes
) {
596 CFStringRef algorithm
= CFDictionaryGetValue(info
->attributes
, SD_XML_ATTR_ALGORITHM
);
598 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
601 #pragma unused(child)
603 CFNumberRef sector_size
= copyChildWithNameAsInteger(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_SECTOR_SIZE
);
605 CFDictionarySetValue(dict
, SD_XML_SECTOR_SIZE
, sector_size
);
606 CFRelease(sector_size
);
609 CFDataRef digest
= copyChildWithNameAsData(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_DIGEST
);
611 CFDictionarySetValue(dict
, SD_XML_DIGEST
, digest
);
615 CFDataRef digests
= copyChildWithNameAsData(tree
, namespaces
, SD_XML_NAMESPACE
, SD_XML_DIGESTS
);
617 CFDictionarySetValue(dict
, SD_XML_DIGESTS
, digests
);
621 CFDictionarySetValue(verifications
, algorithm
, dict
);
629 cfprintf(stderr
, "%sELEM:\t%@\t[%@]\n", state
->prefix
, name
, ns
);
632 if (ns
) CFRelease(ns
);
633 if (name
) CFRelease(name
);
634 } else if (type
== kCFXMLNodeTypeWhitespace
) {
638 CFStringRef str
= CFXMLNodeGetString(node
);
639 cfprintf(stderr
, "%s% 4d:\t%@\n", state
->prefix
, type
, str
);
643 // only recurse further if we have been specifically instructed to
646 struct parseState local
;
647 memcpy(&local
, state
, sizeof(struct parseState
));
648 local
.namespaces
= namespaces
;
650 asprintf(&local
.prefix
, "%s ", state
->prefix
);
652 CFTreeApplyFunctionToChildren(tree
, _parseXMLNode
, &local
);
657 if (namespaces
) CFRelease(namespaces
);
660 CFPropertyListRef
_SecureDownloadParseTicketXML(CFDataRef xmlData
) {
661 if (!xmlData
) return NULL
;
664 CFXMLTreeRef tree
= CFXMLTreeCreateFromData(NULL
, xmlData
, url
, kCFXMLParserNoOptions
, kCFXMLNodeCurrentVersion
);
665 if (!tree
) return NULL
;
667 struct parseState state
;
668 memset(&state
, 0, sizeof(state
));
672 state
.plists
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
674 CFTreeApplyFunctionToChildren(tree
, _parseXMLNode
, &state
);
678 CFPropertyListRef result
= NULL
;
679 // For now, only return the first resource encountered
680 if (state
.plists
&& CFArrayGetCount(state
.plists
) > 0) {
681 result
= CFArrayGetValueAtIndex(state
.plists
, 0);
684 if (state
.plists
) CFRelease(state
.plists
);
688 static void _appendCString(CFMutableDataRef data
, const char* cstring
) {
689 CFDataAppendBytes(data
, (UInt8
*)cstring
, strlen(cstring
));
692 static void _appendCFString(CFMutableDataRef data
, CFStringRef string
) {
693 CFDataRef utf8
= CFStringCreateExternalRepresentation(NULL
, string
, kCFStringEncodingUTF8
, '?');
695 CFDataAppendBytes(data
, CFDataGetBytePtr(utf8
), CFDataGetLength(utf8
));
700 static void _appendCFNumber(CFMutableDataRef data
, CFNumberRef number
) {
701 CFStringRef str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@"), number
);
703 _appendCFString(data
, str
);
708 static void _appendCFURL(CFMutableDataRef data
, CFURLRef url
) {
709 CFURLRef abs
= CFURLCopyAbsoluteURL(url
);
711 CFStringRef str
= CFURLGetString(abs
);
713 _appendCFString(data
, str
);
719 // appends data in base64 encoded form
720 static void _appendCFData(CFMutableDataRef data
, CFDataRef moreData
) {
721 CFStringRef str
= encodeBase64String(CFDataGetBytePtr(moreData
), CFDataGetLength(moreData
), 0);
723 _appendCFString(data
, str
);
728 // appends a date in W3C DateTime format
729 static void _appendCFDate(CFMutableDataRef data
, CFDateRef date
) {
730 CFLocaleRef locale
= CFLocaleCreate(NULL
, CFSTR("en_US"));
732 CFDateFormatterRef formatter
= CFDateFormatterCreate(NULL
, locale
, kCFDateFormatterNoStyle
, kCFDateFormatterNoStyle
);
734 CFDateFormatterSetFormat(formatter
, CFSTR("yyyy-MM-dd'T'HH:mm:ss.S'Z'"));
735 CFTimeZoneRef tz
= CFTimeZoneCreateWithTimeIntervalFromGMT(NULL
, 0);
737 CFDateFormatterSetProperty(formatter
, kCFDateFormatterTimeZone
, tz
);
738 CFStringRef str
= CFDateFormatterCreateStringWithDate(NULL
, formatter
, date
);
740 _appendCFString(data
, str
);
745 CFRelease(formatter
);
751 static CFArrayRef
dictionaryGetSortedKeys(CFDictionaryRef dictionary
) {
752 CFIndex count
= CFDictionaryGetCount(dictionary
);
754 const void** keys
= malloc(sizeof(CFTypeRef
) * count
);
755 CFDictionaryGetKeysAndValues(dictionary
, keys
, NULL
);
756 CFArrayRef keysArray
= CFArrayCreate(NULL
, keys
, count
, &kCFTypeArrayCallBacks
);
757 CFMutableArrayRef sortedKeys
= CFArrayCreateMutableCopy(NULL
, count
, keysArray
);
758 CFRelease(keysArray
);
761 CFArraySortValues(sortedKeys
, CFRangeMake(0, count
), (CFComparatorFunction
)CFStringCompare
, 0);
765 CFDataRef
_SecureDownloadCreateTicketXML(CFPropertyListRef plist
) {
766 CFMutableDataRef data
= CFDataCreateMutable(NULL
, 0);
767 if (!data
) return NULL
;
769 _appendCString(data
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
770 _appendCString(data
, "<SecureDownload xmlns=\"http://www.apple.com/2006/SecureDownload/1\">\n");
771 _appendCString(data
, " <resource>\n");
773 CFStringRef name
= CFDictionaryGetValue(plist
, SD_XML_NAME
);
775 _appendCString(data
, "\t<name>");
776 _appendCFString(data
, name
);
777 _appendCString(data
, "</name>\n");
780 CFNumberRef num
= CFDictionaryGetValue(plist
, SD_XML_SIZE
);
782 _appendCString(data
, "\t<size>");
783 _appendCFNumber(data
, num
);
784 _appendCString(data
, "</size>\n");
787 CFDateRef created
= CFDictionaryGetValue(plist
, SD_XML_CREATED
);
789 _appendCString(data
, "\t<created>");
790 _appendCFDate(data
, created
);
791 _appendCString(data
, "</created>\n");
794 _appendCString(data
, "\t<sites>\n");
795 CFArrayRef urls
= CFDictionaryGetValue(plist
, SD_XML_URL
);
797 CFIndex i
, count
= CFArrayGetCount(urls
);
798 for (i
= 0; i
< count
; ++i
) {
799 _appendCString(data
, "\t\t<url>");
800 _appendCFURL(data
, CFArrayGetValueAtIndex(urls
, i
));
801 _appendCString(data
, "</url>\n");
804 _appendCString(data
, "\t</sites>\n");
806 CFDictionaryRef verifications
= CFDictionaryGetValue(plist
, SD_XML_VERIFICATIONS
);
808 _appendCString(data
, "\t<verifications>\n");
809 CFArrayRef algorithms
= dictionaryGetSortedKeys(verifications
);
811 CFIndex i
, count
= CFArrayGetCount(algorithms
);
812 for (i
= 0; i
< count
; ++i
) {
813 CFStringRef algorithm
= CFArrayGetValueAtIndex(algorithms
, i
);
815 _appendCString(data
, "\t\t<verification algorithm=\"");
816 _appendCFString(data
, algorithm
);
817 _appendCString(data
, "\">\n");
818 CFDictionaryRef dict
= CFDictionaryGetValue(verifications
, algorithm
);
820 CFDataRef digest
= CFDictionaryGetValue(dict
, SD_XML_DIGEST
);
822 _appendCString(data
, "\t\t\t<digest>");
823 _appendCFData(data
, digest
);
824 _appendCString(data
, "</digest>\n");
827 CFNumberRef sector_size
= CFDictionaryGetValue(dict
, SD_XML_SECTOR_SIZE
);
829 _appendCString(data
, "\t\t\t<sector_size>");
830 _appendCFNumber(data
, sector_size
);
831 _appendCString(data
, "</sector_size>\n");
834 CFDataRef digests
= CFDictionaryGetValue(dict
, SD_XML_DIGESTS
);
836 _appendCString(data
, "\t\t\t<digests>");
837 _appendCFData(data
, digests
);
838 _appendCString(data
, "</digests>\n");
841 _appendCString(data
, "\t\t</verification>\n");
844 CFRelease(algorithms
);
846 _appendCString(data
, "\t</verifications>\n");
849 _appendCString(data
, " </resource>\n");
850 _appendCString(data
, "</SecureDownload>\n");
857 int main(int argc
, char* argv
[]) {
859 CFDataRef data
= read_data("/Users/kevin/Desktop/SecureDownloadXML/SecureDownload.xml");
861 CFPropertyListRef plist
= _SecureDownloadParseTicketXML(data
);
864 CFDataRef output
= _SecureDownloadCreateTicketXML(plist
);
866 write(STDOUT_FILENO
, CFDataGetBytePtr(output
), CFDataGetLength(output
));
874 // help look for leaks
875 cfprintf(stderr
, "pid = %d\n", getpid());