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
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
23 #include "glibcompat.h"
28 #define PURPLE_IMAGE_GET_PRIVATE(obj) \
29 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_IMAGE, PurpleImagePrivate))
35 const gchar
*extension
;
38 gchar
*friendly_filename
;
59 static GObjectClass
*parent_class
;
60 static guint signals
[SIG_LAST
];
61 static GParamSpec
*properties
[PROP_LAST
];
63 /******************************************************************************
65 ******************************************************************************/
68 has_failed(PurpleImage
*image
)
70 gboolean ready_changed
;
71 PurpleImagePrivate
*priv
;
72 priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
74 g_return_if_fail(!priv
->has_failed
);
76 priv
->has_failed
= TRUE
;
77 ready_changed
= (priv
->is_ready
!= FALSE
);
78 priv
->is_ready
= FALSE
;
81 g_string_free(priv
->contents
, TRUE
);
82 priv
->contents
= NULL
;
86 g_object_notify_by_pspec(G_OBJECT(image
),
87 properties
[PROP_IS_READY
]);
89 g_object_notify_by_pspec(G_OBJECT(image
), properties
[PROP_HAS_FAILED
]);
90 g_signal_emit(image
, signals
[SIG_FAILED
], 0);
94 became_ready(PurpleImage
*image
)
96 PurpleImagePrivate
*priv
;
97 priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
99 g_return_if_fail(!priv
->has_failed
);
100 g_return_if_fail(!priv
->is_ready
);
102 priv
->is_ready
= TRUE
;
104 g_object_notify_by_pspec(G_OBJECT(image
), properties
[PROP_IS_READY
]);
105 g_signal_emit(image
, signals
[SIG_READY
], 0);
109 steal_contents(PurpleImagePrivate
*priv
, gpointer data
, gsize length
)
111 g_return_if_fail(priv
!= NULL
);
112 g_return_if_fail(priv
->contents
== NULL
);
113 g_return_if_fail(data
!= NULL
);
114 g_return_if_fail(length
> 0);
116 priv
->contents
= g_string_new(NULL
);
117 g_free(priv
->contents
->str
);
118 priv
->contents
->str
= data
;
119 priv
->contents
->len
= priv
->contents
->allocated_len
= length
;
123 fill_data(PurpleImage
*image
)
125 PurpleImagePrivate
*priv
;
126 GError
*error
= NULL
;
130 priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
137 g_return_if_fail(priv
->path
);
138 (void)g_file_get_contents(priv
->path
, &contents
, &length
, &error
);
140 purple_debug_error("image", "failed to read '%s' image: %s",
141 priv
->path
, error
->message
);
148 steal_contents(priv
, contents
, length
);
152 /******************************************************************************
154 ******************************************************************************/
157 purple_image_new_from_file(const gchar
*path
, gboolean be_eager
)
159 PurpleImagePrivate
*priv
;
162 g_return_val_if_fail(path
!= NULL
, NULL
);
163 g_return_val_if_fail(g_file_test(path
, G_FILE_TEST_EXISTS
), NULL
);
165 img
= g_object_new(PURPLE_TYPE_IMAGE
, NULL
);
166 purple_image_set_friendly_filename(img
, path
);
167 priv
= PURPLE_IMAGE_GET_PRIVATE(img
);
168 priv
->path
= g_strdup(path
);
172 if (!priv
->contents
) {
177 g_assert(priv
->is_ready
&& !priv
->has_failed
);
184 purple_image_new_from_data(gpointer data
, gsize length
)
187 PurpleImagePrivate
*priv
;
189 g_return_val_if_fail(data
!= NULL
, NULL
);
190 g_return_val_if_fail(length
> 0, NULL
);
192 img
= g_object_new(PURPLE_TYPE_IMAGE
, NULL
);
193 priv
= PURPLE_IMAGE_GET_PRIVATE(img
);
195 steal_contents(priv
, data
, length
);
201 purple_image_save(PurpleImage
*image
, const gchar
*path
)
203 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
208 g_return_val_if_fail(priv
!= NULL
, FALSE
);
209 g_return_val_if_fail(path
!= NULL
, FALSE
);
210 g_return_val_if_fail(path
[0] != '\0', FALSE
);
212 data
= purple_image_get_data(image
);
213 len
= purple_image_get_size(image
);
215 g_return_val_if_fail(data
!= NULL
, FALSE
);
216 g_return_val_if_fail(len
> 0, FALSE
);
218 succ
= purple_util_write_data_to_file_absolute(path
, data
, len
);
219 if (succ
&& priv
->path
== NULL
)
220 priv
->path
= g_strdup(path
);
226 purple_image_get_path(PurpleImage
*image
)
228 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
230 g_return_val_if_fail(priv
!= NULL
, NULL
);
236 purple_image_is_ready(PurpleImage
*image
)
238 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
240 g_return_val_if_fail(priv
!= NULL
, FALSE
);
242 return priv
->is_ready
;
246 purple_image_has_failed(PurpleImage
*image
)
248 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
250 g_return_val_if_fail(priv
!= NULL
, TRUE
);
252 return priv
->has_failed
;
256 purple_image_get_size(PurpleImage
*image
)
258 PurpleImagePrivate
*priv
;
259 priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
261 g_return_val_if_fail(priv
!= NULL
, 0);
262 g_return_val_if_fail(priv
->is_ready
, 0);
265 g_return_val_if_fail(priv
->contents
, 0);
267 return priv
->contents
->len
;
271 purple_image_get_data(PurpleImage
*image
)
273 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
275 g_return_val_if_fail(priv
!= NULL
, NULL
);
276 g_return_val_if_fail(priv
->is_ready
, NULL
);
279 g_return_val_if_fail(priv
->contents
, NULL
);
281 return priv
->contents
->str
;
285 purple_image_get_extension(PurpleImage
*image
)
287 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
290 g_return_val_if_fail(priv
!= NULL
, NULL
);
293 return priv
->extension
;
295 if (purple_image_get_size(image
) < 4)
298 data
= purple_image_get_data(image
);
299 g_assert(data
!= NULL
);
301 if (memcmp(data
, "GIF8", 4) == 0)
302 return priv
->extension
= "gif";
303 if (memcmp(data
, "\xff\xd8\xff", 3) == 0) /* 4th may be e0 through ef */
304 return priv
->extension
= "jpg";
305 if (memcmp(data
, "\x89PNG", 4) == 0)
306 return priv
->extension
= "png";
307 if (memcmp(data
, "MM", 2) == 0)
308 return priv
->extension
= "tif";
309 if (memcmp(data
, "II", 2) == 0)
310 return priv
->extension
= "tif";
311 if (memcmp(data
, "BM", 2) == 0)
312 return priv
->extension
= "bmp";
313 if (memcmp(data
, "\x00\x00\x01\x00", 4) == 0)
314 return priv
->extension
= "ico";
320 purple_image_get_mimetype(PurpleImage
*image
)
322 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
323 const gchar
*ext
= purple_image_get_extension(image
);
325 g_return_val_if_fail(priv
!= NULL
, NULL
);
330 g_return_val_if_fail(ext
!= NULL
, NULL
);
332 if (g_strcmp0(ext
, "gif") == 0)
333 return priv
->mime
= "image/gif";
334 if (g_strcmp0(ext
, "jpg") == 0)
335 return priv
->mime
= "image/jpeg";
336 if (g_strcmp0(ext
, "png") == 0)
337 return priv
->mime
= "image/png";
338 if (g_strcmp0(ext
, "tif") == 0)
339 return priv
->mime
= "image/tiff";
340 if (g_strcmp0(ext
, "bmp") == 0)
341 return priv
->mime
= "image/bmp";
342 if (g_strcmp0(ext
, "ico") == 0)
343 return priv
->mime
= "image/vnd.microsoft.icon";
349 purple_image_generate_filename(PurpleImage
*image
)
351 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
357 g_return_val_if_fail(priv
!= NULL
, NULL
);
359 if (priv
->gen_filename
)
360 return priv
->gen_filename
;
362 ext
= purple_image_get_extension(image
);
363 data
= purple_image_get_data(image
);
364 len
= purple_image_get_size(image
);
366 g_return_val_if_fail(ext
!= NULL
, NULL
);
367 g_return_val_if_fail(data
!= NULL
, NULL
);
368 g_return_val_if_fail(len
> 0, NULL
);
370 checksum
= g_compute_checksum_for_data(G_CHECKSUM_SHA1
, data
, len
);
371 priv
->gen_filename
= g_strdup_printf("%s.%s", checksum
, ext
);
374 return priv
->gen_filename
;
378 purple_image_set_friendly_filename(PurpleImage
*image
, const gchar
*filename
)
380 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
382 const gchar
*escaped
;
384 g_return_if_fail(priv
!= NULL
);
386 newname
= g_path_get_basename(filename
);
387 escaped
= purple_escape_filename(newname
);
391 if (g_strcmp0(escaped
, "") == 0 || g_strcmp0(escaped
, ".") == 0 ||
392 g_strcmp0(escaped
, G_DIR_SEPARATOR_S
) == 0 ||
393 g_strcmp0(escaped
, "/") == 0 || g_strcmp0(escaped
, "\\") == 0)
398 g_free(priv
->friendly_filename
);
399 priv
->friendly_filename
= g_strdup(escaped
);
403 purple_image_get_friendly_filename(PurpleImage
*image
)
405 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
407 g_return_val_if_fail(priv
!= NULL
, NULL
);
409 if (G_UNLIKELY(!priv
->friendly_filename
)) {
410 const gchar
*newname
= purple_image_generate_filename(image
);
411 gsize newname_len
= strlen(newname
);
413 if (newname_len
< 10)
416 /* let's use last 6 characters from checksum + 4 characters
418 newname
+= newname_len
- 10;
419 priv
->friendly_filename
= g_strdup(newname
);
422 if (G_UNLIKELY(priv
->is_ready
&&
423 strchr(priv
->friendly_filename
, '.') == NULL
))
425 const gchar
*ext
= purple_image_get_extension(image
);
428 return priv
->friendly_filename
;
430 tmp
= g_strdup_printf("%s.%s", priv
->friendly_filename
, ext
);
431 g_free(priv
->friendly_filename
);
432 priv
->friendly_filename
= tmp
;
435 return priv
->friendly_filename
;
439 purple_image_transfer_new(void)
442 PurpleImagePrivate
*priv
;
444 img
= g_object_new(PURPLE_TYPE_IMAGE
, NULL
);
445 priv
= PURPLE_IMAGE_GET_PRIVATE(img
);
447 priv
->is_ready
= FALSE
;
448 priv
->contents
= g_string_new(NULL
);
454 purple_image_transfer_write(PurpleImage
*image
, gconstpointer data
,
457 PurpleImagePrivate
*priv
=
458 PURPLE_IMAGE_GET_PRIVATE(image
);
460 g_return_if_fail(priv
!= NULL
);
461 g_return_if_fail(!priv
->has_failed
);
462 g_return_if_fail(!priv
->is_ready
);
463 g_return_if_fail(priv
->contents
!= NULL
);
464 g_return_if_fail(data
!= NULL
|| length
== 0);
469 g_string_append_len(priv
->contents
, (const gchar
*)data
, length
);
473 purple_image_transfer_close(PurpleImage
*image
)
475 PurpleImagePrivate
*priv
=
476 PURPLE_IMAGE_GET_PRIVATE(image
);
478 g_return_if_fail(priv
!= NULL
);
479 g_return_if_fail(!priv
->has_failed
);
480 g_return_if_fail(!priv
->is_ready
);
481 g_return_if_fail(priv
->contents
!= NULL
);
483 if (priv
->contents
->len
== 0) {
484 purple_debug_error("image", "image is empty");
493 purple_image_transfer_failed(PurpleImage
*image
)
495 PurpleImagePrivate
*priv
=
496 PURPLE_IMAGE_GET_PRIVATE(image
);
498 g_return_if_fail(priv
!= NULL
);
499 g_return_if_fail(!priv
->has_failed
);
500 g_return_if_fail(!priv
->is_ready
);
505 /******************************************************************************
507 ******************************************************************************/
510 purple_image_init(GTypeInstance
*instance
, gpointer klass
)
512 PurpleImage
*image
= PURPLE_IMAGE(instance
);
513 PurpleImagePrivate
*priv
=
514 PURPLE_IMAGE_GET_PRIVATE(image
);
516 priv
->contents
= NULL
;
517 priv
->is_ready
= TRUE
;
518 priv
->has_failed
= FALSE
;
522 purple_image_finalize(GObject
*obj
)
524 PurpleImage
*image
= PURPLE_IMAGE(obj
);
525 PurpleImagePrivate
*priv
=
526 PURPLE_IMAGE_GET_PRIVATE(image
);
529 g_string_free(priv
->contents
, TRUE
);
531 g_free(priv
->gen_filename
);
532 g_free(priv
->friendly_filename
);
534 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
538 purple_image_get_property(GObject
*object
, guint par_id
, GValue
*value
,
541 PurpleImage
*image
= PURPLE_IMAGE(object
);
542 PurpleImagePrivate
*priv
= PURPLE_IMAGE_GET_PRIVATE(image
);
546 g_value_set_boolean(value
, priv
->is_ready
);
548 case PROP_HAS_FAILED
:
549 g_value_set_boolean(value
, priv
->has_failed
);
552 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, par_id
, pspec
);
558 purple_image_class_init(PurpleImageClass
*klass
)
560 GObjectClass
*gobj_class
= G_OBJECT_CLASS(klass
);
562 parent_class
= g_type_class_peek_parent(klass
);
564 g_type_class_add_private(klass
, sizeof(PurpleImagePrivate
));
566 gobj_class
->finalize
= purple_image_finalize
;
567 gobj_class
->get_property
= purple_image_get_property
;
569 properties
[PROP_IS_READY
] = g_param_spec_boolean("is-ready",
570 "Is ready", "The image is ready to be displayed. Image may "
571 "change the state to failed in a single case: if it's backed "
572 "by a file and that file fails to load",
573 TRUE
, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
575 properties
[PROP_HAS_FAILED
] = g_param_spec_boolean("has-failed",
576 "Has hailed", "The remote host has failed to send the image",
577 FALSE
, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
579 g_object_class_install_properties(gobj_class
, PROP_LAST
, properties
);
582 * PurpleImage::failed:
583 * @image: a image that failed to transfer.
585 * Called when a @image fails to be transferred. It's guaranteed to be
586 * fired at most once for a particular @image.
588 signals
[SIG_FAILED
] = g_signal_new("failed", G_OBJECT_CLASS_TYPE(klass
),
590 g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
593 * PurpleImage::ready:
594 * @image: a image that became ready.
596 * Called when a @image becames ready to be displayed. It's guaranteed to be
597 * fired at most once for a particular @image.
599 signals
[SIG_READY
] = g_signal_new("ready", G_OBJECT_CLASS_TYPE(klass
),
601 g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
605 purple_image_get_type(void)
607 static GType type
= 0;
609 if (G_UNLIKELY(type
== 0)) {
610 static const GTypeInfo info
= {
611 .class_size
= sizeof(PurpleImageClass
),
612 .class_init
= (GClassInitFunc
)purple_image_class_init
,
613 .instance_size
= sizeof(PurpleImage
),
614 .instance_init
= purple_image_init
,
617 type
= g_type_register_static(G_TYPE_OBJECT
,
618 "PurpleImage", &info
, 0);