Skip to content

Commit

Permalink
Do not assume g_content_type_guess() always returns valid results
Browse files Browse the repository at this point in the history
On Chrome OS the database of magic is either incomplete or missing and so
provide important fallbacks manually.
  • Loading branch information
hughsie committed Aug 19, 2020
1 parent cebfa2b commit 3101baa
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 22 deletions.
3 changes: 3 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ libxmlb = library(
'xb-builder-node.c',
'xb-builder-source.c',
'xb-builder-source-ctx.c',
'xb-common.c',
'xb-machine.c',
'xb-opcode.c',
'xb-node.c',
Expand Down Expand Up @@ -115,6 +116,7 @@ if get_option('introspection')
'xb-builder-source.h',
'xb-builder-source-ctx.c',
'xb-builder-source-ctx.h',
'xb-common.c',
'xb-machine.c',
'xb-machine.h',
'xb-node.c',
Expand Down Expand Up @@ -198,6 +200,7 @@ if get_option('tests')
'xb-builder-node.c',
'xb-builder-source.c',
'xb-builder-source-ctx.c',
'xb-common.c',
'xb-machine.c',
'xb-node.c',
'xb-node-query.c',
Expand Down
28 changes: 6 additions & 22 deletions src/xb-builder-source-ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <gio/gio.h>

#include "xb-builder-source-ctx-private.h"
#include "xb-common-private.h"

typedef struct {
GInputStream *istream;
Expand Down Expand Up @@ -100,39 +101,22 @@ xb_builder_source_ctx_get_content_type (XbBuilderSourceCtx *self,
GError **error)
{
XbBuilderSourceCtxPrivate *priv = GET_PRIVATE (self);
g_autofree gchar *content_type = NULL;
gsize bufsz = 0;
guchar buf[4096] = { 0x00 };

g_return_val_if_fail (XB_IS_BUILDER_SOURCE_CTX (self), NULL);

if (G_IS_SEEKABLE (priv->istream)) {
gsize bufsz = 0;
guchar buf[4096] = { 0x00 };
if (!g_input_stream_read_all (priv->istream, buf, sizeof(buf),
&bufsz, cancellable, error))
return NULL;
if (!g_seekable_seek (G_SEEKABLE (priv->istream), 0, G_SEEK_SET,
cancellable, error))
return NULL;
if (bufsz > 0)
content_type = g_content_type_guess (priv->filename, buf, bufsz, NULL);
}

/* either unseekable, or empty */
if (content_type == NULL)
content_type = g_content_type_guess (priv->filename, NULL, 0, NULL);

#ifdef _WIN32
/* map Windows "mime-type" to a content type */
if (g_strcmp0 (content_type, ".gz") == 0)
return g_strdup ("application/gzip");
if (g_strcmp0 (content_type, ".txt") == 0 ||
g_strcmp0 (content_type, ".xml") == 0)
return g_strdup ("application/xml");
if (g_strcmp0 (content_type, ".desktop") == 0)
return g_strdup ("application/x-desktop");
#endif

return g_steal_pointer (&content_type);
if (bufsz > 0)
return xb_content_type_guess (priv->filename, buf, bufsz);
return xb_content_type_guess (priv->filename, NULL, 0);
}

/* private */
Expand Down
13 changes: 13 additions & 0 deletions src/xb-common-private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/

#pragma once

#include <glib.h>

gchar *xb_content_type_guess (const gchar *filename,
const guchar *buf,
gsize bufsz);
98 changes: 98 additions & 0 deletions src/xb-common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/

#define G_LOG_DOMAIN "XbCommon"

#include "config.h"

#include <string.h>
#include <gio/gio.h>

#include "xb-common-private.h"

static const gchar *
xb_content_type_guess_from_fn (const gchar *filename)
{
gchar *ext; /* no ownership */

g_return_val_if_fail (filename != NULL, NULL);

/* get file extension with dot */
ext = g_strrstr (filename, ".");
if (ext == NULL)
return NULL;

/* map Windows "mime-type" to a content type */
if (g_strcmp0 (ext, ".gz") == 0)
return "application/gzip";
if (g_strcmp0 (ext, ".txt") == 0 ||
g_strcmp0 (ext, ".xml") == 0)
return "application/xml";
if (g_strcmp0 (ext, ".desktop") == 0)
return "application/x-desktop";
return NULL;
}

static gboolean
xb_content_type_match (const guchar *buf, gsize bufsz, gsize offset,
const gchar *magic, gsize magic_size)
{
/* document too small */
if (offset + magic_size > bufsz)
return FALSE;
return memcmp (buf + offset, magic, magic_size) == 0;
}

/**
* xb_content_type_guess: (skip)
* @filename: (nullable): filename
* @buf: (nullable): file data buffer
* @bufsz: size of file data buffer
*
* Guesses the content type based on example data. Either @filename or @buf may
* be %NULL, in which case the guess will be based solely on the other argument.
*
* Returns: a string indicating a guessed content type
**/
gchar *
xb_content_type_guess (const gchar *filename, const guchar *buf, gsize bufsz)
{
g_autofree gchar *content_type = NULL;

/* check for bad results, e.g. from Chrome OS */
content_type = g_content_type_guess (filename, buf, bufsz, NULL);
if (g_strcmp0 (content_type, "application/octet-stream") == 0 ||
g_strcmp0 (content_type, "text/plain") == 0) {

/* magic */
if (bufsz > 0) {
if (xb_content_type_match (buf, bufsz, 0x0, "\x1f\x8b", 2))
return g_strdup ("application/gzip");
if (xb_content_type_match (buf, bufsz, 0x0, "<?xml", 5))
return g_strdup ("application/xml");
if (xb_content_type_match (buf, bufsz, 0x0, "[Desktop Entry]", 15))
return g_strdup ("application/x-desktop");
}

/* file extensions */
if (filename != NULL) {
const gchar *tmp = xb_content_type_guess_from_fn (filename);
if (tmp != NULL)
return g_strdup (tmp);
}
}

#ifdef _WIN32
/* fall back harder as there is no mime data at all */
if (filename != NULL) {
const gchar *tmp = xb_content_type_guess_from_fn (filename);
if (tmp != NULL)
return g_strdup (tmp);
}
#endif

return g_steal_pointer (&content_type);
}

0 comments on commit 3101baa

Please sign in to comment.