SecureDownloadInternal.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include "SecureDownloadInternal.h"
#include "SecCFRelease.h"
#include "simulatecrash_assert.h"
#define LOCAL_DEBUG 0
#if LOCAL_DEBUG
extern CFDataRef read_data(char* path);
#endif
#define SD_XML_NAMESPACE CFSTR("http://www.apple.com/2006/SecureDownload/1")
#define SD_XML_ROOT CFSTR("SecureDownload")
#define SD_XML_RESOURCE CFSTR("resource")
#define SD_XML_SITES CFSTR("sites")
#define SD_XML_VERIFICATION CFSTR("verification")
#define SD_XML_ATTR_ALGORITHM CFSTR("algorithm")
struct parseState {
CFDictionaryRef namespaces; #if LOCAL_DEBUG
char* prefix;
#endif
CFMutableArrayRef plists; CFMutableDictionaryRef plist; };
static inline unsigned char decode64(unsigned char c)
{
switch(c) {
case 'A': return 0;
case 'B': return 1;
case 'C': return 2;
case 'D': return 3;
case 'E': return 4;
case 'F': return 5;
case 'G': return 6;
case 'H': return 7;
case 'I': return 8;
case 'J': return 9;
case 'K': return 10;
case 'L': return 11;
case 'M': return 12;
case 'N': return 13;
case 'O': return 14;
case 'P': return 15;
case 'Q': return 16;
case 'R': return 17;
case 'S': return 18;
case 'T': return 19;
case 'U': return 20;
case 'V': return 21;
case 'W': return 22;
case 'X': return 23;
case 'Y': return 24;
case 'Z': return 25;
case 'a': return 26;
case 'b': return 27;
case 'c': return 28;
case 'd': return 29;
case 'e': return 30;
case 'f': return 31;
case 'g': return 32;
case 'h': return 33;
case 'i': return 34;
case 'j': return 35;
case 'k': return 36;
case 'l': return 37;
case 'm': return 38;
case 'n': return 39;
case 'o': return 40;
case 'p': return 41;
case 'q': return 42;
case 'r': return 43;
case 's': return 44;
case 't': return 45;
case 'u': return 46;
case 'v': return 47;
case 'w': return 48;
case 'x': return 49;
case 'y': return 50;
case 'z': return 51;
case '0': return 52;
case '1': return 53;
case '2': return 54;
case '3': return 55;
case '4': return 56;
case '5': return 57;
case '6': return 58;
case '7': return 59;
case '8': return 60;
case '9': return 61;
case '+': return 62;
case '/': return 63;
}
return 255;
}
static CF_RETURNS_RETAINED CFDataRef decodeBase64Data(const UInt8* ptr, size_t len) {
CFMutableDataRef result = CFDataCreateMutable(NULL, len); if (!result) return NULL;
CFIndex i, j;
int skip = 0;
UInt8 triplet[3] = {0, 0, 0};
for (i = 0, j = 0; i < len; ++i) {
unsigned char c = ptr[i];
if (c == ' ') { continue; }
if (c == '\t') { continue; }
if (c == '\r') { continue; }
if (c == '\n') { skip = 0; continue; }
if (skip) { continue; }
if (!skip && c == '=') { --j; skip = 1; continue; }
unsigned char x = decode64(c);
if (x == 255) { skip = 1; continue; }
if (j == 0) {
triplet[0] |= ((x << 2) & 0xFC);
++j;
} else if (j == 1) {
triplet[0] |= ((x >> 4) & 0x03);
triplet[1] |= ((x << 4) & 0xF0);
++j;
} else if (j == 2) {
triplet[1] |= ((x >> 2) & 0x0F);
triplet[2] |= ((x << 6) & 0xC0);
++j;
} else if (j == 3) {
triplet[2] |= ((x) & 0x3F);
CFDataAppendBytes(result, triplet, j);
memset(triplet, 0, sizeof(triplet));
j = 0;
}
}
if (j > 0) {
CFDataAppendBytes(result, triplet, j);
}
return result;
}
static CF_RETURNS_RETAINED CFStringRef encodeBase64String(const UInt8* ptr, size_t len, int wrap) {
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/=";
CFMutableStringRef string = CFStringCreateMutable(NULL, 0);
if (!string) return NULL;
int i = 0; int column = 0; for (;;) {
UniChar c[16]; int j = 0; int index;
#define ADDCHAR(_X_) do { c[j++] = _X_; if (wrap && (++column == 64)) { column = 0; c[j++] = '\n'; } } while (0);
index = (ptr[i] >> 2) & 0x3F;
ADDCHAR(alphabet[index]);
index = (ptr[i] << 4) & 0x30;
if ((i+1) < len) {
index = index | ((ptr[i+1] >> 4) & 0x0F);
ADDCHAR(alphabet[index]);
} else { ADDCHAR(alphabet[index]);
ADDCHAR('=');
ADDCHAR('=');
}
if ((i+1) < len) {
index = (ptr[i+1] << 2) & 0x3C;
if ((i+2) < len) {
index = index | ((ptr[i+2] >> 6) & 0x03);
ADDCHAR(alphabet[index]);
} else { ADDCHAR(alphabet[index]);
ADDCHAR('=');
}
}
if ((i+2) < len) {
index = (ptr[i+2]) & 0x3F;
ADDCHAR(alphabet[index]);
}
CFStringAppendCharacters(string, c, j);
i += 3; if (i >= len) {
if (wrap && c[j-1] != '\n') {
c[0] = '\n';
CFStringAppendCharacters(string, c, 1);
}
break;
}
}
return string;
}
static CFDictionaryRef copyNamespacesForNode(CFDictionaryRef namespaces, CFXMLNodeRef node) {
CFMutableDictionaryRef result = NULL;
CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
CFXMLElementInfo* info = (CFXMLElementInfo*)CFXMLNodeGetInfoPtr(node);
if (namespaces && type == kCFXMLNodeTypeElement && info->attributes && info->attributeOrder) {
result = CFDictionaryCreateMutableCopy(NULL, 0, namespaces);
} else if (namespaces) {
result = (CFMutableDictionaryRef)CFRetain(namespaces);
return result;
} else if (type == kCFXMLNodeTypeElement && info->attributes && info->attributeOrder) {
result = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (result) CFDictionarySetValue(result, CFSTR(""), CFSTR(""));
} else {
result = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (result) CFDictionarySetValue(result, CFSTR(""), CFSTR(""));
return result;
}
if (!result) return NULL;
CFArrayRef attrs = info->attributeOrder;
CFIndex i, count = CFArrayGetCount(attrs);
for (i = 0; i < count; ++i) {
CFStringRef attr = CFArrayGetValueAtIndex(attrs, i);
if (CFEqual(CFSTR("xmlns"), attr)) {
CFStringRef value = CFDictionaryGetValue(info->attributes, attr);
if (value) {
CFDictionarySetValue(result, CFSTR(""), value);
}
} else {
CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(NULL, attr, CFSTR(":"));
CFIndex numparts = parts ? CFArrayGetCount(parts) : 0;
if (numparts == 2) {
CFStringRef prefix = CFArrayGetValueAtIndex(parts, 0);
CFStringRef suffix = CFArrayGetValueAtIndex(parts, 1);
if (CFEqual(CFSTR("xmlns"), prefix)) {
CFStringRef value = CFDictionaryGetValue(info->attributes, attr);
if (value) {
CFDictionarySetValue(result, suffix, value);
}
}
}
if (parts) CFRelease(parts);
}
}
return result;
}
static void copyNodeNamespaceAndName(CFDictionaryRef namespaces, CFXMLNodeRef node, CFStringRef* namespace, CFStringRef* name) {
CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
*namespace = NULL;
*name = NULL;
if (type == kCFXMLNodeTypeElement) {
CFStringRef qname = CFXMLNodeGetString(node);
CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(NULL, qname, CFSTR(":"));
CFIndex numparts = parts ? CFArrayGetCount(parts) : 0;
if (numparts == 1) {
*namespace = CFRetain(CFDictionaryGetValue(namespaces, CFSTR("")));
*name = CFRetain(CFArrayGetValueAtIndex(parts, 0));
} else if (numparts == 2) {
CFStringRef prefix = CFArrayGetValueAtIndex(parts, 0);
CFStringRef ns = CFDictionaryGetValue(namespaces, prefix);
*namespace = ns ? CFRetain(ns) : NULL;
*name = CFRetain(CFArrayGetValueAtIndex(parts, 1));
} else {
}
if (parts) CFRelease(parts);
}
}
static void _appendTreeString(const void *value, void *context) {
CFXMLTreeRef tree = (CFXMLTreeRef)value;
CFMutableStringRef result = (CFMutableStringRef)context;
CFXMLNodeRef node = CFXMLTreeGetNode(tree);
CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
if (type == kCFXMLNodeTypeElement) {
CFTreeApplyFunctionToChildren(tree, _appendTreeString, result);
} else if (type == kCFXMLNodeTypeText) {
CFStringRef str = CFXMLNodeGetString(node);
if (str) CFStringAppend(result, str);
}
}
static CFMutableStringRef copyTreeString(CFXMLTreeRef tree) {
CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
CFTreeApplyFunctionToChildren(tree, _appendTreeString, result);
return result;
}
static CFArrayRef copyChildrenWithName(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
tree = CFTreeGetFirstChild(tree);
while (tree) {
CFXMLNodeRef node = CFXMLTreeGetNode(tree);
CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
if (type == kCFXMLNodeTypeElement) {
CFDictionaryRef namespaces = copyNamespacesForNode(inNamespaces, node);
if (namespaces) {
CFStringRef ns, n;
copyNodeNamespaceAndName(namespaces, node, &ns, &n);
if (ns && n && CFEqual(ns, namespace) && CFEqual(n, name)) {
CFArrayAppendValue(result, tree);
}
if (ns) CFRelease(ns);
if (n) CFRelease(n);
CFRelease(namespaces);
}
}
tree = CFTreeGetNextSibling(tree);
}
return result;
}
static CFXMLTreeRef getChildWithName(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
CFXMLTreeRef result = NULL;
CFArrayRef array = copyChildrenWithName(tree, inNamespaces, namespace, name);
if (array && CFArrayGetCount(array) > 0) {
result = (CFXMLTreeRef)CFArrayGetValueAtIndex(array, 0);
}
if (array) CFRelease(array);
return result;
}
static CFStringRef copyChildWithNameAsString(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
CFStringRef result = NULL;
CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
if (child) result = copyTreeString(child);
return result;
}
static CFNumberRef copyChildWithNameAsInteger(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
CFNumberRef result = NULL;
CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
if (child) {
CFStringRef str = copyTreeString(child);
if (str) {
SInt32 size = CFStringGetIntValue(str);
result = CFNumberCreate(NULL, kCFNumberSInt32Type, &size);
CFRelease(str);
}
}
return result;
}
static CFArrayRef copyChildrenWithNameAsURLs(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayRef children = copyChildrenWithName(tree, inNamespaces, namespace, name);
if (children) {
CFIndex i, count = CFArrayGetCount(children);
for (i = 0; i < count; ++i) {
CFXMLTreeRef child = (CFXMLTreeRef)CFArrayGetValueAtIndex(children, i);
CFStringRef str = copyTreeString(child);
if (str) {
CFURLRef url = CFURLCreateWithString(NULL, str, NULL);
if (url) {
CFArrayAppendValue(result, url);
CFRelease(url);
}
CFRelease(str);
}
}
CFRelease(children);
}
return result;
}
static CFDataRef copyChildWithNameAsData(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
CFDataRef result = NULL;
CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
if (child) {
CFStringRef str = copyTreeString(child);
if (str) {
CFIndex len = CFStringGetLength(str);
CFIndex used;
UInt8* buffer = malloc(len);
if (buffer) {
if (CFStringGetBytes(str, CFRangeMake(0, len), kCFStringEncodingASCII, ' ', 0, buffer, len, &used)) {
result = decodeBase64Data(buffer, used);
}
free(buffer);
}
CFRelease(str);
}
}
return result;
}
static CFDateRef copyChildWithNameAsDate(CFXMLTreeRef tree, CFDictionaryRef inNamespaces, CFStringRef namespace, CFStringRef name) {
CFDateRef result = NULL;
CFXMLTreeRef child = getChildWithName(tree, inNamespaces, namespace, name);
if (child) {
CFMutableStringRef str = copyTreeString(child);
if (str) {
CFStringTrimWhitespace(str);
if (CFStringGetLength(str) > 21) {
CFStringRef year = CFStringCreateWithSubstring(NULL, str, CFRangeMake(0, 4));
CFStringRef month = CFStringCreateWithSubstring(NULL, str, CFRangeMake(5, 2));
CFStringRef day = CFStringCreateWithSubstring(NULL, str, CFRangeMake(8, 2));
CFStringRef hour = CFStringCreateWithSubstring(NULL, str, CFRangeMake(11, 2));
CFStringRef minute = CFStringCreateWithSubstring(NULL, str, CFRangeMake(14, 2));
CFStringRef second = CFStringCreateWithSubstring(NULL, str, CFRangeMake(17, 2));
CFStringRef tenth = CFStringCreateWithSubstring(NULL, str, CFRangeMake(20, 1));
CFGregorianDate gregory;
memset(&gregory, 0, sizeof(gregory));
if (year) { gregory.year = CFStringGetIntValue(year); CFRelease(year); }
if (month) { gregory.month = CFStringGetIntValue(month); CFRelease(month); }
if (day) { gregory.day = CFStringGetIntValue(day); CFRelease(day); }
if (hour) { gregory.hour = CFStringGetIntValue(hour); CFRelease(hour); }
if (minute) { gregory.minute = CFStringGetIntValue(minute); CFRelease(minute); }
if (second) { gregory.second = (double)CFStringGetIntValue(second); CFRelease(second); }
if (tenth) { gregory.second += ((double)CFStringGetIntValue(tenth)/(double)10.0); CFRelease(tenth); }
CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
if (tz) {
CFAbsoluteTime at = CFGregorianDateGetAbsoluteTime(gregory, tz);
result = CFDateCreate(NULL, at);
CFRelease(tz);
}
}
CFRelease(str);
}
}
return result;
}
static void _parseXMLNode(const void *value, void *context) {
CFXMLTreeRef tree = (CFXMLTreeRef)value;
struct parseState* state = (struct parseState*)context;
CFXMLNodeRef node = CFXMLTreeGetNode(tree);
assert(node);
CFDictionaryRef namespaces = copyNamespacesForNode(state->namespaces, node);
CFXMLNodeTypeCode type = CFXMLNodeGetTypeCode(node);
int descend = 0;
if (type == kCFXMLNodeTypeElement) {
CFStringRef ns, name;
copyNodeNamespaceAndName(namespaces, node, &ns, &name);
if (ns && name) {
if (CFEqual(ns, SD_XML_NAMESPACE)) {
if (CFEqual(name, SD_XML_ROOT)) {
descend = 1;
} else if (CFEqual(name, SD_XML_RESOURCE)) {
state->plist = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (state->plist) {
CFArrayAppendValue(state->plists, state->plist);
CFRelease(state->plist);
}
CFStringRef name = copyChildWithNameAsString(tree, namespaces, SD_XML_NAMESPACE, SD_XML_NAME);
if (name && state->plist) {
CFDictionarySetValue(state->plist, SD_XML_NAME, name);
}
CFReleaseNull(name);
CFNumberRef size = copyChildWithNameAsInteger(tree, namespaces, SD_XML_NAMESPACE, SD_XML_SIZE);
if (size && state->plist) {
CFDictionarySetValue(state->plist, SD_XML_SIZE, size);
}
CFReleaseNull(size);
CFDateRef created = copyChildWithNameAsDate(tree, namespaces, SD_XML_NAMESPACE, SD_XML_CREATED);
if (created && state->plist) {
CFDictionarySetValue(state->plist, SD_XML_CREATED, created);
}
CFReleaseNull(created);
descend = 1;
} else if (CFEqual(name, SD_XML_SITES)) {
CFArrayRef urls = copyChildrenWithNameAsURLs(tree, namespaces, SD_XML_NAMESPACE, SD_XML_URL);
if (urls && state->plist) {
CFDictionarySetValue(state->plist, SD_XML_URL, urls);
}
CFReleaseNull(urls);
} else if (CFEqual(name, SD_XML_VERIFICATIONS)) {
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict && state->plist) {
CFDictionarySetValue(state->plist, SD_XML_VERIFICATIONS, dict);
CFRelease(dict);
descend = 1;
}
} else if (CFEqual(name, SD_XML_VERIFICATION) && state->plist) {
CFMutableDictionaryRef verifications = (CFMutableDictionaryRef)CFDictionaryGetValue(state->plist, SD_XML_VERIFICATIONS);
CFXMLElementInfo* info = (CFXMLElementInfo*)CFXMLNodeGetInfoPtr(node);
if (verifications && info && info->attributes) {
CFStringRef algorithm = CFDictionaryGetValue(info->attributes, SD_XML_ATTR_ALGORITHM);
if (algorithm) {
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict) {
CFXMLTreeRef child;
#pragma unused(child)
CFNumberRef sector_size = copyChildWithNameAsInteger(tree, namespaces, SD_XML_NAMESPACE, SD_XML_SECTOR_SIZE);
if (sector_size) {
CFDictionarySetValue(dict, SD_XML_SECTOR_SIZE, sector_size);
CFRelease(sector_size);
}
CFDataRef digest = copyChildWithNameAsData(tree, namespaces, SD_XML_NAMESPACE, SD_XML_DIGEST);
if (digest) {
CFDictionarySetValue(dict, SD_XML_DIGEST, digest);
CFRelease(digest);
}
CFDataRef digests = copyChildWithNameAsData(tree, namespaces, SD_XML_NAMESPACE, SD_XML_DIGESTS);
if (digests) {
CFDictionarySetValue(dict, SD_XML_DIGESTS, digests);
CFRelease(digests);
}
CFDictionarySetValue(verifications, algorithm, dict);
CFRelease(dict);
}
}
}
}
}
#if LOCAL_DEBUG
cfprintf(stderr, "%sELEM:\t%@\t[%@]\n", state->prefix, name, ns);
#endif
}
if (ns) CFRelease(ns);
if (name) CFRelease(name);
} else if (type == kCFXMLNodeTypeWhitespace) {
} else {
#if LOCAL_DEBUG
CFStringRef str = CFXMLNodeGetString(node);
cfprintf(stderr, "%s% 4d:\t%@\n", state->prefix, type, str);
#endif
}
if (descend) {
struct parseState local;
memcpy(&local, state, sizeof(struct parseState));
local.namespaces = namespaces;
#if LOCAL_DEBUG
asprintf(&local.prefix, "%s ", state->prefix);
#endif
CFTreeApplyFunctionToChildren(tree, _parseXMLNode, &local);
#if LOCAL_DEBUG
free(local.prefix);
#endif
}
if (namespaces) CFRelease(namespaces);
}
CFPropertyListRef _SecureDownloadParseTicketXML(CFDataRef xmlData) {
if (!xmlData) return NULL;
CFURLRef url = NULL;
CFXMLTreeRef tree = CFXMLTreeCreateFromData(NULL, xmlData, url, kCFXMLParserNoOptions, kCFXMLNodeCurrentVersion);
if (!tree) return NULL;
struct parseState state;
memset(&state, 0, sizeof(state));
#if LOCAL_DEBUG
state.prefix = "";
#endif
state.plists = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (state.plists) {
CFTreeApplyFunctionToChildren(tree, _parseXMLNode, &state);
}
CFRelease(tree);
CFPropertyListRef result = NULL;
if (state.plists && CFArrayGetCount(state.plists) > 0) {
result = CFArrayGetValueAtIndex(state.plists, 0);
CFRetain(result);
}
if (state.plists) CFRelease(state.plists);
return result;
}
static void _appendCString(CFMutableDataRef data, const char* cstring) {
CFDataAppendBytes(data, (UInt8*)cstring, strlen(cstring));
}
static void _appendCFString(CFMutableDataRef data, CFStringRef string) {
CFDataRef utf8 = CFStringCreateExternalRepresentation(NULL, string, kCFStringEncodingUTF8, '?');
if (utf8) {
CFDataAppendBytes(data, CFDataGetBytePtr(utf8), CFDataGetLength(utf8));
CFRelease(utf8);
}
}
static void _appendCFNumber(CFMutableDataRef data, CFNumberRef number) {
CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), number);
if (str) {
_appendCFString(data, str);
CFRelease(str);
}
}
static void _appendCFURL(CFMutableDataRef data, CFURLRef url) {
CFURLRef abs = CFURLCopyAbsoluteURL(url);
if (abs) {
CFStringRef str = CFURLGetString(abs);
if (str) {
_appendCFString(data, str);
}
CFRelease(abs);
}
}
static void _appendCFData(CFMutableDataRef data, CFDataRef moreData) {
CFStringRef str = encodeBase64String(CFDataGetBytePtr(moreData), CFDataGetLength(moreData), 0);
if (str) {
_appendCFString(data, str);
CFRelease(str);
}
}
static void _appendCFDate(CFMutableDataRef data, CFDateRef date) {
CFLocaleRef locale = CFLocaleCreate(NULL, CFSTR("en_US"));
if (locale) {
CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
if (formatter) {
CFDateFormatterSetFormat(formatter, CFSTR("yyyy-MM-dd'T'HH:mm:ss.S'Z'"));
CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0);
if (tz) {
CFDateFormatterSetProperty(formatter, kCFDateFormatterTimeZone, tz);
CFStringRef str = CFDateFormatterCreateStringWithDate(NULL, formatter, date);
if (str) {
_appendCFString(data, str);
CFRelease(str);
}
CFRelease(tz);
}
CFRelease(formatter);
}
CFRelease(locale);
}
}
static CFArrayRef dictionaryGetSortedKeys(CFDictionaryRef dictionary) {
CFIndex count = CFDictionaryGetCount(dictionary);
const void** keys = malloc(sizeof(CFTypeRef) * count);
CFDictionaryGetKeysAndValues(dictionary, keys, NULL);
CFArrayRef keysArray = CFArrayCreate(NULL, keys, count, &kCFTypeArrayCallBacks);
CFMutableArrayRef sortedKeys = CFArrayCreateMutableCopy(NULL, count, keysArray);
CFRelease(keysArray);
free(keys);
CFArraySortValues(sortedKeys, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, 0);
return sortedKeys;
}
CFDataRef _SecureDownloadCreateTicketXML(CFPropertyListRef plist) {
CFMutableDataRef data = CFDataCreateMutable(NULL, 0);
if (!data) return NULL;
_appendCString(data, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
_appendCString(data, "<SecureDownload xmlns=\"http://www.apple.com/2006/SecureDownload/1\">\n");
_appendCString(data, " <resource>\n");
CFStringRef name = CFDictionaryGetValue(plist, SD_XML_NAME);
if (name) {
_appendCString(data, "\t<name>");
_appendCFString(data, name);
_appendCString(data, "</name>\n");
}
CFNumberRef num = CFDictionaryGetValue(plist, SD_XML_SIZE);
if (num) {
_appendCString(data, "\t<size>");
_appendCFNumber(data, num);
_appendCString(data, "</size>\n");
}
CFDateRef created = CFDictionaryGetValue(plist, SD_XML_CREATED);
if (created) {
_appendCString(data, "\t<created>");
_appendCFDate(data, created);
_appendCString(data, "</created>\n");
}
_appendCString(data, "\t<sites>\n");
CFArrayRef urls = CFDictionaryGetValue(plist, SD_XML_URL);
if (urls) {
CFIndex i, count = CFArrayGetCount(urls);
for (i = 0; i < count; ++i) {
_appendCString(data, "\t\t<url>");
_appendCFURL(data, CFArrayGetValueAtIndex(urls, i));
_appendCString(data, "</url>\n");
}
}
_appendCString(data, "\t</sites>\n");
CFDictionaryRef verifications = CFDictionaryGetValue(plist, SD_XML_VERIFICATIONS);
if (verifications) {
_appendCString(data, "\t<verifications>\n");
CFArrayRef algorithms = dictionaryGetSortedKeys(verifications);
if (algorithms) {
CFIndex i, count = CFArrayGetCount(algorithms);
for (i = 0; i < count; ++i) {
CFStringRef algorithm = CFArrayGetValueAtIndex(algorithms, i);
if (algorithm) {
_appendCString(data, "\t\t<verification algorithm=\"");
_appendCFString(data, algorithm);
_appendCString(data, "\">\n");
CFDictionaryRef dict = CFDictionaryGetValue(verifications, algorithm);
if (dict) {
CFDataRef digest = CFDictionaryGetValue(dict, SD_XML_DIGEST);
if (digest) {
_appendCString(data, "\t\t\t<digest>");
_appendCFData(data, digest);
_appendCString(data, "</digest>\n");
}
CFNumberRef sector_size = CFDictionaryGetValue(dict, SD_XML_SECTOR_SIZE);
if (sector_size) {
_appendCString(data, "\t\t\t<sector_size>");
_appendCFNumber(data, sector_size);
_appendCString(data, "</sector_size>\n");
}
CFDataRef digests = CFDictionaryGetValue(dict, SD_XML_DIGESTS);
if (digest) {
_appendCString(data, "\t\t\t<digests>");
_appendCFData(data, digests);
_appendCString(data, "</digests>\n");
}
}
_appendCString(data, "\t\t</verification>\n");
}
}
CFRelease(algorithms);
}
_appendCString(data, "\t</verifications>\n");
}
_appendCString(data, " </resource>\n");
_appendCString(data, "</SecureDownload>\n");
return data;
}
#if LOCAL_DEBUG
#include <unistd.h>
int main(int argc, char* argv[]) {
CFDataRef data = read_data("/Users/kevin/Desktop/SecureDownloadXML/SecureDownload.xml");
if (data) {
CFPropertyListRef plist = _SecureDownloadParseTicketXML(data);
CFShow(plist);
if (plist) {
CFDataRef output = _SecureDownloadCreateTicketXML(plist);
if (output) {
write(STDOUT_FILENO, CFDataGetBytePtr(output), CFDataGetLength(output));
CFRelease(output);
}
CFRelease(plist);
}
CFRelease(data);
}
cfprintf(stderr, "pid = %d\n", getpid());
sleep(1000);
}
#endif