gimpthumb-utils.c 11 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * Thumbnail handling according to the Thumbnail Managing Standard.
 * http://triq.net/~pearl/thumbnail-spec/
 *
 * Copyright (C) 2001-2003  Sven Neumann <sven@gimp.org>
 *                          Michael Natterer <mitch@gimp.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

28
#include <errno.h>
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <glib-object.h>

#ifdef G_OS_WIN32
#include <libgimpbase/gimpwin32-io.h>
#endif

#include "libgimpmath/gimpmath.h"

44
#include "gimpthumb-error.h"
45 46 47
#include "gimpthumb-types.h"
#include "gimpthumb-utils.h"

48 49
#include "libgimp/libgimp-intl.h"

50

51 52 53
static gint           gimp_thumb_size     (GimpThumbSize  size);
static const gchar  * gimp_thumb_png_name (const gchar   *uri);
static void           gimp_thumb_exit     (void);
54 55 56 57 58 59 60 61 62 63 64 65



static gboolean    gimp_thumb_initialized = FALSE;
static gint        thumb_num_sizes        = 0;
static gint       *thumb_sizes            = NULL;
static gchar      *thumb_dir              = NULL;
static gchar     **thumb_subdirs          = NULL;
static gchar      *thumb_fail_subdir      = NULL;



66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
/**
 * gimp_thumb_init:
 * @creator: an ASCII string that identifies the thumbnail creator
 * @thumb_basedir: an absolute path or %NULL to use the default
 *
 * This function initializes the thumbnail system. It must be called
 * before any other functions from libgimpthumb are used. You may call
 * it more than once if you want to change the @thumb_basedir but if
 * you do that, you should make sure that no thread is still using the
 * library. Apart from this function, libgimpthumb is multi-thread
 * safe.
 *
 * The @creator string must be 7bit ASCII and should contain the name
 * of the software that creates the thumbnails. It is used to handle
 * thumbnail creation failures. See the spec for more details.
 *
 * Usually you will pass %NULL for @thumb_basedir. Thumbnails will
 * then be stored in the user's personal thumbnail directory as
 * defined in the spec. If you wish to use libgimpthumb to store
 * application-specific thumbnails, you can specify a different base
 * directory here.
 *
 * Return value: %TRUE if the library was successfully initialized.
 **/
90 91 92 93 94 95 96 97 98
gboolean
gimp_thumb_init (const gchar *creator,
                 const gchar *thumb_basedir)
{
  GEnumClass *enum_class;
  GEnumValue *enum_value;
  gint        i;

  g_return_val_if_fail (creator != NULL, FALSE);
99 100
  g_return_val_if_fail (thumb_basedir == NULL ||
                        g_path_is_absolute (thumb_basedir), FALSE);
101 102 103 104 105 106 107 108 109 110 111

  if (gimp_thumb_initialized)
    gimp_thumb_exit ();

  thumb_dir = (thumb_basedir ?
               g_strdup (thumb_basedir) :
               g_build_filename (g_get_home_dir(), ".thumbnails", NULL));

  enum_class = g_type_class_ref (GIMP_TYPE_THUMB_SIZE);

  thumb_num_sizes = enum_class->n_values;
Sven Neumann's avatar
Sven Neumann committed
112 113
  thumb_sizes     = g_new (gint, thumb_num_sizes);
  thumb_subdirs   = g_new (gchar *, thumb_num_sizes);
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

  for (i = 0, enum_value = enum_class->values;
       i < enum_class->n_values;
       i++, enum_value++)
    {
      thumb_sizes[i]   = enum_value->value;
      thumb_subdirs[i] = g_build_filename (thumb_dir,
                                           enum_value->value_nick, NULL);
    }

  thumb_fail_subdir = thumb_subdirs[0];
  thumb_subdirs[0]  = g_build_filename (thumb_fail_subdir, creator, NULL);

  gimp_thumb_initialized = TRUE;

  return gimp_thumb_initialized;
}

132 133 134 135 136 137 138 139 140 141 142
/**
 * gimp_thumb_get_thumb_dir:
 * @size: a GimpThumbSize
 *
 * Retrieve the name of a thumbnail folder for a specific size. The
 * returned pointer will become invalid if gimp_thumb_init() is used
 * again. It must not be changed or freed.
 *
 * Return value: the thumbnail directory in the encoding of the filesystem
 **/
