XMMS2
|
00001 /* XMMS2 - X Music Multiplexer System 00002 * Copyright (C) 2003-2011 XMMS2 Team 00003 * 00004 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 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 /* no operator found */ 00172 return o->o; 00173 } else if (c == o->c) { 00174 (*s)++; /* skip operator */ 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 /* @todo Implement string operators */ 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; /* won't get here, handled above */ 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 /* this isn't a magic entry, but the description of the tree */ 00274 g_free (data[0]); /* desc */ 00275 g_free (data[1]); /* mime */ 00276 g_free (data); 00277 } else { 00278 xmms_magic_entry_free (entry); 00279 } 00280 00281 return FALSE; /* continue traversal */ 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 /* indent level is number of leading '>' characters */ 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 /* larger jumps are invalid */ 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 /* do we have enough data ready for this check? 00373 * if not, read some more 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 /* couldn't read enough data */ 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); /* returns */ 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); /* returns */ 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); /* returns */ 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 /* empty subtrees match anything */ 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 /* only one of the contained sets has to match */ 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 /* now process the magic specs in the argument list */ 00536 va_start (ap, mime); 00537 00538 s = va_arg (ap, gchar *); 00539 if (!s) { /* no magic specs passed -> failure */ 00540 va_end (ap); 00541 return FALSE; 00542 } 00543 00544 /* root node stores the description and the mimetype */ 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); /* we need our own copy */ 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 /* only add this tree to the list if all spec chunks are valid */ 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; /* start with a 128 bytes buffer */ 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);