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