const gchar *
143 144 145 146 147 148 149 150
gimp_thumb_get_thumb_dir (GimpThumbSize  size)
{
  g_return_val_if_fail (gimp_thumb_initialized, FALSE);

  size = gimp_thumb_size (size);

  return thumb_subdirs[size];
}
151

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
/**
 * gimp_thumb_ensure_thumb_dir:
 * @size: a GimpThumbSize
 * @error: return location for possible errors
 *
 * This function checks if the directory that is required to store
 * thumbnails for a particular @size exist and attempts to create it
 * if necessary.
 *
 * You shouldn't have to call this function directly since
 * gimp_thumbnail_save_thumb() and gimp_thumbnail_save_failure() will
 * do this for you.
 *
 * Return value: %TRUE is the directory exists, %FALSE if it could not
 *               be created
 **/
168
gboolean
169 170
gimp_thumb_ensure_thumb_dir (GimpThumbSize   size,
                             GError        **error)
171 172 173 174 175 176
{
  gint i;

  g_return_val_if_fail (gimp_thumb_initialized, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

177 178 179 180 181 182 183
  i = gimp_thumb_size (size);

  if (g_file_test (thumb_subdirs[i], G_FILE_TEST_IS_DIR))
    return TRUE;

  if (g_file_test (thumb_dir, G_FILE_TEST_IS_DIR) ||
      (mkdir (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0))
184
    {
185 186 187 188
      if (i == 0)
        mkdir (thumb_fail_subdir, S_IRUSR | S_IWUSR | S_IXUSR);

      mkdir (thumb_subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR);
189 190
    }

191 192 193 194 195 196 197 198
  if (g_file_test (thumb_subdirs[i], G_FILE_TEST_IS_DIR))
    return TRUE;

  g_set_error (error,
               GIMP_THUMB_ERROR, GIMP_THUMB_ERROR_MKDIR,
               _("Failed to create thumbnail folder '%s'."),
               thumb_subdirs[i]);
  return FALSE;
199 200
}

201 202 203 204 205 206 207 208 209 210 211 212
/**
 * gimp_thumb_name_from_uri:
 * @uri: an escaped URI in UTF-8 encoding
 * @size: a #GimpThumbSize
 *
 * Creates the name of the thumbnail file of the specified @size that
 * belongs to an image file located at the given @uri.
 *
 * Return value: a newly allocated filename in the encoding of the
 *               filesystem or %NULL if you attempt to create thumbnails
 *               for files in the thumbnail directory.
 **/
213
gchar *
214 215
gimp_thumb_name_from_uri (const gchar   *uri,
                          GimpThumbSize  size)
216
{
217
  gint i;
218 219

  g_return_val_if_fail (gimp_thumb_initialized, NULL);
220
  g_return_val_if_fail (uri != NULL, NULL);
221

222
  if (strstr (uri, thumb_dir))
223 224
    return NULL;

225
  i = gimp_thumb_size (size);
226

227
  return g_build_filename (thumb_subdirs[i], gimp_thumb_png_name (uri), NULL);
228 229
}

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
/**
 * gimp_thumb_find_thumb:
 * @uri: an escaped URI in UTF-8 encoding
 * @size: pointer to a #GimpThumbSize
 *
 * This function attempts to locate a thumbnail for the given
 * @url. First it tries the size that is stored at @size. If no
 * thumbnail of that size is found, it will look for a larger
 * thumbnail, then falling back to a smaller size. If a thumbnail is
 * found, it's size is written to the variable pointer to by @size
 * and the file location is returned.
 *
 * Return value: a newly allocated string in the encoding of the
 *               filesystem or %NULL if no thumbnail for @uri was found
 **/
245
gchar *
Sven Neumann's avatar
Sven Neumann committed
246 247
gimp_thumb_find_thumb (const gchar   *uri,
                       GimpThumbSize *size)
248 249 250 251 252 253
{
  const gchar *name;
  gchar       *thumb_name;
  gint         i, n;

  g_return_val_if_fail (gimp_thumb_initialized, NULL);
254 255
  g_return_val_if_fail (uri != NULL, NULL);
  g_return_val_if_fail (size != NULL, NULL);
256
  g_return_val_if_fail (*size > GIMP_THUMB_SIZE_FAIL, NULL);
257 258 259

  name = gimp_thumb_png_name (uri);

260
  i = n = gimp_thumb_size (*size);
261 262 263 264 265

  for (; i < thumb_num_sizes; i++)
    {
      thumb_name = g_build_filename (thumb_subdirs[i], name, NULL);

266
      if (gimp_thumb_file_test (thumb_name, NULL, NULL, NULL) ==
267
          GIMP_THUMB_FILE_TYPE_REGULAR)
268 269 270 271 272 273 274 275 276 277 278 279
        {
          *size = thumb_sizes[i];
          return thumb_name;
        }

      g_free (thumb_name);
    }

  for (i = n - 1; i >= 0; i--)
    {
      thumb_name = g_build_filename (thumb_subdirs[i], name, NULL);

280
      if (gimp_thumb_file_test (thumb_name, NULL, NULL, NULL) ==
281
          GIMP_THUMB_FILE_TYPE_REGULAR)
282 283 284 285 286 287 288 289 290 291 292
        {
          *size = thumb_sizes[i];
          return thumb_name;
        }

      g_free (thumb_name);
    }

  return NULL;
}

293 294 295 296 297
/**
 * gimp_thumb_file_test:
 * @filename: a filename in the encoding of the filesystem
 * @mtime: return location for modification time
 * @size: return location for file size
298
 * @err_no: return location for system "errno"
299 300 301 302 303
 *
 * This is a convenience and portability wrapper around stat(). It
 * checks if the given @filename exists and returns modification time
 * and file size in 64bit integer values.
 *
304 305
 * Return value: The type of the file, or #GIMP_THUMB_FILE_TYPE_NONE if
 *               the file doesn't exist.
306
 **/
307
GimpThumbFileType
308 309
gimp_thumb_file_test (const gchar *filename,
                      gint64      *mtime,
310 311
                      gint64      *size,
                      gint        *err_no)
312 313 314
{
  struct stat s;

315 316
  g_return_val_if_fail (filename != NULL, FALSE);

317
  if (stat (filename, &s) == 0)
318
    {
319 320 321
      if (mtime)  *mtime  = s.st_mtime;
      if (size)   *size   = s.st_size;
      if (err_no) *err_no = 0;
322 323 324 325 326 327 328 329 330

      if (S_ISREG (s.st_mode))
        {
          return GIMP_THUMB_FILE_TYPE_REGULAR;
        }
      else if (S_ISDIR (s.st_mode))
        {
          return GIMP_THUMB_FILE_TYPE_FOLDER;
        }
331

332
      return GIMP_THUMB_FILE_TYPE_SPECIAL;
333 334
    }

335 336 337
  if (mtime)  *mtime  = 0;
  if (size)   *size   = 0;
  if (err_no) *err_no = errno;
338 339

  return GIMP_THUMB_FILE_TYPE_NONE;
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
}

static void
gimp_thumb_exit (void)
{
  gint i;

  g_free (thumb_dir);
  g_free (thumb_sizes);
  for (i = 0; i < thumb_num_sizes; i++)
    g_free (thumb_subdirs[i]);
  g_free (thumb_subdirs);
  g_free (thumb_fail_subdir);

  thumb_num_sizes        = 0;
  thumb_sizes            = NULL;
  thumb_dir              = NULL;
  thumb_subdirs          = NULL;
  thumb_fail_subdir      = NULL;
  gimp_thumb_initialized = FALSE;
}

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
static gint
gimp_thumb_size (GimpThumbSize size)
{
  gint i = 0;

  if (size > GIMP_THUMB_SIZE_FAIL)
    {
      for (i = 1;
           i < thumb_num_sizes && thumb_sizes[i] < size;
           i++)
        /* nothing */;

      if (i == thumb_num_sizes)
        i--;
    }

  return i;
}

381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
static const gchar *
gimp_thumb_png_name (const gchar *uri)
{
  static gchar name[40];
  guchar       digest[16];
  guchar       n;
  gint         i;

  gimp_md5_get_digest (uri, -1, digest);

  for (i = 0; i < 16; i++)
    {
      n = (digest[i] >> 4) & 0xF;
      name[i * 2]     = (n > 9) ? 'a' + n - 10 : '0' + n;

      n = digest[i] & 0xF;
      name[i * 2 + 1] = (n > 9) ? 'a' + n - 10 : '0' + n;
    }

  strncpy (name + 32, ".png", 5);

  return (const gchar *) name;
}