On Thu, 2008-07-03 at 00:06 -0400, Matthias Clasen wrote:
> Its been a month since I sent out this proposal, and the only comment I
> have heard so far is that the name "Content types" may be slightly
> ambiguous. My proposal for addressing this is to change the section
> title to "Content types for volumes", and stick with x-content for the
> type, since it is already in use.
>
> If there is no further feedback, I'd like to move ahead with adding this
> to the spec, since we want to implement content type sniffing in gvfs
> soon.
Here is an updated version of the patch for the spec, using a less
ambiguous title and adding some more details.
Bastien, if there is no more feedback, can you commit this when you come
back ?
Thanks, Matthias
Index: shared-mime-info-spec.xml
===================================================================
RCS file: /cvs/mime/shared-mime-info/shared-mime-info-spec.xml,v
retrieving revision 1.60
diff -u -r1.60 shared-mime-info-spec.xml
--- shared-mime-info-spec.xml 9 Jun 2008 09:27:30 -0000 1.60
+++ shared-mime-info-spec.xml 11 Jul 2008 14:22:01 -0000
@@ -1,8 +1,8 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
- <!ENTITY updated "25 January 2008">
- <!ENTITY version "0.17">
+ <!ENTITY updated "11 July 2008">
+ <!ENTITY version "0.18">
]>
<article id="index">
@@ -343,6 +343,34 @@
If <userinput>localName</userinput> is present but empty then the document element may have
any name, but the namespace must still match.
</para></listitem>
+ <listitem><para>
+<userinput>treemagic</userinput> elements contain a list of <userinput>treematch</userinput> elements,
+any of which may match, and an optional <userinput>priority</userinput> attribute for all of the
+contained rules. The default priority value is 50, and the maximum is 100.
+ </para><para>
+Each <userinput>treematch</userinput> element has a number of attributes:
+
+<informaltable>
+ <tgroup cols="3">
+ <thead><row><entry>Attribute</entry><entry>Required?</entry><entry>Value</entry></row></thead>
+ <tbody>
+
+ <row><entry>path</entry><entry>Yes</entry><entry>A path that must be present on the mounted volume/filesystem.</entry></row>
+
+ <row><entry>type</entry><entry>No</entry><entry>The type of path. Possible values: <userinput>file</userinput>, <userinput>directory</userinput>, <userinput>link</userinput></entry></row>
+
+ <row><entry>ignore-case</entry><entry>No</entry><entry>Whether path should be matched case-insensitively. Possible values: <userinput>true</userinput>, <userinput>false</userinput></entry></row>
+
+ <row><entry>executable</entry><entry>No</entry><entry>Whether the file must be executable. Possible values: <userinput>true</userinput>, <userinput>false</userinput></entry></row>
+
+ <row><entry>non-empty</entry><entry>No</entry><entry>Whether the directory must be non-empty. Possible values: <userinput>true</userinput>, <userinput>false</userinput></entry></row>
+
+ </tbody></tgroup>
+</informaltable>
+
+<userinput>treematch</userinput> elements can be nested, meaning that both the outer and the inner <userinput>treematch</userinput>
+must be satisfied for a "match".
+ </para></listitem>
</itemizedlist>
Applications may also define their own elements, provided they are namespaced to prevent collisions.
Unknown elements are copied directly to the output XML files like <userinput>comment</userinput>
@@ -574,6 +602,39 @@
</para>
</sect2>
<sect2>
+ <title>The treemagic files</title>
+ <para>
+The tree magic data is stored in a file with a format that is very similar to the magic file format.
+ </para>
+ <para>
+The file starts with the magic string "MIME-TreeMagic\0\n". There is no version number in the file.
+Incompatible changes will be handled by creating both the current `treemagic' and a newer `treemagic2'
+in the new format. Where possible, changes will be made in a compatible fashion.
+ </para>
+ <para>
+The rest of the file is made up of a sequence of small sections. Each section is introduced by giving
+the priority and type in brackeds, followed by a newline character. Higher priority entries come
+first. Example:
+<screen>[50:x-content/image-dcf]\n</screen>
+Each line in the section takes the form:
+<screen>[ indent ] ">" "\"" path "\"" "=" type [ "," option ]* "\n"</screen>
+<informaltable>
+ <tgroup cols="2">
+ <thead><row><entry>Part</entry><entry>Meaning</entry></row></thead>
+ <tbody>
+
+ <row><entry>indent</entry><entry>The nesting depth of the rule.</entry></row>
+ <row><entry>path</entry><entry>The path to match.</entry></row>
+ <row><entry>type</entry><entry>The required file type, one of "file", "directory", "link" or "any"</entry></row>
+ <row><entry>option</entry><entry>Optional for the optional attributes of <userinput>treematch</userinput> elements.
+Possible values are "executable", "ignore-case", "non-empty", or a MIME type</entry></row>
+ </tbody>
+ </tgroup>
+</informaltable>
+ </para><para>
+ </para>
+ </sect2>
+ <sect2>
<title>The mime.cache files</title>
<para>
The <filename>mime.cache</filename> files contain the same information as the
@@ -847,6 +908,22 @@
</para>
</sect2>
<sect2>
+ <title>Content types for volumes</title>
+ <para>
+Traditional MIME types apply to individual files or bytestreams. It is often useful
+to apply the same methodologies when classifying the content of mountable volumes or
+filesystems. The x-content type has been introduced for this purpose. Typical examples
+are x-content/audio-dvd, x-content/blank-cd or x-content/image-dcf.
+ </para>
+ <para>
+Matching of content types works with <userinput>treemagic</userinput> elements, which
+are analogous to the <userinput>magic</userinput> elements used for MIME type matching.
+Instead of looking for byte sequences in files, <userinput>treemagic</userinput> element
+allow to look for files with certain names, permissions or mime types in a directory
+hierarchy.
+ </para>
+ </sect2>
+ <sect2>
<title>Security implications</title>
<para>
The system described in this document is intended to allow different programs
--- update-mime-database.c.beforetree 2008-06-09 00:08:13.000000000 -0400
+++ update-mime-database.c 2008-06-09 23:02:22.000000000 -0400
@@ -66,6 +66,12 @@
/* A parsed <match> element */
typedef struct _Match Match;
+/* A parsed <treemagic> element */
+typedef struct _TreeMagic TreeMagic;
+
+/* A parsed <treematch> element */
+typedef struct _TreeMatch TreeMatch;
+
/* A parsed <glob> element */
typedef struct _Glob Glob;
@@ -102,6 +108,23 @@
GList *matches;
};
+struct _TreeMagic {
+ int priority;
+ Type *type;
+ GList *matches;
+};
+
+struct _TreeMatch {
+ char *path;
+ gboolean ignore_case;
+ gboolean executable;
+ gboolean non_empty;
+ gint type;
+ char *mimetype;
+
+ GList *matches;
+};
+
/* Maps MIME type names to Types */
static GHashTable *types = NULL;
@@ -114,6 +137,9 @@
/* 'magic' nodes */
static GPtrArray *magic_array = NULL;
+/* 'treemagic' nodes */
+static GPtrArray *tree_magic_array = NULL;
+
/* Maps MIME type names to superclass names */
static GHashTable *subclass_hash = NULL;
@@ -132,6 +158,8 @@
/* Static prototypes */
static Magic *magic_new(xmlNode *node, Type *type, GError **error);
+static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error);
+
static void g_log_handler (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
@@ -372,6 +400,20 @@
else
g_return_val_if_fail(magic == NULL, FALSE);
}
+ else if (strcmp((char *)field->name, "treemagic") == 0)
+ {
+ TreeMagic *magic;
+
+ magic = tree_magic_new(field, type, error);
+
+ if (!*error)
+ {
+ g_return_val_if_fail(magic != NULL, FALSE);
+ g_ptr_array_add(tree_magic_array, magic);
+ }
+ else
+ g_return_val_if_fail(magic == NULL, FALSE);
+ }
else if (strcmp((char *)field->name, "comment") == 0 ||
strcmp((char *)field->name, "acronym") == 0 ||
strcmp((char *)field->name, "expanded-acronym") == 0)
@@ -857,6 +899,25 @@
return retval;
}
+/* Comparison function to get the tree magic rules in priority order */
+static gint cmp_tree_magic(gconstpointer a, gconstpointer b)
+{
+ TreeMagic *aa = *(TreeMagic **) a;
+ TreeMagic *bb = *(TreeMagic **) b;
+ int retval;
+
+ if (aa->priority > bb->priority)
+ return -1;
+ else if (aa->priority < bb->priority)
+ return 1;
+
+ retval = strcmp(aa->type->media, bb->type->media);
+ if (!retval)
+ retval = strcmp(aa->type->subtype, bb->type->subtype);
+
+ return retval;
+}
+
/* Write out 'n' as a two-byte big-endian number to 'stream' */
static void write16(FILE *stream, guint32 n)
{
@@ -1398,6 +1459,222 @@
return magic;
}
+static TreeMatch *tree_match_new(void)
+{
+ TreeMatch *match;
+
+ match = g_new(TreeMatch, 1);
+ match->path = NULL;
+ match->ignore_case = 0;
+ match->executable = 0;
+ match->non_empty = 0;
+ match->type = 0;
+ match->mimetype = NULL;
+ match->matches = NULL;
+
+ return match;
+}
+
+static void tree_match_free(TreeMatch *match)
+{
+ GList *next;
+
+ g_return_if_fail(match != NULL);
+
+ for (next = match->matches; next; next = next->next)
+ tree_match_free((TreeMatch *) next->data);
+
+ g_list_free(match->matches);
+
+ g_free(match->path);
+ g_free(match->mimetype);
+
+ g_free(match);
+}
+
+/* Turn the list of child nodes of 'parent' into a list of TreeMatches */
+static GList *build_tree_matches(xmlNode *parent, GError **error)
+{
+ xmlNode *node;
+ GList *out = NULL;
+ char *attr;
+
+ g_return_val_if_fail(error != NULL, NULL);
+
+ for (node = parent->xmlChildrenNode; node; node = node->next)
+ {
+ TreeMatch *match;
+
+ if (node->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (node->ns == NULL || xmlStrcmp(node->ns->href, FREE_NS) != 0)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Element found with non-freedesktop.org "
+ "namespace"));
+ break;
+ }
+
+ if (strcmp((char *)node->name, "treematch") != 0)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Expected <treematch> element, but found "
+ "<%s> instead"), node->name);
+ break;
+ }
+
+ match = tree_match_new();
+
+ attr = my_xmlGetNsProp(node, "path", NULL);
+ if (attr)
+ {
+ match->path = g_strdup (attr);
+ xmlFree (attr);
+ }
+ else
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Missing 'path' attribute in <treematch>"));
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "type", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "file") == 0)
+ {
+ match->type = 1;
+ }
+ else if (strcmp (attr, "directory") == 0)
+ {
+ match->type = 2;
+ }
+ else if (strcmp (attr, "link") == 0)
+ {
+ match->type = 3;
+ }
+ else
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Invalid 'type' attribute in <treematch>"));
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "executable", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "true") == 0)
+ {
+ match->executable = 1;
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "ignore-case", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "true") == 0)
+ {
+ match->ignore_case = 1;
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "non-empty", NULL);
+ if (attr)
+ {
+ if (strcmp (attr, "true") == 0)
+ {
+ match->non_empty = 1;
+ }
+ xmlFree(attr);
+ }
+ }
+ if (!*error)
+ {
+ attr = my_xmlGetNsProp(node, "mimetype", NULL);
+ if (attr)
+ {
+ match->mimetype = g_strdup (attr);
+ xmlFree(attr);
+ }
+ }
+
+ if (*error)
+ {
+ tree_match_free(match);
+ break;
+ }
+
+ out = g_list_append(out, match);
+
+ match->matches = build_tree_matches(node, error);
+ if (*error)
+ break;
+ }
+
+ return out;
+}
+
+static void tree_magic_free(TreeMagic *magic)
+{
+ GList *next;
+
+ g_return_if_fail(magic != NULL);
+
+ for (next = magic->matches; next; next = next->next)
+ tree_match_free((TreeMatch *) next->data);
+ g_list_free(magic->matches);
+
+ g_free(magic);
+}
+
+/* Create a new TreeMagic object by parsing 'node' (a <treemagic> element) */
+static TreeMagic *tree_magic_new(xmlNode *node, Type *type, GError **error)
+{
+ TreeMagic *magic = NULL;
+ int prio;
+
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(type != NULL, NULL);
+ g_return_val_if_fail(error != NULL, NULL);
+
+ prio = get_priority(node);
+
+ if (prio == -1)
+ {
+ g_set_error(error, MIME_ERROR, 0,
+ _("Bad priority (%d) in <treemagic> element"), prio);
+ }
+ else
+ {
+ magic = g_new(TreeMagic, 1);
+ magic->priority = prio;
+ magic->type = type;
+ magic->matches = build_tree_matches(node, error);
+
+ if (*error)
+ {
+ gchar *old = (*error)->message;
+ tree_magic_free(magic);
+ magic = NULL;
+ (*error)->message = g_strconcat(
+ _("Error in <treematch> element: "), old, NULL);
+ g_free(old);
+ }
+ }
+
+ return magic;
+}
+
/* Write a list of Match elements (and their children) to the 'magic' file */
static void write_magic_children(FILE *stream, GList *matches, int indent)
{
@@ -1442,6 +1719,62 @@
write_magic_children(stream, magic->matches, 0);
}
+/* Write a list of TreeMatch elements (and their children) to the 'treemagic' file */
+static void write_tree_magic_children(FILE *stream, GList *matches, int indent)
+{
+ GList *next;
+
+ for (next = matches; next; next = next->next)
+ {
+ TreeMatch *match = (TreeMatch *) next->data;
+
+ if (indent)
+ g_fprintf(stream,
+ "%d>\"%s\"=",
+ indent,
+ match->path);
+ else
+ g_fprintf(stream, ">\"%s\"=", match->path);
+
+ switch (match->type)
+ {
+ default:
+ case 0:
+ fputs("any", stream);
+ break;
+ case 1:
+ fputs("file", stream);
+ break;
+ case 2:
+ fputs("directory", stream);
+ break;
+ case 3:
+ fputs("link", stream);
+ break;
+ }
+ if (match->ignore_case)
+ fputs (",ignore-case", stream);
+ if (match->executable)
+ fputs (",executable", stream);
+ if (match->non_empty)
+ fputs (",non-empty", stream);
+ if (match->mimetype)
+ g_fprintf (stream, ",%s", match->mimetype);
+
+ fputc('\n', stream);
+
+ write_tree_magic_children(stream, match->matches, indent + 1);
+ }
+}
+/* Write a whole TreeMagic element to the 'treemagic' file */
+static void write_tree_magic(FILE *stream, TreeMagic *magic)
+{
+ g_fprintf(stream, "[%d:%s/%s]\n", magic->priority,
+ magic->type->media, magic->type->subtype);
+
+ write_tree_magic_children(stream, magic->matches, 0);
+}
+
/* Check each of the directories with generated XML files, looking for types
* which we didn't get on this scan, and delete them.
*/
@@ -3076,6 +3409,7 @@
namespace_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
magic_array = g_ptr_array_new();
+ tree_magic_array = g_ptr_array_new();
subclass_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, free_string_list);
alias_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -3225,6 +3559,28 @@
{
FILE *stream;
char *path;
+ int i;
+ path = g_strconcat(mime_dir, "/treemagic.new", NULL);
+ stream = open_or_die(path);
+ fwrite("MIME-TreeMagic\0\n", 1, 16, stream);
+
+ if (tree_magic_array->len)
+ g_ptr_array_sort(tree_magic_array, cmp_tree_magic);
+ for (i = 0; i < tree_magic_array->len; i++)
+ {
+ TreeMagic *magic = (TreeMagic *) tree_magic_array->pdata[i];
+
+ write_tree_magic(stream, magic);
+ }
+ fclose(stream);
+
+ atomic_update(path);
+ g_free(path);
+ }
+
+ {
+ FILE *stream;
+ char *path;
path = g_strconcat(mime_dir, "/mime.cache.new", NULL);
stream = open_or_die(path);
@@ -3237,6 +3593,8 @@
g_ptr_array_foreach(magic_array, (GFunc)magic_free, NULL);
g_ptr_array_free(magic_array, TRUE);
+ g_ptr_array_foreach(tree_magic_array, (GFunc)tree_magic_free, NULL);
+ g_ptr_array_free(tree_magic_array, TRUE);
g_hash_table_destroy(types);
g_hash_table_destroy(globs_hash);
_______________________________________________
xdg mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/xdg