]> git.street.me.uk Git - andy/viking.git/blobdiff - src/thumbnails.c
SF Bugs#132: Fix to correctly open GPX files in Windows via double click.
[andy/viking.git] / src / thumbnails.c
index 0461114b65554655f631eb1b75c0e42d20e4c670..58e028dcc737e83e957ed08a35c0f9a8cbdba04a 100644 (file)
@@ -41,6 +41,7 @@
 #include "viking.h"
 #include "thumbnails.h"
 #include "icons/icons.h"
+#include "md5_hash.h"
 
 #ifdef __CYGWIN__
 #ifdef __CYGWIN_USE_BIG_TYPES__
@@ -55,7 +56,6 @@
 
 #undef MIN /* quit yer whining, gcc */
 #undef MAX
-#include <sys/param.h> /* for realpath() */
 #ifndef MAX
 /* We need MAX macro and some system does not offer it */
 #define MAX(a,b) (((a)>(b))?(a):(b))
@@ -66,8 +66,6 @@
 #ifdef WINDOWS
 #define THUMB_DIR "\\THUMBNAILS\\" /* viking maps default viking\maps */
 #define THUMB_SUB_DIR "normal\\"
-#define realpath(X,Y) _fullpath(Y,X,MAX_PATH)
-
 #else
 #define THUMB_DIR "/.thumbnails/"
 #define THUMB_SUB_DIR "normal/"
 
 #define PIXMAP_THUMB_SIZE  128
 
-#ifndef MAXPATHLEN
-#define MAXPATHLEN 1024
-#endif
-
-static char *md5_hash(const char *message);
-static char *pathdup(const char *path);
 static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full);
 static GdkPixbuf *child_create_thumbnail(const gchar *path);
 
@@ -108,7 +100,7 @@ void a_thumbnails_create(const gchar *filename)
 
   if ( ! pixbuf )
     pixbuf = child_create_thumbnail(filename);
-  
+
   if ( pixbuf )
     g_object_unref (  G_OBJECT ( pixbuf ) );
 }
@@ -122,7 +114,7 @@ GdkPixbuf *a_thumbnails_scale_pixbuf(GdkPixbuf *src, int max_w, int max_h)
 
        if (w <= max_w && h <= max_h)
        {
-               gdk_pixbuf_ref(src);
+               g_object_ref ( G_OBJECT ( src ) );
                return src;
        }
        else
@@ -145,14 +137,17 @@ static GdkPixbuf *child_create_thumbnail(const gchar *path)
        GdkPixbuf *image, *tmpbuf;
 
        image = gdk_pixbuf_new_from_file(path, NULL);
+       if (!image)
+               return NULL;
+
        tmpbuf = gdk_pixbuf_apply_embedded_orientation(image);
        g_object_unref(G_OBJECT(image));
        image = tmpbuf;
 
        if (image)
-        {
+       {
                GdkPixbuf *thumb = save_thumbnail(path, image);
-               gdk_pixbuf_unref ( image );
+               g_object_unref ( G_OBJECT ( image ) );
                return thumb;
        }
 
@@ -187,15 +182,16 @@ static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
        ssize = g_strdup_printf(ST_SIZE_FMT, info.st_size);
        smtime = g_strdup_printf("%ld", (long) info.st_mtime);
 
-       path = pathdup(pathname);
+       path = file_realpath_dup(pathname);
        uri = g_strconcat("file://", path, NULL);
        md5 = md5_hash(uri);
        g_free(path);
-               
+
        to = g_string_new(HOME_DIR);
        g_string_append(to, THUMB_DIR);
        g_string_append(to, THUMB_SUB_DIR);
-       g_mkdir_with_parents(to->str, 0700);
+       if ( g_mkdir_with_parents(to->str, 0700) != 0 )
+               g_warning ("%s: Failed to mkdir %s", __FUNCTION__, to->str );
        g_string_append(to, md5);
        name_len = to->len + 4; /* Truncate to this length when renaming */
 #ifdef WINDOWS
@@ -206,19 +202,38 @@ static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full)
 
        g_free(md5);
 
