00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <glib.h>
00019 #include <glib/gprintf.h>
00020 #include <string.h>
00021 #include <stdlib.h>
00022
00023 #include "xmms/xmms_log.h"
00024 #include "xmmspriv/xmms_xform.h"
00025
00026 static GList *magic_list, *ext_list;
00027
00028 #define SWAP16(v, endian) \
00029 if (endian == G_LITTLE_ENDIAN) { \
00030 v = GUINT16_TO_LE (v); \
00031 } else if (endian == G_BIG_ENDIAN) { \
00032 v = GUINT16_TO_BE (v); \
00033 }
00034
00035 #define SWAP32(v, endian) \
00036 if (endian == G_LITTLE_ENDIAN) { \
00037 v = GUINT32_TO_LE (v); \
00038 } else if (endian == G_BIG_ENDIAN) { \
00039 v = GUINT32_TO_BE (v); \
00040 }
00041
00042 #define CMP(v1, entry, v2) \
00043 if (entry->pre_test_and_op) { \
00044 v1 &= entry->pre_test_and_op; \
00045 } \
00046 \
00047 switch (entry->oper) { \
00048 case XMMS_MAGIC_ENTRY_OPERATOR_EQUAL: \
00049 return v1 == v2; \
00050 case XMMS_MAGIC_ENTRY_OPERATOR_LESS_THAN: \
00051 return v1 < v2; \
00052 case XMMS_MAGIC_ENTRY_OPERATOR_GREATER_THAN: \
00053 return v1 > v2; \
00054 case XMMS_MAGIC_ENTRY_OPERATOR_AND: \
00055 return (v1 & v2) == v2; \
00056 case XMMS_MAGIC_ENTRY_OPERATOR_NAND: \
00057 return (v1 & v2) != v2; \
00058 } \
00059
00060 typedef enum xmms_magic_entry_type_St {
00061 XMMS_MAGIC_ENTRY_TYPE_UNKNOWN = 0,
00062 XMMS_MAGIC_ENTRY_TYPE_BYTE,
00063 XMMS_MAGIC_ENTRY_TYPE_INT16,
00064 XMMS_MAGIC_ENTRY_TYPE_INT32,
00065 XMMS_MAGIC_ENTRY_TYPE_STRING,
00066 XMMS_MAGIC_ENTRY_TYPE_STRINGC,
00067 } xmms_magic_entry_type_t;
00068
00069 typedef enum xmms_magic_entry_operator_St {
00070 XMMS_MAGIC_ENTRY_OPERATOR_EQUAL = 0,
00071 XMMS_MAGIC_ENTRY_OPERATOR_LESS_THAN,
00072 XMMS_MAGIC_ENTRY_OPERATOR_GREATER_THAN,
00073 XMMS_MAGIC_ENTRY_OPERATOR_AND,
00074 XMMS_MAGIC_ENTRY_OPERATOR_NAND
00075 } xmms_magic_entry_operator_t;
00076
00077 typedef struct xmms_magic_entry_St {
00078 guint offset;
00079 xmms_magic_entry_type_t type;
00080 gint endian;
00081 guint len;
00082 guint pre_test_and_op;
00083 xmms_magic_entry_operator_t oper;
00084
00085 union {
00086 guint8 i8;
00087 guint16 i16;
00088 guint32 i32;
00089 gchar s[32];
00090 } value;
00091 } xmms_magic_entry_t;
00092
00093 typedef struct xmms_magic_checker_St {
00094 xmms_xform_t *xform;
00095 gchar *buf;
00096 guint alloc;
00097 guint read;
00098 guint offset;
00099 gint dumpcount;
00100 } xmms_magic_checker_t;
00101
00102 typedef struct xmms_magic_ext_data_St {
00103 gchar *type;
00104 gchar *pattern;
00105 } xmms_magic_ext_data_t;
00106
00107 static void xmms_magic_tree_free (GNode *tree);
00108
00109 static gchar *xmms_magic_match (xmms_magic_checker_t *c, const gchar *u);
00110 static guint xmms_magic_complexity (GNode *tree);
00111
00112 static void
00113 xmms_magic_entry_free (xmms_magic_entry_t *e)
00114 {
00115 g_free (e);
00116 }
00117
00118 static xmms_magic_entry_type_t
00119 parse_type (gchar **s, gint *endian)
00120 {
00121 struct {
00122 const gchar *string;
00123 xmms_magic_entry_type_t type;
00124 gint endian;
00125 } *t, types[] = {
00126 {"byte", XMMS_MAGIC_ENTRY_TYPE_BYTE, G_BYTE_ORDER},
00127 {"short", XMMS_MAGIC_ENTRY_TYPE_INT16, G_BYTE_ORDER},
00128 {"long", XMMS_MAGIC_ENTRY_TYPE_INT16, G_BYTE_ORDER},
00129 {"beshort", XMMS_MAGIC_ENTRY_TYPE_INT16, G_BIG_ENDIAN},
00130 {"belong", XMMS_MAGIC_ENTRY_TYPE_INT32, G_BIG_ENDIAN},
00131 {"leshort", XMMS_MAGIC_ENTRY_TYPE_INT16, G_LITTLE_ENDIAN},
00132 {"lelong", XMMS_MAGIC_ENTRY_TYPE_INT32, G_LITTLE_ENDIAN},
00133 {"string/c", XMMS_MAGIC_ENTRY_TYPE_STRINGC, G_BYTE_ORDER},
00134 {"string", XMMS_MAGIC_ENTRY_TYPE_STRING, G_BYTE_ORDER},
00135 {NULL, XMMS_MAGIC_ENTRY_TYPE_UNKNOWN, G_BYTE_ORDER}
00136 };
00137
00138 for (t = types; t; t++) {
00139 int l = t->string ? strlen (t->string) : 0;
00140
00141 if (!l || !strncmp (*s, t->string, l)) {
00142 *s += l;
00143 *endian = t->endian;
00144
00145 return t->type;
00146 }
00147 }
00148
00149 g_assert_not_reached ();
00150 }
00151
00152
00153 static xmms_magic_entry_operator_t
00154 parse_oper (gchar **s)
00155 {
00156 gchar c = **s;
00157 struct {
00158 gchar c;
00159 xmms_magic_entry_operator_t o;
00160 } *o, opers[] = {
00161 {'=', XMMS_MAGIC_ENTRY_OPERATOR_EQUAL},
00162 {'<', XMMS_MAGIC_ENTRY_OPERATOR_LESS_THAN},
00163 {'>', XMMS_MAGIC_ENTRY_OPERATOR_GREATER_THAN},
00164 {'&', XMMS_MAGIC_ENTRY_OPERATOR_AND},
00165 {'^', XMMS_MAGIC_ENTRY_OPERATOR_NAND},
00166 {'\0', XMMS_MAGIC_ENTRY_OPERATOR_EQUAL}
00167 };
00168
00169 for (o = opers; o; o++) {
00170 if (!o->c) {
00171
00172 return o->o;
00173 } else if (c == o->c) {
00174 (*s)++;
00175 return o->o;
00176 }
00177 }
00178
00179 g_assert_not_reached ();
00180 }
00181
00182 static gboolean
00183 parse_pre_test_and_op (xmms_magic_entry_t *entry, gchar **end)
00184 {
00185 gboolean ret = FALSE;
00186
00187 if (**end == ' ') {
00188 (*end)++;
00189 return TRUE;
00190 }
00191
00192 switch (entry->type) {
00193 case XMMS_MAGIC_ENTRY_TYPE_BYTE:
00194 case XMMS_MAGIC_ENTRY_TYPE_INT16:
00195 case XMMS_MAGIC_ENTRY_TYPE_INT32:
00196 if (**end == '&') {
00197 (*end)++;
00198 entry->pre_test_and_op = strtoul (*end, end, 0);
00199 ret = TRUE;
00200 }
00201 default:
00202 break;
00203 }
00204
00205 return ret;
00206 }
00207
00208 static xmms_magic_entry_t *
00209 parse_entry (const gchar *s)
00210 {
00211 xmms_magic_entry_t *entry;
00212 gchar *end = NULL;
00213
00214 entry = g_new0 (xmms_magic_entry_t, 1);
00215 entry->endian = G_BYTE_ORDER;
00216 entry->oper = XMMS_MAGIC_ENTRY_OPERATOR_EQUAL;
00217 entry->offset = strtoul (s, &end, 0);
00218
00219 end++;
00220
00221 entry->type = parse_type (&end, &entry->endian);
00222 if (entry->type == XMMS_MAGIC_ENTRY_TYPE_UNKNOWN) {
00223 g_free (entry);
00224 return NULL;
00225 }
00226
00227 if (!parse_pre_test_and_op (entry, &end)) {
00228 g_free (entry);
00229 return NULL;
00230 }
00231
00232
00233 switch (entry->type) {
00234 case XMMS_MAGIC_ENTRY_TYPE_STRING:
00235 case XMMS_MAGIC_ENTRY_TYPE_STRINGC:
00236 break;
00237 default:
00238 entry->oper = parse_oper (&end);
00239 break;
00240 }
00241
00242 switch (entry->type) {
00243 case XMMS_MAGIC_ENTRY_TYPE_BYTE:
00244 entry->value.i8 = strtoul (end, &end, 0);
00245 entry->len = 1;
00246 break;
00247 case XMMS_MAGIC_ENTRY_TYPE_INT16:
00248 entry->value.i16 = strtoul (end, &end, 0);
00249 entry->len = 2;
00250 break;
00251 case XMMS_MAGIC_ENTRY_TYPE_INT32:
00252 entry->value.i32 = strtoul (end, &end, 0);
00253 entry->len = 4;
00254 break;
00255 case XMMS_MAGIC_ENTRY_TYPE_STRING:
00256 case XMMS_MAGIC_ENTRY_TYPE_STRINGC:
00257 g_strlcpy (entry->value.s, end, sizeof (entry->value.s));
00258 entry->len = strlen (entry->value.s);
00259 break;
00260 default:
00261 break;
00262 }
00263
00264 return entry;
00265 }
00266
00267 static gboolean
00268 free_node (GNode *node, xmms_magic_entry_t *entry)
00269 {
00270 if (G_NODE_IS_ROOT (node)) {
00271 gpointer *data = node->data;
00272
00273
00274 g_free (data[0]);
00275 g_free (data[1]);
00276 g_free (data);
00277 } else {
00278 xmms_magic_entry_free (entry);
00279 }
00280
00281 return FALSE;
00282 }
00283
00284 static void
00285 xmms_magic_tree_free (GNode *tree)
00286 {
00287 g_node_traverse (tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
00288 (GNodeTraverseFunc) free_node, NULL);
00289 }
00290
00291 static GNode *
00292 xmms_magic_add_node (GNode *tree, const gchar *s, GNode *prev_node)
00293 {
00294 xmms_magic_entry_t *entry;
00295 gpointer *data = tree->data;
00296 guint indent = 0, prev_indent;
00297
00298 g_assert (s);
00299
00300 XMMS_DBG ("adding magic spec to tree '%s'", (gchar *) data[0]);
00301
00302
00303 while (*s == '>') {
00304 indent++;
00305 s++;
00306 }
00307
00308 entry = parse_entry (s);
00309 if (!entry) {
00310 XMMS_DBG ("cannot parse magic entry");
00311 return NULL;
00312 }
00313
00314 if (!indent) {
00315 return g_node_append_data (tree, entry);
00316 }
00317
00318 if (!prev_node) {
00319 XMMS_DBG ("invalid indent level");
00320 xmms_magic_entry_free (entry);
00321 return NULL;
00322 }
00323
00324 prev_indent = g_node_depth (prev_node) - 2;
00325
00326 if (indent > prev_indent) {
00327
00328 if (indent != prev_indent + 1) {
00329 XMMS_DBG ("invalid indent level");
00330 xmms_magic_entry_free (entry);
00331 return NULL;
00332 }
00333
00334 return g_node_append_data (prev_node, entry);
00335 } else {
00336 while (indent < prev_indent) {
00337 prev_indent--;
00338 prev_node = prev_node->parent;
00339 }
00340
00341 return g_node_insert_after (prev_node->parent, prev_node,
00342 g_node_new (entry));
00343 }
00344 }
00345
00346 static gint
00347 read_data (xmms_magic_checker_t *c, guint needed)
00348 {
00349 xmms_error_t e;
00350
00351 if (needed > c->alloc) {
00352 c->alloc = needed;
00353 c->buf = g_realloc (c->buf, c->alloc);
00354 }
00355
00356 xmms_error_reset (&e);
00357
00358 return xmms_xform_peek (c->xform, c->buf, needed, &e);
00359 }
00360
00361 static gboolean
00362 node_match (xmms_magic_checker_t *c, GNode *node)
00363 {
00364 xmms_magic_entry_t *entry = node->data;
00365 guint needed = c->offset + entry->offset + entry->len;
00366 guint8 i8;
00367 guint16 i16;
00368 guint32 i32;
00369 gint tmp;
00370 gchar *ptr;
00371
00372
00373
00374
00375 if (c->read < needed) {
00376 tmp = read_data (c, needed);
00377 if (tmp == -1) {
00378 return FALSE;
00379 }
00380
00381 c->read = tmp;
00382 if (c->read < needed) {
00383
00384 return FALSE;
00385 }
00386 }
00387
00388 ptr = &c->buf[c->offset + entry->offset];
00389
00390 switch (entry->type) {
00391 case XMMS_MAGIC_ENTRY_TYPE_BYTE:
00392 memcpy (&i8, ptr, sizeof (i8));
00393 CMP (i8, entry, entry->value.i8);
00394 case XMMS_MAGIC_ENTRY_TYPE_INT16:
00395 memcpy (&i16, ptr, sizeof (i16));
00396 SWAP16 (i16, entry->endian);
00397 CMP (i16, entry, entry->value.i16);
00398 case XMMS_MAGIC_ENTRY_TYPE_INT32:
00399 memcpy (&i32, ptr, sizeof (i32));
00400 SWAP32 (i32, entry->endian);
00401 CMP (i32, entry, entry->value.i32);
00402 case XMMS_MAGIC_ENTRY_TYPE_STRING:
00403 return !strncmp (ptr, entry->value.s, entry->len);
00404 case XMMS_MAGIC_ENTRY_TYPE_STRINGC:
00405 return !g_ascii_strncasecmp (ptr, entry->value.s, entry->len);
00406 default:
00407 return FALSE;
00408 }
00409 }
00410
00411 static gboolean
00412 tree_match (xmms_magic_checker_t *c, GNode *tree)
00413 {
00414 GNode *n;
00415
00416
00417 if (!tree->children) {
00418 return TRUE;
00419 }
00420
00421 for (n = tree->children; n; n = n->next) {
00422 if (node_match (c, n) && tree_match (c, n)) {
00423 return TRUE;
00424 }
00425 }
00426
00427 return FALSE;
00428 }
00429
00430 static gchar *
00431 xmms_magic_match (xmms_magic_checker_t *c, const gchar *uri)
00432 {
00433 const GList *l;
00434 gchar *u, *dump;
00435 int i;
00436
00437 g_return_val_if_fail (c, NULL);
00438
00439
00440 for (l = magic_list; l; l = g_list_next (l)) {
00441 GNode *tree = l->data;
00442
00443 if (tree_match (c, tree)) {
00444 gpointer *data = tree->data;
00445 XMMS_DBG ("magic plugin detected '%s' (%s)",
00446 (char *)data[1], (char *)data[0]);
00447 return (char *) (data[1]);
00448 }
00449 }
00450
00451 if (!uri)
00452 return NULL;
00453
00454 u = g_ascii_strdown (uri, -1);
00455 for (l = ext_list; l; l = g_list_next (l)) {
00456 xmms_magic_ext_data_t *e = l->data;
00457 if (g_pattern_match_simple (e->pattern, u)) {
00458 XMMS_DBG ("magic plugin detected '%s' (by extension '%s')", e->type, e->pattern);
00459 g_free (u);
00460 return e->type;
00461 }
00462 }
00463 g_free (u);
00464
00465 if (c->dumpcount > 0) {
00466 dump = g_malloc ((MIN (c->read, c->dumpcount) * 3) + 1);
00467 u = dump;
00468
00469 XMMS_DBG ("Magic didn't match anything...");
00470 for (i = 0; i < c->dumpcount && i < c->read; i++) {
00471 g_sprintf (u, "%02X ", (unsigned char)c->buf[i]);
00472 u += 3;
00473 }
00474 XMMS_DBG ("%s", dump);
00475
00476 g_free (dump);
00477 }
00478
00479 return NULL;
00480 }
00481
00482 static guint
00483 xmms_magic_complexity (GNode *tree)
00484 {
00485 return g_node_n_nodes (tree, G_TRAVERSE_ALL);
00486 }
00487
00488 static gint
00489 cb_sort_magic_list (GNode *a, GNode *b)
00490 {
00491 guint n1, n2;
00492
00493 n1 = xmms_magic_complexity (a);
00494 n2 = xmms_magic_complexity (b);
00495
00496 if (n1 > n2) {
00497 return -1;
00498 } else if (n1 < n2) {
00499 return 1;
00500 } else {
00501 return 0;
00502 }
00503 }
00504
00505
00506 gboolean
00507 xmms_magic_extension_add (const gchar *mime, const gchar *ext)
00508 {
00509 xmms_magic_ext_data_t *e;
00510
00511 g_return_val_if_fail (mime, FALSE);
00512 g_return_val_if_fail (ext, FALSE);
00513
00514 e = g_new0 (xmms_magic_ext_data_t, 1);
00515 e->pattern = g_strdup (ext);
00516 e->type = g_strdup (mime);
00517
00518 ext_list = g_list_prepend (ext_list, e);
00519
00520 return TRUE;
00521 }
00522
00523 gboolean
00524 xmms_magic_add (const gchar *desc, const gchar *mime, ...)
00525 {
00526 GNode *tree, *node = NULL;
00527 va_list ap;
00528 gchar *s;
00529 gpointer *root_props;
00530 gboolean ret = TRUE;
00531
00532 g_return_val_if_fail (desc, FALSE);
00533 g_return_val_if_fail (mime, FALSE);
00534
00535
00536 va_start (ap, mime);
00537
00538 s = va_arg (ap, gchar *);
00539 if (!s) {
00540 va_end (ap);
00541 return FALSE;
00542 }
00543
00544
00545 root_props = g_new0 (gpointer, 2);
00546 root_props[0] = g_strdup (desc);
00547 root_props[1] = g_strdup (mime);
00548 tree = g_node_new (root_props);
00549
00550 do {
00551 if (!*s) {
00552 ret = FALSE;
00553 xmms_log_error ("invalid magic spec: '%s'", s);
00554 break;
00555 }
00556
00557 s = g_strdup (s);
00558 node = xmms_magic_add_node (tree, s, node);
00559 g_free (s);
00560
00561 if (!node) {
00562 xmms_log_error ("invalid magic spec: '%s'", s);
00563 ret = FALSE;
00564 break;
00565 }
00566 } while ((s = va_arg (ap, gchar *)));
00567
00568 va_end (ap);
00569
00570
00571 if (ret) {
00572 magic_list =
00573 g_list_insert_sorted (magic_list, tree,
00574 (GCompareFunc) cb_sort_magic_list);
00575 } else {
00576 xmms_magic_tree_free (tree);
00577 }
00578
00579 return ret;
00580 }
00581
00582 static gboolean
00583 xmms_magic_plugin_init (xmms_xform_t *xform)
00584 {
00585 xmms_magic_checker_t c;
00586 gchar *res;
00587 const gchar *url;
00588 xmms_config_property_t *cv;
00589
00590 c.xform = xform;
00591 c.read = c.offset = 0;
00592 c.alloc = 128;
00593 c.buf = g_malloc (c.alloc);
00594
00595 cv = xmms_xform_config_lookup (xform, "dumpcount");
00596 c.dumpcount = xmms_config_property_get_int (cv);
00597
00598 url = xmms_xform_indata_find_str (xform, XMMS_STREAM_TYPE_URL);
00599
00600 res = xmms_magic_match (&c, url);
00601 if (res) {
00602 xmms_xform_metadata_set_str (xform, XMMS_MEDIALIB_ENTRY_PROPERTY_MIME, res);
00603 xmms_xform_outdata_type_add (xform,
00604 XMMS_STREAM_TYPE_MIMETYPE,
00605 res,
00606 XMMS_STREAM_TYPE_END);
00607 }
00608
00609 g_free (c.buf);
00610
00611 return !!res;
00612 }
00613
00614 static gboolean
00615 xmms_magic_plugin_setup (xmms_xform_plugin_t *xform_plugin)
00616 {
00617 xmms_xform_methods_t methods;
00618
00619 XMMS_XFORM_METHODS_INIT (methods);
00620 methods.init = xmms_magic_plugin_init;
00621 methods.read = xmms_xform_read;
00622 methods.seek = xmms_xform_seek;
00623
00624 xmms_xform_plugin_methods_set (xform_plugin, &methods);
00625
00626 xmms_xform_plugin_indata_add (xform_plugin,
00627 XMMS_STREAM_TYPE_MIMETYPE,
00628 "application/octet-stream",
00629 XMMS_STREAM_TYPE_END);
00630
00631 xmms_xform_plugin_config_property_register (xform_plugin, "dumpcount",
00632 "16", NULL, NULL);
00633
00634 return TRUE;
00635 }
00636
00637 XMMS_XFORM_BUILTIN (magic,
00638 "Magic file identifier",
00639 XMMS_VERSION,
00640 "Magic file identifier",
00641 xmms_magic_plugin_setup);