/* * Copyright (C) 2005 Christophe Fergeau * * * The code contained in this file is free software; you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this code; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * iTunes and iPod are trademarks of Apple * * This product is not supported/written/published by Apple! * */ #include #include #include #include #include #include #include #include #include "db-parse-context.h" #include "db-itunes-parser.h" #include "itdb_endianness.h" DBParseContext * db_parse_context_new (const unsigned char *buffer, off_t len, guint byte_order) { DBParseContext *result; result = g_new0 (DBParseContext, 1); if (result == NULL) { return NULL; } result->buffer = buffer; result->cur_pos = buffer; result->total_len = len; result->byte_order = byte_order; return result; } void db_parse_context_destroy (DBParseContext *ctx, gboolean unmap) { g_return_if_fail (ctx != NULL); if (unmap) { munmap ((void*)ctx->buffer, ctx->total_len); } g_free (ctx); } static void db_parse_context_set_header_len (DBParseContext *ctx, off_t len) { /* FIXME: this can probably happen in malformed itunesdb files, * don't g_assert on this, only output a warning */ g_assert ((ctx->cur_pos - ctx->buffer) <= len); g_assert (len <= ctx->total_len); ctx->header_len = len; } void db_parse_context_set_total_len (DBParseContext *ctx, off_t len) { /* FIXME: this can probably happen in malformed itunesdb files, * don't g_assert on this, only output a warning */ g_assert ((ctx->cur_pos - ctx->buffer) <= len); if (ctx->header_len != 0) { g_assert (len >= ctx->header_len); } ctx->total_len = len; } off_t db_parse_context_get_remaining_length (DBParseContext *ctx) { if (ctx->header_len != 0) { return ctx->header_len - (ctx->cur_pos - ctx->buffer); } else { return ctx->total_len - (ctx->cur_pos - ctx->buffer); } } DBParseContext * db_parse_context_get_sub_context (DBParseContext *ctx, off_t offset) { DBParseContext *sub_ctx; if (offset >= ctx->total_len) { return NULL; } sub_ctx = db_parse_context_new (&ctx->buffer[offset], ctx->total_len - offset, ctx->byte_order); sub_ctx->db = ctx->db; return sub_ctx; } DBParseContext * db_parse_context_get_next_child (DBParseContext *ctx) { if (ctx->header_len == 0) { return NULL; } if (ctx->header_len >= ctx->total_len) { return NULL; } return db_parse_context_get_sub_context (ctx, ctx->header_len); } void * db_parse_context_get_m_header_internal (DBParseContext *ctx, const char *id, off_t size) { MHeader *h; char *header_id; if (db_parse_context_get_remaining_length (ctx) < 8) { return NULL; } h = (MHeader *)ctx->cur_pos; header_id = g_strndup ((char *)h->header_id, 4); if (ctx->byte_order == G_BIG_ENDIAN) { g_strreverse (header_id); } if (strncmp (id, header_id, 4) != 0) { return NULL; } g_free (header_id); /* FIXME: this test sucks for compat: if a field is smaller than * expected, we probably should create a buffer of the appropriate * size inited to 0, copy the data that is available in it and use * that buffer in the rest of the code (maybe it's harmful to have * some fields at 0 in some headers though...) */ if (get_gint32 (h->header_len, ctx->byte_order) < size) { return NULL; } db_parse_context_set_header_len (ctx, get_gint32 (h->header_len, ctx->byte_order)); return h; } DBParseContext * db_parse_context_new_from_file (const char *filename, Itdb_DB *db) { int fd; struct stat stat_buf; int result; unsigned char *buffer; DBParseContext *ctx; Itdb_Device *device; buffer = NULL; ctx = NULL; device = db_get_device (db); g_return_val_if_fail (device, NULL); fd = open (filename, O_RDONLY); if (fd == -1) { g_print ("Failed to open %s: %s\n", filename, strerror (errno)); return NULL; } result = fstat (fd, &stat_buf); if (result == -1) { g_print ("Failed to read %s size: %s\n", filename, strerror (errno)); goto error; } if (!S_ISREG (stat_buf.st_mode)) { g_print ("%s is not a regular file\n", filename); goto error; } if (stat_buf.st_size > ITUNESDB_MAX_SIZE) { g_print ("%s is too big to be an buffer file\n", filename); goto error; } buffer = mmap (NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); if (buffer == MAP_FAILED) { g_print ("Error while mmap'ing %s: %s\n", filename, strerror (errno)); goto error; } if (device->byte_order == 0) itdb_device_autodetect_endianess (device); ctx = db_parse_context_new (buffer, stat_buf.st_size, device->byte_order); if (ctx == NULL) { munmap (buffer, stat_buf.st_size); } ctx->db = db; error: close (fd); return ctx; }