Merged pidgin/main into default
[pidgin-git.git] / libpurple / image.c
blob73663e573880ede8f932522dd12629fb2ec1ea3d
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "debug.h"
26 #include "image.h"
28 #define PURPLE_IMAGE_GET_PRIVATE(obj) \
29 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_IMAGE, PurpleImagePrivate))
31 typedef struct {
32 gchar *path;
34 GBytes *contents;
36 const gchar *extension;
37 const gchar *mime;
38 gchar *gen_filename;
39 gchar *friendly_filename;
40 } PurpleImagePrivate;
42 enum {
43 PROP_0,
44 PROP_PATH,
45 PROP_CONTENTS,
46 PROP_SIZE,
47 PROP_LAST
50 static GParamSpec *properties[PROP_LAST];
52 /******************************************************************************
53 * Helpers
54 ******************************************************************************/
55 static void
56 _purple_image_set_path(PurpleImage *image, const gchar *path) {
57 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
59 g_free(priv->path);
61 priv->path = g_strdup(path);
64 static void
65 _purple_image_set_contents(PurpleImage *image, GBytes *bytes) {
66 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
68 if(priv->contents)
69 g_bytes_unref(priv->contents);
71 priv->contents = (bytes) ? g_bytes_ref(bytes) : NULL;
74 /******************************************************************************
75 * Object stuff
76 ******************************************************************************/
77 G_DEFINE_TYPE_WITH_PRIVATE(PurpleImage, purple_image, G_TYPE_OBJECT);
79 static void
80 purple_image_init(PurpleImage *image) {
83 static void
84 purple_image_finalize(GObject *obj) {
85 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(obj);
87 if(priv->contents)
88 g_bytes_unref(priv->contents);
90 g_free(priv->path);
91 g_free(priv->gen_filename);
92 g_free(priv->friendly_filename);
94 G_OBJECT_CLASS(purple_image_parent_class)->finalize(obj);
97 static void
98 purple_image_set_property(GObject *obj, guint param_id,
99 const GValue *value, GParamSpec *pspec)
101 PurpleImage *image = PURPLE_IMAGE(obj);
103 switch (param_id) {
104 case PROP_PATH:
105 _purple_image_set_path(image, g_value_get_string(value));
106 break;
107 case PROP_CONTENTS:
108 _purple_image_set_contents(image, g_value_get_boxed(value));
109 break;
110 default:
111 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
112 break;
116 static void
117 purple_image_get_property(GObject *obj, guint param_id, GValue *value,
118 GParamSpec *pspec)
120 PurpleImage *image = PURPLE_IMAGE(obj);
122 switch (param_id) {
123 case PROP_PATH:
124 g_value_set_string(value, purple_image_get_path(image));
125 break;
126 case PROP_CONTENTS:
127 g_value_set_boxed(value, purple_image_get_contents(image));
128 break;
129 case PROP_SIZE:
130 g_value_set_uint64(value, purple_image_get_data_size(image));
131 break;
132 default:
133 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
134 break;
138 static void
139 purple_image_class_init(PurpleImageClass *klass) {
140 GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
142 gobj_class->finalize = purple_image_finalize;
143 gobj_class->get_property = purple_image_get_property;
144 gobj_class->set_property = purple_image_set_property;
146 properties[PROP_PATH] = g_param_spec_string(
147 "path",
148 "path",
149 "The filepath for the image if one was provided",
150 NULL,
151 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
154 properties[PROP_CONTENTS] = g_param_spec_boxed(
155 "contents",
156 "contents",
157 "The contents of the image stored in a GBytes",
158 G_TYPE_BYTES,
159 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
162 properties[PROP_SIZE] = g_param_spec_uint64(
163 "size",
164 "size",
165 "The size of the image in bytes",
167 G_MAXUINT64,
169 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS
172 g_object_class_install_properties(gobj_class, PROP_LAST, properties);
175 /******************************************************************************
176 * API
177 ******************************************************************************/
178 PurpleImage *
179 purple_image_new_from_bytes(GBytes *bytes) {
180 return g_object_new(
181 PURPLE_TYPE_IMAGE,
182 "contents", bytes,
183 NULL
187 PurpleImage *
188 purple_image_new_from_file(const gchar *path, GError **error) {
189 PurpleImage *image = NULL;
190 GBytes *bytes = NULL;
191 gchar *contents = NULL;
192 gsize length = 0;
194 if(!g_file_get_contents(path, &contents, &length, error)) {
195 return NULL;
198 bytes = g_bytes_new_take(contents, length);
200 image = g_object_new(
201 PURPLE_TYPE_IMAGE,
202 "contents", bytes,
203 "path", path,
204 NULL
207 g_bytes_unref(bytes);
209 return image;
212 PurpleImage *
213 purple_image_new_from_data(const guint8 *data, gsize length) {
214 PurpleImage *image;
215 GBytes *bytes = NULL;
217 bytes = g_bytes_new(data, length);
219 image = purple_image_new_from_bytes(bytes);
221 g_bytes_unref(bytes);
223 return image;
226 PurpleImage *
227 purple_image_new_take_data(guint8 *data, gsize length) {
228 PurpleImage *image;
229 GBytes *bytes = NULL;
231 bytes = g_bytes_new_take(data, length);
233 image = purple_image_new_from_bytes(bytes);
235 g_bytes_unref(bytes);
237 return image;
240 gboolean
241 purple_image_save(PurpleImage *image, const gchar *path) {
242 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
243 gconstpointer data;
244 gsize len;
245 gboolean succ;
247 g_return_val_if_fail(priv != NULL, FALSE);
248 g_return_val_if_fail(path != NULL, FALSE);
249 g_return_val_if_fail(path[0] != '\0', FALSE);
251 data = purple_image_get_data(image);
252 len = purple_image_get_data_size(image);
254 g_return_val_if_fail(data != NULL, FALSE);
255 g_return_val_if_fail(len > 0, FALSE);
257 succ = purple_util_write_data_to_file_absolute(path, data, len);
258 if (succ && priv->path == NULL)
259 priv->path = g_strdup(path);
261 return succ;
264 GBytes *
265 purple_image_get_contents(const PurpleImage *image) {
266 PurpleImagePrivate *priv = NULL;
268 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
270 priv = PURPLE_IMAGE_GET_PRIVATE(image);
272 if(priv->contents)
273 return g_bytes_ref(priv->contents);
275 return NULL;
278 const gchar *
279 purple_image_get_path(PurpleImage *image) {
280 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
282 g_return_val_if_fail(priv != NULL, NULL);
284 return priv->path ? priv->path : purple_image_generate_filename(image);
287 gsize
288 purple_image_get_data_size(PurpleImage *image) {
289 PurpleImagePrivate *priv;
291 g_return_val_if_fail(PURPLE_IS_IMAGE(image), 0);
293 priv = PURPLE_IMAGE_GET_PRIVATE(image);
295 if(priv->contents)
296 return g_bytes_get_size(priv->contents);
298 return 0;
301 gconstpointer
302 purple_image_get_data(PurpleImage *image) {
303 PurpleImagePrivate *priv = NULL;
305 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
307 priv = PURPLE_IMAGE_GET_PRIVATE(image);
309 if(priv->contents)
310 return g_bytes_get_data(priv->contents, NULL);
312 return NULL;
315 const gchar *
316 purple_image_get_extension(PurpleImage *image) {
317 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
318 gconstpointer data;
320 g_return_val_if_fail(priv != NULL, NULL);
322 if (priv->extension)
323 return priv->extension;
325 if (purple_image_get_data_size(image) < 4)
326 return NULL;
328 data = purple_image_get_data(image);
329 g_assert(data != NULL);
331 if (memcmp(data, "GIF8", 4) == 0)
332 return priv->extension = "gif";
333 if (memcmp(data, "\xff\xd8\xff", 3) == 0) /* 4th may be e0 through ef */
334 return priv->extension = "jpg";
335 if (memcmp(data, "\x89PNG", 4) == 0)
336 return priv->extension = "png";
337 if (memcmp(data, "MM", 2) == 0)
338 return priv->extension = "tif";
339 if (memcmp(data, "II", 2) == 0)
340 return priv->extension = "tif";
341 if (memcmp(data, "BM", 2) == 0)
342 return priv->extension = "bmp";
343 if (memcmp(data, "\x00\x00\x01\x00", 4) == 0)
344 return priv->extension = "ico";
346 return NULL;
349 const gchar *
350 purple_image_get_mimetype(PurpleImage *image) {
351 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
352 const gchar *ext = purple_image_get_extension(image);
354 g_return_val_if_fail(priv != NULL, NULL);
356 if (priv->mime)
357 return priv->mime;
359 g_return_val_if_fail(ext != NULL, NULL);
361 if (g_strcmp0(ext, "gif") == 0)
362 return priv->mime = "image/gif";
363 if (g_strcmp0(ext, "jpg") == 0)
364 return priv->mime = "image/jpeg";
365 if (g_strcmp0(ext, "png") == 0)
366 return priv->mime = "image/png";
367 if (g_strcmp0(ext, "tif") == 0)
368 return priv->mime = "image/tiff";
369 if (g_strcmp0(ext, "bmp") == 0)
370 return priv->mime = "image/bmp";
371 if (g_strcmp0(ext, "ico") == 0)
372 return priv->mime = "image/vnd.microsoft.icon";
374 return NULL;
377 const gchar *
378 purple_image_generate_filename(PurpleImage *image) {
379 PurpleImagePrivate *priv = NULL;
380 gconstpointer data;
381 gsize len;
382 const gchar *ext = NULL;
383 gchar *checksum;
385 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
387 priv = PURPLE_IMAGE_GET_PRIVATE(image);
389 if (priv->gen_filename)
390 return priv->gen_filename;
392 /* grab the image's data and size of that data */
393 data = purple_image_get_data(image);
394 len = purple_image_get_data_size(image);
396 /* create a checksum of it and use it as the start of our filename */
397 checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, len);
399 /* if the image has a known format, set the extension appropriately */
400 ext = purple_image_get_extension(image);
401 if(ext != NULL) {
402 priv->gen_filename = g_strdup_printf("%s.%s", checksum, ext);
403 g_free(checksum);
404 } else {
405 priv->gen_filename = checksum;
408 return priv->gen_filename;
411 void
412 purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename) {
413 PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
414 gchar *newname;
415 const gchar *escaped;
417 g_return_if_fail(priv != NULL);
419 newname = g_path_get_basename(filename);
420 escaped = purple_escape_filename(newname);
421 g_free(newname);
422 newname = NULL;
424 if (g_strcmp0(escaped, "") == 0 || g_strcmp0(escaped, ".") == 0 ||
425 g_strcmp0(escaped, G_DIR_SEPARATOR_S) == 0 ||
426 g_strcmp0(escaped, "/") == 0 || g_strcmp0(escaped, "\\") == 0)
428 escaped = NULL;
431 g_free(priv->friendly_filename);
432 priv->friendly_filename = g_strdup(escaped);
435 const gchar *
436 purple_image_get_friendly_filename(PurpleImage *image) {
437 PurpleImagePrivate *priv = NULL;
439 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
441 priv = PURPLE_IMAGE_GET_PRIVATE(image);
443 if(priv->friendly_filename) {
444 return priv->friendly_filename;
447 return purple_image_generate_filename(image);