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