+       // Thumb::URI must be in ISO-8859-1 encoding otherwise gdk_pixbuf_save() will fail
+       // - e.g. if characters such as 'ě' are encountered
+       // Also see http://en.wikipedia.org/wiki/ISO/IEC_8859-1
+       // ATM GLIB Manual doesn't specify in which version this function became available
+       //  find out that it's fairly recent so may break builds without this test
+#if GLIB_CHECK_VERSION(2,40,0)
+       char *thumb_uri = g_str_to_ascii ( uri, NULL );
+#else
+       char *thumb_uri = g_strdup ( uri );
+#endif
        old_mask = umask(0077);
-       gdk_pixbuf_save(thumb, to->str, "png", NULL,
-                       "tEXt::Thumb::Image::Width", swidth,
-                       "tEXt::Thumb::Image::Height", sheight,
-                       "tEXt::Thumb::Size", ssize,
-                       "tEXt::Thumb::MTime", smtime,
-                       "tEXt::Thumb::URI", uri,
-                       "tEXt::Software", PROJECT,
-                       "tEXt::Software::Orientation", orientation ? orientation : "0",
-                       NULL);
+       GError *error = NULL;
+       gdk_pixbuf_save(thumb, to->str, "png", &error,
+                       "tEXt::Thumb::Image::Width", swidth,
+                       "tEXt::Thumb::Image::Height", sheight,
+                       "tEXt::Thumb::Size", ssize,
+                       "tEXt::Thumb::MTime", smtime,
+                       "tEXt::Thumb::URI", thumb_uri,
+                       "tEXt::Software", PROJECT,
+                       "tEXt::Software::Orientation", orientation ? orientation : "0",
+                       NULL);
        umask(old_mask);
+       g_free(thumb_uri);
 
-       /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
+       if (error) {
+               g_warning ( "%s::%s", __FUNCTION__, error->message );
+               g_error_free ( error );
+               g_object_unref ( G_OBJECT(thumb) );
+               thumb = NULL; /* return NULL */
+       }
+       else
+       /* We create the file ###.png.Viking-PID and rename it to avoid
         * a race condition if two programs create the same thumb at
         * once.
         */
@@ -255,11 +270,11 @@ GdkPixbuf *a_thumbnails_get(const gchar *pathname)
        const char *ssize, *smtime;
        struct stat info;
 
-       path = pathdup(pathname);
+       path = file_realpath_dup(pathname);
        uri = g_strconcat("file://", path, NULL);
        md5 = md5_hash(uri);
        g_free(uri);
-       
+
        thumb_path = g_strdup_printf("%s%s%s%s.png", HOME_DIR, THUMB_DIR, THUMB_SUB_DIR, md5);
 
        g_free(md5);
@@ -286,276 +301,10 @@ GdkPixbuf *a_thumbnails_get(const gchar *pathname)
        goto out;
 err:
        if (thumb)
-               gdk_pixbuf_unref(thumb);
+               g_object_unref ( G_OBJECT ( thumb ) );
        thumb = NULL;
 out:
        g_free(path);
        g_free(thumb_path);
        return thumb;
 }
