static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset) { uint64_t i, count; uint64_t size; uint64_t elementID; value_ptr element = NULL; value_ptr *array = NULL; value_ptr value = NULL; BOOL ok = YES; assert(bplist->data_bytes != NULL && offset < bplist->length); if ((count = bplist_get_a_size(bplist, &offset, "array")) == UINT64_MAX) return NULL; if (count > UINT64_MAX / bplist->object_ref_size - offset) { // Offset overflow. bplist_log("Bad binary plist: %s object overlaps end of container.\n", "array"); return NULL; } size = bplist->object_ref_size * count; if (size + offset > bplist->length) { bplist_log("Bad binary plist: %s object overlaps end of container.\n", "array"); return NULL; } // got count, the number of array elements value = value_create(); assert(value); if (count == 0) { // count must be size_t or smaller because max file size is 100MB value_set_array(value, array, (size_t) count); return value; } array = allocate(sizeof(value_ptr) * (size_t) count); for (i = 0; i != count; ++i) { bplist_log_verbose("[%u]\n", i); elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size, bplist->object_ref_size); element = extract_object(bplist, elementID); if (element != NULL) { array[i] = element; } else { ok = NO; break; } } if (ok) { // count is smaller than size_t max because of 100MB file limit value_set_array(value, array, (size_t) count); } return value; }
static value_ptr extract_date(bplist_info_ptr bplist, uint64_t offset) { value_ptr value; assert(bplist->data_bytes != NULL && offset < bplist->length); // Data has size code like int and real, but only 3 (meaning 8 bytes) is valid. if (bplist->data_bytes[offset] != kVALUE_FULLDATETAG) { bplist_log("Bad binary plist: invalid size for date object.\n"); return NULL; } if (offset + 1 + sizeof (double) > bplist->length) { bplist_log("Bad binary plist: %s object overlaps end of container.\n", "date"); return NULL; } // FIXME: what to do if faced with other sizes for double? assert (sizeof (double) == sizeof (uint64_t)); uint64_t date = read_sized_int(bplist, offset + 1, sizeof(double)); // Note that this handles byte swapping. value = value_create(); value_set_date(value, *(double *)&date); return value; }
static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset) { /* UIDs are used by Cocoa's key-value coder. When writing other plist formats, they are expanded to dictionaries of the form <dict><key>CF$UID</key><integer>value</integer></dict>, so we do the same here on reading. This results in plists identical to what running plutil -convert xml1 gives us. However, this is not the same result as [Core]Foundation's plist parser, which extracts them as un- introspectable CF objects. In fact, it even seems to convert the CF$UID dictionaries from XML plists on the fly. */ value_ptr value; uint64_t uid; if (!read_self_sized_int(bplist, offset, &uid, NULL)) { bplist_log("Bad binary plist: invalid UID object.\n"); return NULL; } // assert(NO); // original code suggests using a string for a key // but our dictionaries all use big ints for keys, so I don't know // what to do here // In practice, I believe this code is never executed by PortMidi. // I changed it to do something and not raise compiler warnings, but // not sure what the code should do. value = value_create(); value_set_uid(value, uid); // return [NSDictionary dictionaryWithObject: // [NSNumber numberWithUnsignedLongLong:value] // forKey:"CF$UID"]; return value; }
static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset) { uint64_t size; value_ptr value; assert(bplist->data_bytes != NULL && offset < bplist->length); if ((size = bplist_get_a_size(bplist, &offset, "data")) == UINT64_MAX) return NULL; value = value_create(); // cast is ok because we only allow files up to 100MB: value_set_data(value, bplist->data_bytes + (size_t) offset, (size_t) size); return value; }
static value_ptr extract_int(bplist_info_ptr bplist, uint64_t offset) { value_ptr value = value_create(); value->tag = kTAG_INT; if (!read_self_sized_int(bplist, offset, &value->uinteger, NULL)) { bplist_log("Bad binary plist: invalid integer object.\n"); } /* NOTE: originally, I sign-extended here. This was the wrong thing; it turns out that negative ints are always stored as 64-bit, and smaller ints are unsigned. */ return value; }
static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset) { uint64_t size; value_ptr value; assert(bplist->data_bytes != NULL && offset < bplist->length); if ((size = bplist_get_a_size(bplist, &offset, (char *)"unicode string")) == UINT64_MAX) return NULL; value = value_create(); // cast is ok because we only allow 100MB files value_set_unicode_string(value, bplist->data_bytes + (size_t) offset, (size_t) size); return value; }
static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset) { value_ptr value = value_create(); uint32_t size; assert(bplist->data_bytes != NULL && offset < bplist->length); size = 1 << (bplist->data_bytes[offset] & 0x0F); // FIXME: what to do if faced with other sizes for float/double? assert (sizeof (float) == sizeof (uint32_t) && sizeof (double) == sizeof (uint64_t)); if (offset + 1 + size > bplist->length) { bplist_log("Bad binary plist: %s object overlaps end of container.\n", "floating-point number"); free(value); return NULL; } if (size == sizeof (float)) { // cast is ok because we know size is 4 bytes uint32_t i = (uint32_t) read_sized_int(bplist, offset + 1, size); // Note that this handles byte swapping. value_set_real(value, *(float *)&i); return value; } else if (size == sizeof (double)) { uint64_t i = read_sized_int(bplist, offset + 1, size); // Note that this handles byte swapping. value_set_real(value, *(double *)&i); return value; } else { // Can't handle floats of other sizes. bplist_log("Bad binary plist: can't handle %u-byte float.\n", size); free(value); return NULL; } }
static value_ptr extract_simple(bplist_info_ptr bplist, uint64_t offset) { assert(bplist->data_bytes != NULL && offset < bplist->length); value_ptr value = value_create(); switch (bplist->data_bytes[offset]) { case kVALUE_NULL: value->tag = kVALUE_NULL; return value; case kVALUE_TRUE: value->tag = kVALUE_TRUE; return value; case kVALUE_FALSE: value->tag = kVALUE_FALSE; return value; } // Note: kVALUE_FILLER is treated as invalid, because it, er, is. bplist_log("Bad binary plist: invalid atom.\n"); free(value); return NULL; }
static value_ptr extract_dictionary(bplist_info_ptr bplist, uint64_t offset) { uint64_t i, count; uint64_t size; uint64_t elementID; value_ptr value = NULL; dict_ptr dict = NULL; BOOL ok = YES; assert(bplist->data_bytes != NULL && offset < bplist->length); if ((count = bplist_get_a_size(bplist, &offset, "array")) == UINT64_MAX) return NULL; if (count > UINT64_MAX / (bplist->object_ref_size * 2) - offset) { // Offset overflow. bplist_log("Bad binary plist: %s object overlaps end of container.\n", "dictionary"); return NULL; } size = bplist->object_ref_size * count * 2; if (size + offset > bplist->length) { bplist_log("Bad binary plist: %s object overlaps end of container.\n", "dictionary"); return NULL; } value = value_create(); if (count == 0) { value_set_dict(value, NULL); return value; } for (i = 0; i != count; ++i) { value_ptr key; value_ptr val; elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size, bplist->object_ref_size); key = extract_object(bplist, elementID); if (key != NULL) { bplist_log_verbose("key: %p\n", key); } else { ok = NO; break; } elementID = read_sized_int(bplist, offset + (i + count) * bplist->object_ref_size, bplist->object_ref_size); val = extract_object(bplist, elementID); if (val != NULL) { dict_insert(&dict, key, val); } else { ok = NO; break; } } if (ok) { value_set_dict(value, dict); } return value; }