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());