-
-/* pathdup() stuff */
-
-static char *pathdup(const char *path)
-{
-       char real[MAXPATHLEN];
-
-       g_return_val_if_fail(path != NULL, NULL);
-
-       if (realpath(path, real))
-               return g_strdup(real);
-
-       return g_strdup(path);
-}
-
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest. The original code was
- * written by Colin Plumb in 1993, and put in the public domain.
- * 
- * Modified to use glib datatypes. Put under GPL to simplify
- * licensing for ROX-Filer. Taken from Debian's dpkg package.
- *
- */
-
-#define md5byte unsigned char
-
-typedef struct _MD5Context MD5Context;
-
-struct _MD5Context {
-       guint32 buf[4];
-       guint32 bytes[2];
-       guint32 in[16];
-};
-
-static void MD5Init(MD5Context *ctx);
-static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len);
-static char *MD5Final(MD5Context *ctx);
-static void MD5Transform(guint32 buf[4], guint32 const in[16]);
-
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-static void byteSwap(guint32 *buf, unsigned words)
-{
-       md5byte *p = (md5byte *)buf;
-
-       do {
-               *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 |
-                       ((unsigned)p[1] << 8 | p[0]);
-               p += 4;
-       } while (--words);
-}
-#else
-#define byteSwap(buf,words)
-#endif
-
-/*
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
-static void MD5Init(MD5Context *ctx)
-{
-       ctx->buf[0] = 0x67452301;
-       ctx->buf[1] = 0xefcdab89;
-       ctx->buf[2] = 0x98badcfe;
-       ctx->buf[3] = 0x10325476;
-
-       ctx->bytes[0] = 0;
-       ctx->bytes[1] = 0;
-}
-
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len)
-{
-       guint32 t;
-
-       /* Update byte count */
-
-       t = ctx->bytes[0];
-       if ((ctx->bytes[0] = t + len) < t)
-               ctx->bytes[1]++;        /* Carry from low to high */
-
-       t = 64 - (t & 0x3f);    /* Space available in ctx->in (at least 1) */
-       if (t > len) {
-               memcpy((md5byte *)ctx->in + 64 - t, buf, len);
-               return;
-       }
-       /* First chunk is an odd size */
-       memcpy((md5byte *)ctx->in + 64 - t, buf, t);
-       byteSwap(ctx->in, 16);
-       MD5Transform(ctx->buf, ctx->in);
-       buf += t;
-       len -= t;
-
-       /* Process data in 64-byte chunks */
-       while (len >= 64) {
-               memcpy(ctx->in, buf, 64);
-               byteSwap(ctx->in, 16);
-               MD5Transform(ctx->buf, ctx->in);
-               buf += 64;
-               len -= 64;
-       }
-
-       /* Handle any remaining bytes of data. */
-       memcpy(ctx->in, buf, len);
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern 
- * 1 0* (64-bit count of bits processed, MSB-first)
- * Returns the newly allocated string of the hash.
- */
-static char *MD5Final(MD5Context *ctx)
-{
-       char *retval;
-       int i;
-       int count = ctx->bytes[0] & 0x3f;       /* Number of bytes in ctx->in */
-       md5byte *p = (md5byte *)ctx->in + count;
-       guint8  *bytes;
-
-       /* Set the first char of padding to 0x80.  There is always room. */
-       *p++ = 0x80;
-
-       /* Bytes of padding needed to make 56 bytes (-8..55) */
-       count = 56 - 1 - count;
-
-       if (count < 0) {        /* Padding forces an extra block */
-               memset(p, 0, count + 8);
-               byteSwap(ctx->in, 16);
-               MD5Transform(ctx->buf, ctx->in);
-               p = (md5byte *)ctx->in;
-               count = 56;
-       }
-       memset(p, 0, count);
-       byteSwap(ctx->in, 14);
-
-       /* Append length in bits and transform */
-       ctx->in[14] = ctx->bytes[0] << 3;
-       ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
-       MD5Transform(ctx->buf, ctx->in);
-
-       byteSwap(ctx->buf, 4);
-
-       retval = g_malloc(33);
-       bytes = (guint8 *) ctx->buf;
-       for (i = 0; i < 16; i++)
-               sprintf(retval + (i * 2), "%02x", bytes[i]);
-       retval[32] = '\0';
-       
-       return retval;
-}
-
-# ifndef ASM_MD5
-
-/* The four core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f,w,x,y,z,in,s) \
-        (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data.  MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-static void MD5Transform(guint32 buf[4], guint32 const in[16])
-{
-       register guint32 a, b, c, d;
-
-       a = buf[0];
-       b = buf[1];
-       c = buf[2];
-       d = buf[3];
-
-       MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-       MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-       MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
-       MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-       MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-       MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-       MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
-       MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
-       MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
-       MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-       MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-       MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-       MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
-       MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-       MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-       MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-       MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-       MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
-       MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-       MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-       MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-       MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
-       MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-       MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-       MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-       MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-       MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-       MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-       MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-       MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-       MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-       MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-       MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-       MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
-       MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-       MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-       MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-       MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-       MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-       MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-       MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-       MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-       MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-       MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
-       MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-       MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-       MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-       MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
-       MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
-       MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
-       MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-       MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-       MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-       MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-       MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-       MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-       MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-       MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-       MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
-       MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-       MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-       MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-       MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-       MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
-       buf[0] += a;
-       buf[1] += b;
-       buf[2] += c;
-       buf[3] += d;
-}
-
-# endif /* ASM_MD5 */
-
-static char *md5_hash(const char *message)
-{
-       MD5Context ctx;
-
-       MD5Init(&ctx);
-       MD5Update(&ctx, (md5byte *) message, strlen(message));
-       return MD5Final(&ctx);
-}