1 /*
2 * Copyright (C) 2005 Christophe Fergeau
3 *
4 *
5 * The code contained in this file is free software; you can redistribute
6 * it and/or modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either version
8 * 2.1 of the License, or (at your option) any later version.
9 *
10 * This file is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this code; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * iTunes and iPod are trademarks of Apple
20 *
21 * This product is not supported/written/published by Apple!
22 *
23 */
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #include <glib.h>
38 #include <glib/gstdio.h>
39 #include "db-parse-context.h"
40 #include "db-itunes-parser.h"
41 #include "itdb_endianness.h"
42
43 #ifndef HAVE_G_MAPPED_FILE_UNREF
44 #define g_mapped_file_unref(f) g_mapped_file_free(f)
45 #endif
46
47 DBParseContext *
db_parse_context_new(const unsigned char * buffer,off_t len,guint byte_order)48 db_parse_context_new (const unsigned char *buffer, off_t len, guint byte_order)
49 {
50 DBParseContext *result;
51
52 result = g_new0 (DBParseContext, 1);
53 if (result == NULL) {
54 return NULL;
55 }
56
57 result->buffer = buffer;
58 result->cur_pos = buffer;
59 result->total_len = len;
60 result->byte_order = byte_order;
61
62 return result;
63 }
64
65 void
db_parse_context_destroy(DBParseContext * ctx)66 db_parse_context_destroy (DBParseContext *ctx)
67 {
68 g_return_if_fail (ctx != NULL);
69
70 if (ctx->mapped_file) {
71 g_mapped_file_unref(ctx->mapped_file);
72 }
73
74 g_free (ctx);
75 }
76
77 static void
db_parse_context_set_header_len(DBParseContext * ctx,off_t len)78 db_parse_context_set_header_len (DBParseContext *ctx, off_t len)
79 {
80 /* FIXME: this can probably happen in malformed itunesdb files,
81 * don't g_assert on this, only output a warning
82 */
83 g_assert ((ctx->cur_pos - ctx->buffer) <= len);
84 g_assert (len <= ctx->total_len);
85 ctx->header_len = len;
86 }
87
88 void
db_parse_context_set_total_len(DBParseContext * ctx,off_t len)89 db_parse_context_set_total_len (DBParseContext *ctx, off_t len)
90 {
91 /* FIXME: this can probably happen in malformed itunesdb files,
92 * don't g_assert on this, only output a warning
93 */
94 g_assert ((ctx->cur_pos - ctx->buffer) <= len);
95 if (ctx->header_len != 0) {
96 g_assert (len >= ctx->header_len);
97 }
98 ctx->total_len = len;
99 }
100
101
102 off_t
db_parse_context_get_remaining_length(DBParseContext * ctx)103 db_parse_context_get_remaining_length (DBParseContext *ctx)
104 {
105 if (ctx->header_len != 0) {
106 return ctx->header_len - (ctx->cur_pos - ctx->buffer);
107 } else {
108 return ctx->total_len - (ctx->cur_pos - ctx->buffer);
109 }
110 }
111
112 DBParseContext *
db_parse_context_get_sub_context(DBParseContext * ctx,off_t offset)113 db_parse_context_get_sub_context (DBParseContext *ctx, off_t offset)
114 {
115 DBParseContext *sub_ctx;
116
117 if (offset >= ctx->total_len) {
118 return NULL;
119 }
120 sub_ctx = db_parse_context_new (&ctx->buffer[offset],
121 ctx->total_len - offset,
122 ctx->byte_order);
123 sub_ctx->db = ctx->db;
124 sub_ctx->artwork = ctx->artwork;
125 return sub_ctx;
126 }
127
128
129 DBParseContext *
db_parse_context_get_next_child(DBParseContext * ctx)130 db_parse_context_get_next_child (DBParseContext *ctx)
131 {
132 if (ctx->header_len == 0) {
133 return NULL;
134 }
135 if (ctx->header_len >= ctx->total_len) {
136 return NULL;
137 }
138
139 return db_parse_context_get_sub_context (ctx, ctx->header_len);
140 }
141
142 void *
db_parse_context_get_m_header_internal(DBParseContext * ctx,const char * id,off_t size)143 db_parse_context_get_m_header_internal (DBParseContext *ctx, const char *id, off_t size)
144 {
145 MHeader *h;
146 char *header_id;
147
148 if (db_parse_context_get_remaining_length (ctx) < 8) {
149 return NULL;
150 }
151
152 h = (MHeader *)ctx->cur_pos;
153 header_id = g_strndup ((char *)h->header_id, 4);
154 if (ctx->byte_order == G_BIG_ENDIAN) {
155 g_strreverse (header_id);
156 }
157 if (strncmp (id, header_id, 4) != 0) {
158 g_free (header_id);
159 return NULL;
160 }
161
162 g_free (header_id);
163
164 /* FIXME: this test sucks for compat: if a field is smaller than
165 * expected, we probably should create a buffer of the appropriate
166 * size inited to 0, copy the data that is available in it and use
167 * that buffer in the rest of the code (maybe it's harmful to have
168 * some fields at 0 in some headers though...)
169 */
170 if (get_gint32 (h->header_len, ctx->byte_order) < size) {
171 return NULL;
172 }
173
174 db_parse_context_set_header_len (ctx, get_gint32 (h->header_len,
175 ctx->byte_order));
176
177 return h;
178 }
179
180 DBParseContext *
db_parse_context_new_from_file(const char * filename,Itdb_DB * db)181 db_parse_context_new_from_file (const char *filename, Itdb_DB *db)
182 {
183 DBParseContext *ctx;
184 Itdb_Device *device;
185 GError* error;
186 GMappedFile* mapped_file;
187 struct stat stat_buf;
188
189 ctx = NULL;
190 error = NULL;
191 mapped_file = NULL;
192
193 device = db_get_device (db);
194 g_return_val_if_fail (device, NULL);
195
196 if (g_stat (filename, &stat_buf) != 0) {
197 return NULL;
198 };
199 if (stat_buf.st_size > 64 * 1024 * 1024) {
200 g_warning ("%s is too big to be mmapped (%llu bytes)\n",
201 filename, (unsigned long long)stat_buf.st_size);
202 return NULL;
203 }
204
205 mapped_file = g_mapped_file_new(filename, FALSE, &error);
206
207 if (mapped_file == NULL) {
208 g_print ("Error while mapping %s: %s\n", filename,
209 error->message);
210 g_error_free(error);
211 return NULL;
212 }
213
214 if (device->byte_order == 0)
215 itdb_device_autodetect_endianess (device);
216
217 ctx = db_parse_context_new ((guchar *)g_mapped_file_get_contents(mapped_file),
218 g_mapped_file_get_length(mapped_file),
219 device->byte_order);
220
221 if (ctx == NULL) {
222 g_mapped_file_unref(mapped_file);
223 return NULL;
224 }
225 ctx->db = db;
226 ctx->mapped_file = mapped_file;
227
228 return ctx;
229 }
230