1/*  libasf - An Advanced Systems Format media file parser
2 *  Copyright (C) 2006-2010 Juho Vähä-Herttua
3 *
4 *  This library is free software; you can redistribute it and/or
5 *  modify it under the terms of the GNU Lesser General Public
6 *  License as published by the Free Software Foundation; either
7 *  version 2.1 of the License, or (at your option) any later version.
8 *
9 *  This library is distributed in the hope that it will be useful,
10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 *  Lesser General Public License for more details.
13 *
14 *  You should have received a copy of the GNU Lesser General Public
15 *  License along with this library; if not, write to the Free Software
16 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <stdlib.h>
20#include <string.h>
21
22#include "asf.h"
23#include "asfint.h"
24#include "byteio.h"
25#include "header.h"
26#include "guid.h"
27#include "debug.h"
28
29/**
30 * Read next object from buffer pointed by data. Notice that
31 * no buffer overflow checks are done! This function always
32 * expects to have 24 bytes available, which is the size of
33 * the object header (GUID + data size)
34 */
35static void
36asf_parse_read_object(asfint_object_t *obj, uint8_t *data)
37{
38	GetGUID(data, &obj->guid);
39	obj->type = asf_guid_get_type(&obj->guid);
40	obj->size = GetQWLE(data + 16);
41	obj->full_data = data;
42	obj->datalen = 0;
43	obj->data = NULL;
44	obj->next = NULL;
45
46	if (obj->type == GUID_UNKNOWN) {
47		debug_printf("unknown object: %x-%x-%x-%02x%02x%02x%02x%02x%02x%02x%02x, %ld bytes",
48		             obj->guid.v1, obj->guid.v2, obj->guid.v3, obj->guid.v4[0],
49		             obj->guid.v4[1], obj->guid.v4[2], obj->guid.v4[3], obj->guid.v4[4],
50		             obj->guid.v4[5], obj->guid.v4[6], obj->guid.v4[7], (long) obj->size);
51	}
52}
53
54/**
55 * Parse header extension object. Takes a pointer to a newly allocated
56 * header extension structure, a pointer to the data buffer and the
57 * length of the data buffer as its parameters. Subobject contents are
58 * not parsed, but they are added as a linked list to the header object.
59 */
60static int
61asf_parse_headerext(asf_object_headerext_t *header, uint8_t *buf, uint64_t buflen)
62{
63	int64_t datalen;
64	uint8_t *data;
65
66	if (header->size < 46) {
67		/* invalide size for headerext */
68		return ASF_ERROR_INVALID_OBJECT_SIZE;
69	}
70
71	/* Read reserved and datalen fields from the buffer */
72	GetGUID(buf + 24, &header->reserved1);
73	header->reserved2 = GetWLE(buf + 40);
74	header->datalen = GetDWLE(buf + 42);
75
76	if (header->datalen != header->size - 46) {
77		/* invalid header extension data length value */
78		return ASF_ERROR_INVALID_LENGTH;
79	}
80	header->data = buf + 46;
81
82	debug_printf("parsing header extension subobjects");
83
84	datalen = header->datalen;
85	data = header->data;
86	while (datalen > 0) {
87		asfint_object_t *current;
88
89		if (datalen < 24) {
90			/* not enough data for reading a new object */
91			break;
92		}
93
94		/* Allocate a new subobject */
95		current = malloc(sizeof(asfint_object_t));
96		if (!current) {
97			return ASF_ERROR_OUTOFMEM;
98		}
99
100		asf_parse_read_object(current, data);
101		if (current->size > datalen || current->size < 24) {
102			/* invalid object size */
103			break;
104		}
105		current->datalen = current->size - 24;
106		current->data = data + 24;
107
108		/* add to the list of subobjects */
109		if (!header->first) {
110			header->first = current;
111			header->last = current;
112		} else {
113			header->last->next = current;
114			header->last = current;
115		}
116
117		data += current->size;
118		datalen -= current->size;
119	}
120
121	if (datalen != 0) {
122		/* not enough data for reading the whole object */
123		return ASF_ERROR_INVALID_LENGTH;
124	}
125
126	debug_printf("header extension subobjects parsed successfully");
127
128	return header->size;
129}
130
131/**
132 * Takes an initialized asf_file_t structure file as a parameter. Allocates
133 * a new asf_object_header_t in file->header and uses the file->iostream to
134 * read all fields and subobjects into it. Finally calls the
135 * asf_parse_header_validate function to validate the values and parse the
136 * commonly used values into the asf_file_t struct itself.
137 */
138int
139asf_parse_header(asf_file_t *file)
140{
141	asf_object_header_t *header;
142	asf_iostream_t *iostream;
143	uint8_t hdata[30];
144	int tmp;
145
146	file->header = NULL;
147	iostream = &file->iostream;
148
149	/* object minimum is 24 bytes and header needs to have
150	 * the subobject count field and two reserved fields */
151	tmp = asf_byteio_read(iostream, hdata, 30);
152	if (tmp < 0) {
153		/* not enough data to read the header object */
154		return tmp;
155	}
156
157	file->header = malloc(sizeof(asf_object_header_t));
158	header = file->header;
159	if (!header) {
160		return ASF_ERROR_OUTOFMEM;
161	}
162
163	/* read the object and check its size value */
164	asf_parse_read_object((asfint_object_t *) header, hdata);
165	if (header->size < 30) {
166		/* invalid size for header object */
167		return ASF_ERROR_INVALID_OBJECT_SIZE;
168	}
169
170	/* read header object specific compulsory fields */
171	header->subobjects = GetDWLE(hdata + 24);
172	header->reserved1 = hdata[28];
173	header->reserved2 = hdata[29];
174
175	/* clear header extension object and subobject list */
176	header->ext = NULL;
177	header->first = NULL;
178	header->last = NULL;
179
180	/* the header data needs to be allocated for reading */
181	header->datalen = header->size - 30;
182	header->data = malloc(header->datalen * sizeof(uint8_t));
183	if (!header->data) {
184		return ASF_ERROR_OUTOFMEM;
185	}
186
187	tmp = asf_byteio_read(iostream, header->data, header->datalen);
188	if (tmp < 0) {
189		return tmp;
190	}
191
192	if (header->subobjects > 0) {
193		uint64_t datalen;
194		uint8_t *data;
195		int i;
196
197		debug_printf("starting to read subobjects");
198
199		/* use temporary variables for use during the read */
200		datalen = header->datalen;
201		data = header->data;
202		for (i=0; i<header->subobjects; i++) {
203			asfint_object_t *current;
204
205			if (datalen < 24) {
206				/* not enough data for reading object */
207				break;
208			}
209
210			current = malloc(sizeof(asfint_object_t));
211			if (!current) {
212				return ASF_ERROR_OUTOFMEM;
213			}
214
215			asf_parse_read_object(current, data);
216			if (current->size > datalen || current->size < 24) {
217				/* invalid object size */
218				break;
219			}
220
221			/* Check if the current subobject is a header extension
222			 * object or just a normal subobject */
223			if (current->type == GUID_HEADER_EXTENSION && !header->ext) {
224				int ret;
225				asf_object_headerext_t *headerext;
226
227				/* we handle header extension separately because it has
228				 * some subobjects as well */
229				current = realloc(current, sizeof(asf_object_headerext_t));
230				headerext = (asf_object_headerext_t *) current;
231				headerext->first = NULL;
232				headerext->last = NULL;
233				ret = asf_parse_headerext(headerext, data, datalen);
234
235				if (ret < 0) {
236					/* error parsing header extension */
237					return ret;
238				}
239
240				header->ext = headerext;
241			} else {
242				if (current->type == GUID_HEADER_EXTENSION) {
243					debug_printf("WARNING! Second header extension object found, ignoring it!");
244				}
245
246				current->datalen = current->size - 24;
247				current->data = data + 24;
248
249				/* add to list of subobjects */
250				if (!header->first) {
251					header->first = current;
252					header->last = current;
253				} else {
254					header->last->next = current;
255					header->last = current;
256				}
257			}
258
259			data += current->size;
260			datalen -= current->size;
261		}
262
263		if (i != header->subobjects || datalen != 0) {
264			/* header data size doesn't match given subobject count */
265			return ASF_ERROR_INVALID_VALUE;
266		}
267
268		debug_printf("%d subobjects read successfully", i);
269	}
270
271	tmp = asf_parse_header_validate(file, file->header);
272	if (tmp < 0) {
273		/* header read ok but doesn't validate correctly */
274		return tmp;
275	}
276
277	debug_printf("header validated correctly");
278
279	return header->size;
280}
281
282/**
283 * Takes an initialized asf_file_t structure file as a parameter. Allocates
284 * a new asf_object_data_t in file->data and uses the file->iostream to
285 * read all its compulsory fields into it. Notice that the actual data is
286 * not read in any way, because we need to be able to work with non-seekable
287 * streams as well.
288 */
289int
290asf_parse_data(asf_file_t *file)
291{
292	asf_object_data_t *data;
293	asf_iostream_t *iostream;
294	uint8_t ddata[50];
295	int tmp;
296
297	file->data = NULL;
298	iostream = &file->iostream;
299
300	/* object minimum is 24 bytes and data object needs to have
301	 * 26 additional bytes for its internal fields */
302	tmp = asf_byteio_read(iostream, ddata, 50);
303	if (tmp < 0) {
304		return tmp;
305	}
306
307	file->data = malloc(sizeof(asf_object_data_t));
308	data = file->data;
309	if (!data) {
310		return ASF_ERROR_OUTOFMEM;
311	}
312
313	/* read the object and check its size value */
314	asf_parse_read_object((asfint_object_t *) data, ddata);
315	if (data->size < 50) {
316		/* invalid size for data object */
317		return ASF_ERROR_INVALID_OBJECT_SIZE;
318	}
319
320	/* read data object specific compulsory fields */
321	GetGUID(ddata + 24, &data->file_id);
322	data->total_data_packets = GetQWLE(ddata + 40);
323	data->reserved = GetWLE(ddata + 48);
324	data->packets_position = file->position + 50;
325
326	/* If the file_id GUID in data object doesn't match the
327	 * file_id GUID in headers, the file is corrupted */
328	if (!asf_guid_equals(&data->file_id, &file->file_id)) {
329		return ASF_ERROR_INVALID_VALUE;
330	}
331
332	/* if data->total_data_packets is non-zero (not a stream) and
333	   the data packets count doesn't match, return error */
334	if (data->total_data_packets &&
335	    data->total_data_packets != file->data_packets_count) {
336		return ASF_ERROR_INVALID_VALUE;
337	}
338
339	return 50;
340}
341
342/**
343 * Takes an initialized asf_file_t structure file as a parameter. Allocates
344 * a new asf_object_index_t in file->index and uses the file->iostream to
345 * read all its compulsory fields into it. Notice that the actual data is
346 * not read in any way, because we need to be able to work with non-seekable
347 * streams as well.
348 */
349int
350asf_parse_index(asf_file_t *file)
351{
352	asf_object_index_t *index;
353	asf_iostream_t *iostream;
354	uint8_t idata[56];
355	uint64_t entry_data_size;
356	uint8_t *entry_data = NULL;
357	int tmp, i;
358
359	file->index = NULL;
360	iostream = &file->iostream;
361
362	/* read the raw data of an index header */
363	tmp = asf_byteio_read(iostream, idata, 56);
364	if (tmp < 0) {
365		printf("Could not read index header\n");
366		return tmp;
367	}
368
369	/* allocate the index object */
370	index = malloc(sizeof(asf_object_index_t));
371	if (!index) {
372		return ASF_ERROR_OUTOFMEM;
373	}
374
375	asf_parse_read_object((asfint_object_t *) index, idata);
376	if (index->type != GUID_INDEX) {
377		tmp = index->size;
378		free(index);
379
380		/* The guid type was wrong, just return the bytes to skip */
381		return tmp;
382	}
383
384	if (index->size < 56) {
385		/* invalid size for index object */
386		free(index);
387		return ASF_ERROR_INVALID_OBJECT_SIZE;
388	}
389
390	GetGUID(idata + 24, &index->file_id);
391	index->entry_time_interval = GetQWLE(idata + 40);
392	index->max_packet_count = GetDWLE(idata + 48);
393	index->entry_count = GetDWLE(idata + 52);
394
395	printf("INDEX\n");
396	printf("Total Index Entries %d\n",index->entry_count);
397	printf("Index Size in bytes %Ld\n",index->size);
398	printf("Index Max Packet Count %d\n",index->max_packet_count);
399	printf("Index Entry Time Interval %Ld\n",index->entry_time_interval);
400
401	if (index->entry_count == 0) {
402		printf("Index has no entries\n");
403		file->index = index;
404		return index->size;
405	}
406
407	if (index->entry_count * 6 + 56 > index->size) {
408		free(index);
409		return ASF_ERROR_INVALID_LENGTH;
410	}
411
412	entry_data_size = index->entry_count * 6;
413	entry_data = malloc(entry_data_size * sizeof(uint8_t));
414	if (!entry_data) {
415		free(index);
416		return ASF_ERROR_OUTOFMEM;
417	}
418	tmp = asf_byteio_read(iostream, entry_data, entry_data_size);
419	if (tmp < 0) {
420		printf("Could not read entry data\n");
421		free(index);
422		free(entry_data);
423		return tmp;
424	}
425
426	index->entries = malloc(index->entry_count * sizeof(asf_index_entry_t));
427	if (!index->entries) {
428		free(index);
429		free(entry_data);
430		return ASF_ERROR_OUTOFMEM;
431	}
432
433	for (i=0; i<index->entry_count; i++) {
434		index->entries[i].packet_index = GetDWLE(entry_data + i*6);
435		index->entries[i].packet_count = GetWLE(entry_data + i*6 + 4);
436	}
437
438	free(entry_data);
439	file->index = index;
440
441	return index->size;
442}
443