comparison libpurple/smiley.c @ 23142:dea8b856466e

propagate from branch 'im.pidgin.pidgin.custom_smiley' (head c134ff23eba5faac09c13e731e792fa612c91a9a) to branch 'im.pidgin.pidgin.next.minor' (head 4d2d20241c7dac5915e142f0aa9811c9eab40111)
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Mon, 12 May 2008 23:17:48 +0000
parents 53b3c75c9e26
children f1df88964b3d
comparison
equal deleted inserted replaced
23114:d53f72735830 23142:dea8b856466e
1 /**
2 * @file smiley.c Simley API
3 * @ingroup core
4 */
5
6 /* purple
7 *
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 */
26
27 #include "internal.h"
28 #include "xmlnode.h"
29 #include "debug.h"
30 #include "imgstore.h"
31 #include "smiley.h"
32 #include "util.h"
33
34 /**************************************************************************/
35 /* Main structures, members and constants */
36 /**************************************************************************/
37
38 struct _PurpleSmiley
39 {
40 GObject parent;
41 PurpleStoredImage *img; /**< The id of the stored image with the
42 the smiley data. */
43 char *shortcut; /**< Shortcut associated with the custom
44 smiley. This field will work as a
45 unique key by this API. */
46 char *checksum; /**< The smiley checksum. */
47 };
48
49 struct _PurpleSmileyClass
50 {
51 GObjectClass parent_class;
52 };
53
54 static GHashTable *smiley_shortcut_index = NULL; /* shortcut (char *) => smiley (PurpleSmiley*) */
55 static GHashTable *smiley_checksum_index = NULL; /* checksum (char *) => smiley (PurpleSmiley*) */
56
57 static guint save_timer = 0;
58 static gboolean smileys_loaded = FALSE;
59 static char *smileys_dir = NULL;
60
61 #define SMILEYS_DEFAULT_FOLDER "custom_smiley"
62 #define SMILEYS_LOG_ID "smileys"
63
64 #define XML_FILE_NAME "smileys.xml"
65
66 #define XML_ROOT_TAG "smileys"
67 #define XML_PROFILE_TAG "profile"
68 #define XML_PROFILE_NAME_ATTRIB_TAG "name"
69 #define XML_ACCOUNT_TAG "account"
70 #define XML_ACCOUNT_USERID_ATTRIB_TAG "userid"
71 #define XML_SMILEY_SET_TAG "smiley_set"
72 #define XML_SMILEY_TAG "smiley"
73 #define XML_SHORTCUT_ATTRIB_TAG "shortcut"
74 #define XML_CHECKSUM_ATRIB_TAG "checksum"
75 #define XML_FILENAME_ATRIB_TAG "filename"
76
77
78 /******************************************************************************
79 * XML descriptor file layout *
80 ******************************************************************************
81 *
82 * Althought we are creating the profile XML structure here, now we
83 * won't handle it.
84 * So, we just add one profile named "default" that has no associated
85 * account elements, and have only the smiley_set that will contain
86 * all existent custom smiley.
87 *
88 * It's our "Highlander Profile" :-)
89 *
90 ******************************************************************************
91 *
92 * <smileys>
93 * <profile name="john.doe">
94 * <account userid="john.doe@jabber.org">
95 * <account userid="john.doe@gmail.com">
96 * <smiley_set>
97 * <smiley shortcut="aaa" checksum="xxxxxxxx" filename="file_name1.gif"/>
98 * <smiley shortcut="bbb" checksum="yyyyyyy" filename="file_name2.gif"/>
99 * </smiley_set>
100 * </profile>
101 * </smiley>
102 *
103 *****************************************************************************/
104
105
106 /*********************************************************************
107 * Forward declarations *
108 *********************************************************************/
109
110 static gboolean read_smiley_file(const char *path, guchar **data, size_t *len);
111
112 static char *get_file_full_path(const char *filename);
113
114 static PurpleSmiley *purple_smiley_create(const char *shortcut);
115
116 static PurpleSmiley *purple_smiley_load_file(const char *shortcut, const char *checksum,
117 const char *filename);
118
119 static void
120 purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
121 size_t smiley_data_len, const char *filename);
122
123 static void
124 purple_smiley_data_store(PurpleStoredImage *stored_img);
125
126 static void
127 purple_smiley_data_unstore(const char *filename);
128
129 /*********************************************************************
130 * Writing to disk *
131 *********************************************************************/
132
133 static xmlnode *
134 smiley_to_xmlnode(PurpleSmiley *smiley)
135 {
136 xmlnode *smiley_node = NULL;
137
138 smiley_node = xmlnode_new(XML_SMILEY_TAG);
139
140 if (!smiley_node)
141 return NULL;
142
143 xmlnode_set_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG,
144 smiley->shortcut);
145
146 xmlnode_set_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG,
147 smiley->checksum);
148
149 xmlnode_set_attrib(smiley_node, XML_FILENAME_ATRIB_TAG,
150 purple_imgstore_get_filename(smiley->img));
151
152 return smiley_node;
153 }
154
155 static void
156 add_smiley_to_main_node(gpointer key, gpointer value, gpointer user_data)
157 {
158 xmlnode *child_node;
159
160 child_node = smiley_to_xmlnode(value);
161 xmlnode_insert_child((xmlnode*)user_data, child_node);
162 }
163
164 static xmlnode *
165 smileys_to_xmlnode()
166 {
167 xmlnode *root_node, *profile_node, *smileyset_node;
168
169 root_node = xmlnode_new(XML_ROOT_TAG);
170 xmlnode_set_attrib(root_node, "version", "1.0");
171
172 /* See the top comment's above to understand why initial tag elements
173 * are not being considered by now. */
174 profile_node = xmlnode_new(XML_PROFILE_TAG);
175 if (profile_node) {
176 xmlnode_set_attrib(profile_node, XML_PROFILE_NAME_ATTRIB_TAG, "Default");
177 xmlnode_insert_child(root_node, profile_node);
178
179 smileyset_node = xmlnode_new(XML_SMILEY_SET_TAG);
180 if (smileyset_node) {
181 xmlnode_insert_child(profile_node, smileyset_node);
182 g_hash_table_foreach(smiley_shortcut_index, add_smiley_to_main_node, smileyset_node);
183 }
184 }
185
186 return root_node;
187 }
188
189 static void
190 sync_smileys()
191 {
192 xmlnode *root_node;
193 char *data;
194
195 if (!smileys_loaded) {
196 purple_debug_error(SMILEYS_LOG_ID, "Attempted to save smileys before it "
197 "was read!\n");
198 return;
199 }
200
201 root_node = smileys_to_xmlnode();
202 data = xmlnode_to_formatted_str(root_node, NULL);
203 purple_util_write_data_to_file(XML_FILE_NAME, data, -1);
204
205 g_free(data);
206 xmlnode_free(root_node);
207 }
208
209 static gboolean
210 save_smileys_cb(gpointer data)
211 {
212 sync_smileys();
213 save_timer = 0;
214 return FALSE;
215 }
216
217 static void
218 purple_smileys_save()
219 {
220 if (save_timer == 0)
221 save_timer = purple_timeout_add_seconds(5, save_smileys_cb, NULL);
222 }
223
224
225 /*********************************************************************
226 * Reading from disk *
227 *********************************************************************/
228
229 static PurpleSmiley *
230 parse_smiley(xmlnode *smiley_node)
231 {
232 PurpleSmiley *smiley;
233 const char *shortcut = NULL;
234 const char *checksum = NULL;
235 const char *filename = NULL;
236
237 shortcut = xmlnode_get_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG);
238 checksum = xmlnode_get_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG);
239 filename = xmlnode_get_attrib(smiley_node, XML_FILENAME_ATRIB_TAG);
240
241 if ((shortcut == NULL) || (checksum == NULL) || (filename == NULL))
242 return NULL;
243
244 smiley = purple_smiley_load_file(shortcut, checksum, filename);
245
246 return smiley;
247 }
248
249 static void
250 purple_smileys_load()
251 {
252 xmlnode *root_node, *profile_node;
253 xmlnode *smileyset_node = NULL;
254 xmlnode *smiley_node;
255
256 smileys_loaded = TRUE;
257
258 root_node = purple_util_read_xml_from_file(XML_FILE_NAME,
259 _(SMILEYS_LOG_ID));
260
261 if (root_node == NULL)
262 return;
263
264 /* See the top comment's above to understand why initial tag elements
265 * are not being considered by now. */
266 profile_node = xmlnode_get_child(root_node, XML_PROFILE_TAG);
267 if (profile_node)
268 smileyset_node = xmlnode_get_child(profile_node, XML_SMILEY_SET_TAG);
269
270 if (smileyset_node) {
271 smiley_node = xmlnode_get_child(smileyset_node, XML_SMILEY_TAG);
272 for (; smiley_node != NULL;
273 smiley_node = xmlnode_get_next_twin(smiley_node)) {
274 PurpleSmiley *smiley;
275
276 smiley = parse_smiley(smiley_node);
277 }
278 }
279
280 xmlnode_free(root_node);
281 }
282
283 /*********************************************************************
284 * GObject Stuff *
285 *********************************************************************/
286 enum
287 {
288 PROP_0,
289 PROP_SHORTCUT,
290 PROP_IMGSTORE,
291 };
292
293 #define PROP_SHORTCUT_S "shortcut"
294 #define PROP_IMGSTORE_S "image"
295
296 enum
297 {
298 SIG_DESTROY,
299 SIG_LAST
300 };
301
302 static guint signals[SIG_LAST];
303 static GObjectClass *parent_class;
304
305 static void
306 purple_smiley_init(GTypeInstance *instance, gpointer klass)
307 {
308 }
309
310 static void
311 purple_smiley_get_property(GObject *object, guint param_id, GValue *value,
312 GParamSpec *spec)
313 {
314 PurpleSmiley *smiley = PURPLE_SMILEY(object);
315 switch (param_id) {
316 case PROP_SHORTCUT:
317 g_value_set_string(value, smiley->shortcut);
318 break;
319 case PROP_IMGSTORE:
320 g_value_set_pointer(value, smiley->img);
321 break;
322 default:
323 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec);
324 break;
325 }
326 }
327
328 static void
329 purple_smiley_set_property(GObject *object, guint param_id, const GValue *value,
330 GParamSpec *spec)
331 {
332 PurpleSmiley *smiley = PURPLE_SMILEY(object);
333 switch (param_id) {
334 case PROP_SHORTCUT:
335 {
336 const char *shortcut = g_value_get_string(value);
337 purple_smiley_set_shortcut(smiley, shortcut);
338 }
339 break;
340 case PROP_IMGSTORE:
341 {
342 PurpleStoredImage *img = g_value_get_pointer(value);
343
344 purple_imgstore_unref(smiley->img);
345 g_free(smiley->checksum);
346
347 smiley->img = img;
348 if (img) {
349 smiley->checksum = purple_util_get_image_checksum(
350 purple_imgstore_get_data(img),
351 purple_imgstore_get_size(img));
352 purple_smiley_data_store(img);
353 } else {
354 smiley->checksum = NULL;
355 }
356
357 g_object_notify(object, PROP_IMGSTORE_S);
358 }
359 break;
360 default:
361 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec);
362 break;
363 }
364 }
365
366 static void
367 purple_smiley_finalize(GObject *obj)
368 {
369 PurpleSmiley *smiley = PURPLE_SMILEY(obj);
370
371 if (g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut)) {
372 g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
373 g_hash_table_remove(smiley_checksum_index, smiley->checksum);
374 }
375
376 g_free(smiley->shortcut);
377 g_free(smiley->checksum);
378 if (smiley->img)
379 purple_smiley_data_unstore(purple_imgstore_get_filename(smiley->img));
380 purple_imgstore_unref(smiley->img);
381
382 purple_smileys_save();
383 }
384
385 static void
386 purple_smiley_dispose(GObject *gobj)
387 {
388 g_signal_emit(gobj, signals[SIG_DESTROY], 0);
389 parent_class->dispose(gobj);
390 }
391
392 static void
393 purple_smiley_class_init(PurpleSmileyClass *klass)
394 {
395 GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
396 GParamSpec *pspec;
397
398 parent_class = g_type_class_peek_parent(klass);
399
400 gobj_class->get_property = purple_smiley_get_property;
401 gobj_class->set_property = purple_smiley_set_property;
402 gobj_class->finalize = purple_smiley_finalize;
403 gobj_class->dispose = purple_smiley_dispose;
404
405 /* Shortcut */
406 pspec = g_param_spec_string(PROP_SHORTCUT_S, _("Shortcut"),
407 _("The text-shortcut for the smiley"),
408 NULL,
409 G_PARAM_READWRITE);
410 g_object_class_install_property(gobj_class, PROP_SHORTCUT, pspec);
411
412 /* Stored Image */
413 pspec = g_param_spec_pointer(PROP_IMGSTORE_S, _("Stored Image"),
414 _("Stored Image. (that'll have to do for now)"),
415 G_PARAM_READWRITE);
416 g_object_class_install_property(gobj_class, PROP_IMGSTORE, pspec);
417
418 signals[SIG_DESTROY] = g_signal_new("destroy",
419 G_OBJECT_CLASS_TYPE(klass),
420 G_SIGNAL_RUN_LAST,
421 0, NULL, NULL,
422 g_cclosure_marshal_VOID__VOID,
423 G_TYPE_NONE, 0);
424 }
425
426 GType
427 purple_smiley_get_type(void)
428 {
429 static GType type = 0;
430
431 if(type == 0) {
432 static const GTypeInfo info = {
433 sizeof(PurpleSmileyClass),
434 NULL,
435 NULL,
436 (GClassInitFunc)purple_smiley_class_init,
437 NULL,
438 NULL,
439 sizeof(PurpleSmiley),
440 0,
441 purple_smiley_init,
442 NULL,
443 };
444
445 type = g_type_register_static(G_TYPE_OBJECT,
446 "PurpleSmiley",
447 &info, 0);
448 }
449
450 return type;
451 }
452
453 /*********************************************************************
454 * Other Stuff *
455 *********************************************************************/
456
457 static char *get_file_full_path(const char *filename)
458 {
459 char *path;
460
461 path = g_build_filename(purple_smileys_get_storing_dir(), filename, NULL);
462
463 if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
464 g_free(path);
465 return NULL;
466 }
467
468 return path;
469 }
470
471 static PurpleSmiley *
472 purple_smiley_load_file(const char *shortcut, const char *checksum, const char *filename)
473 {
474 PurpleSmiley *smiley = NULL;
475 guchar *smiley_data;
476 size_t smiley_data_len;
477 char *fullpath = NULL;
478
479 g_return_val_if_fail(shortcut != NULL, NULL);
480 g_return_val_if_fail(checksum != NULL, NULL);
481 g_return_val_if_fail(filename != NULL, NULL);
482
483 fullpath = get_file_full_path(filename);
484 if (!fullpath)
485 return NULL;
486
487 smiley = purple_smiley_create(shortcut);
488 if (!smiley) {
489 g_free(fullpath);
490 return NULL;
491 }
492
493 smiley->checksum = g_strdup(checksum);
494
495 if (read_smiley_file(fullpath, &smiley_data, &smiley_data_len))
496 purple_smiley_set_data_impl(smiley, smiley_data,
497 smiley_data_len, filename);
498 else
499 purple_smiley_delete(smiley);
500
501 g_free(fullpath);
502
503 return smiley;
504 }
505
506 static void
507 purple_smiley_data_store(PurpleStoredImage *stored_img)
508 {
509 const char *dirname;
510 char *path;
511 FILE *file = NULL;
512
513 g_return_if_fail(stored_img != NULL);
514
515 if (!smileys_loaded)
516 return;
517
518 dirname = purple_smileys_get_storing_dir();
519 path = g_build_filename(dirname, purple_imgstore_get_filename(stored_img), NULL);
520
521 if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
522 purple_debug_info(SMILEYS_LOG_ID, "Creating smileys directory.\n");
523
524 if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
525 purple_debug_error(SMILEYS_LOG_ID,
526 "Unable to create directory %s: %s\n",
527 dirname, g_strerror(errno));
528 }
529 }
530
531 if ((file = g_fopen(path, "wb")) != NULL) {
532 if (!fwrite(purple_imgstore_get_data(stored_img),
533 purple_imgstore_get_size(stored_img), 1, file)) {
534 purple_debug_error(SMILEYS_LOG_ID, "Error writing %s: %s\n",
535 path, g_strerror(errno));
536 } else {
537 purple_debug_info(SMILEYS_LOG_ID, "Wrote cache file: %s\n", path);
538 }
539
540 fclose(file);
541 } else {
542 purple_debug_error(SMILEYS_LOG_ID, "Unable to create file %s: %s\n",
543 path, g_strerror(errno));
544 g_free(path);
545
546 return;
547 }
548
549 g_free(path);
550 }
551
552 static void
553 purple_smiley_data_unstore(const char *filename)
554 {
555 const char *dirname;
556 char *path;
557
558 g_return_if_fail(filename != NULL);
559
560 dirname = purple_smileys_get_storing_dir();
561 path = g_build_filename(dirname, filename, NULL);
562
563 if (g_file_test(path, G_FILE_TEST_EXISTS)) {
564 if (g_unlink(path))
565 purple_debug_error(SMILEYS_LOG_ID, "Failed to delete %s: %s\n",
566 path, g_strerror(errno));
567 else
568 purple_debug_info(SMILEYS_LOG_ID, "Deleted cache file: %s\n", path);
569 }
570
571 g_free(path);
572 }
573
574 static gboolean
575 read_smiley_file(const char *path, guchar **data, size_t *len)
576 {
577 GError *err = NULL;
578
579 if (!g_file_get_contents(path, (gchar **)data, len, &err)) {
580 purple_debug_error(SMILEYS_LOG_ID, "Error reading %s: %s\n",
581 path, err->message);
582 g_error_free(err);
583
584 return FALSE;
585 }
586
587 return TRUE;
588 }
589
590 static PurpleStoredImage *
591 purple_smiley_data_new(guchar *smiley_data, size_t smiley_data_len)
592 {
593 char *filename;
594 PurpleStoredImage *stored_img;
595
596 g_return_val_if_fail(smiley_data != NULL, NULL);
597 g_return_val_if_fail(smiley_data_len > 0, NULL);
598
599 filename = purple_util_get_image_filename(smiley_data, smiley_data_len);
600
601 if (filename == NULL) {
602 g_free(smiley_data);
603 return NULL;
604 }
605
606 stored_img = purple_imgstore_add(smiley_data, smiley_data_len, filename);
607
608 g_free(filename);
609
610 return stored_img;
611 }
612
613 static void
614 purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
615 size_t smiley_data_len, const char *filename)
616 {
617 PurpleStoredImage *old_img, *new_img;
618 const char *old_filename = NULL;
619 const char *new_filename = NULL;
620
621 g_return_if_fail(smiley != NULL);
622 g_return_if_fail(smiley_data != NULL);
623 g_return_if_fail(smiley_data_len > 0);
624
625 old_img = smiley->img;
626
627 if (filename)
628 new_img = purple_imgstore_add(smiley_data, smiley_data_len, filename);
629 else
630 new_img = purple_smiley_data_new(smiley_data, smiley_data_len);
631
632 g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, new_img, NULL);
633
634 /* If the old and new image files have different names we need
635 * to unstore old image file. */
636 if (!old_img)
637 return;
638
639 old_filename = purple_imgstore_get_filename(old_img);
640 new_filename = purple_imgstore_get_filename(smiley->img);
641
642 if (g_ascii_strcasecmp(old_filename, new_filename)) {
643 purple_smiley_data_unstore(old_filename);
644 purple_imgstore_unref(old_img);
645 }
646 }
647
648
649 /*****************************************************************************
650 * Public API functions *
651 *****************************************************************************/
652
653 static PurpleSmiley *
654 purple_smiley_create(const char *shortcut)
655 {
656 PurpleSmiley *smiley;
657
658 smiley = PURPLE_SMILEY(g_object_new(PURPLE_TYPE_SMILEY, PROP_SHORTCUT_S, shortcut, NULL));
659
660 return smiley;
661 }
662
663 PurpleSmiley *
664 purple_smiley_new(PurpleStoredImage *img, const char *shortcut)
665 {
666 PurpleSmiley *smiley = NULL;
667
668 g_return_val_if_fail(shortcut != NULL, NULL);
669 g_return_val_if_fail(img != NULL, NULL);
670
671 smiley = purple_smileys_find_by_shortcut(shortcut);
672 if (smiley)
673 return smiley;
674
675 smiley = purple_smiley_create(shortcut);
676 if (!smiley)
677 return NULL;
678
679 g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, img, NULL);
680
681 return smiley;
682 }
683
684 static PurpleSmiley *
685 purple_smiley_new_from_stream(const char *shortcut, guchar *smiley_data,
686 size_t smiley_data_len, const char *filename)
687 {
688 PurpleSmiley *smiley;
689
690 g_return_val_if_fail(shortcut != NULL, NULL);
691 g_return_val_if_fail(smiley_data != NULL, NULL);
692 g_return_val_if_fail(smiley_data_len > 0, NULL);
693
694 smiley = purple_smileys_find_by_shortcut(shortcut);
695 if (smiley)
696 return smiley;
697
698 /* purple_smiley_create() sets shortcut */
699 smiley = purple_smiley_create(shortcut);
700 if (!smiley)
701 return NULL;
702
703 purple_smiley_set_data_impl(smiley, smiley_data, smiley_data_len, filename);
704
705 purple_smiley_data_store(smiley->img);
706
707 return smiley;
708 }
709
710 PurpleSmiley *
711 purple_smiley_new_from_file(const char *shortcut, const char *filepath)
712 {
713 PurpleSmiley *smiley = NULL;
714 guchar *smiley_data;
715 size_t smiley_data_len;
716 char *filename;
717
718 g_return_val_if_fail(shortcut != NULL, NULL);
719 g_return_val_if_fail(filepath != NULL, NULL);
720
721 filename = g_path_get_basename(filepath);
722 if (read_smiley_file(filepath, &smiley_data, &smiley_data_len))
723 smiley = purple_smiley_new_from_stream(shortcut, smiley_data,
724 smiley_data_len, filename);
725 g_free(filename);
726
727 return smiley;
728 }
729
730 void
731 purple_smiley_delete(PurpleSmiley *smiley)
732 {
733 g_return_if_fail(smiley != NULL);
734
735 g_object_unref(smiley);
736 }
737
738 gboolean
739 purple_smiley_set_shortcut(PurpleSmiley *smiley, const char *shortcut)
740 {
741 g_return_val_if_fail(smiley != NULL, FALSE);
742 g_return_val_if_fail(shortcut != NULL, FALSE);
743
744 /* Check out whether the new shortcut is already being used. */
745 if (g_hash_table_lookup(smiley_shortcut_index, shortcut))
746 return FALSE;
747
748 /* Remove the old shortcut. */
749 if (smiley->shortcut)
750 g_hash_table_remove(smiley_shortcut_index, smiley->shortcut);
751
752 /* Insert the new shortcut. */
753 g_hash_table_insert(smiley_shortcut_index, g_strdup(shortcut), smiley);
754
755 g_free(smiley->shortcut);
756 smiley->shortcut = g_strdup(shortcut);
757
758 g_object_notify(G_OBJECT(smiley), PROP_SHORTCUT_S);
759
760 purple_smileys_save();
761
762 return TRUE;
763 }
764
765 void
766 purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data,
767 size_t smiley_data_len, gboolean keepfilename)
768 {
769 g_return_if_fail(smiley != NULL);
770 g_return_if_fail(smiley_data != NULL);
771 g_return_if_fail(smiley_data_len > 0);
772
773 /* Remove the previous entry */
774 g_hash_table_remove(smiley_checksum_index, smiley->checksum);
775
776 /* Update the file data. This also updates the checksum. */
777 if ((keepfilename) && (smiley->img) &&
778 (purple_imgstore_get_filename(smiley->img)))
779 purple_smiley_set_data_impl(smiley, smiley_data,
780 smiley_data_len,
781 purple_imgstore_get_filename(smiley->img));
782 else
783 purple_smiley_set_data_impl(smiley, smiley_data,
784 smiley_data_len, NULL);
785
786 /* Reinsert the index item. */
787 g_hash_table_insert(smiley_checksum_index, g_strdup(smiley->checksum), smiley);
788
789 purple_smileys_save();
790 }
791
792 PurpleStoredImage *
793 purple_smiley_get_stored_image(const PurpleSmiley *smiley)
794 {
795 return purple_imgstore_ref(smiley->img);
796 }
797
798 const char *purple_smiley_get_shortcut(const PurpleSmiley *smiley)
799 {
800 g_return_val_if_fail(smiley != NULL, NULL);
801
802 return smiley->shortcut;
803 }
804
805 const char *
806 purple_smiley_get_checksum(const PurpleSmiley *smiley)
807 {
808 g_return_val_if_fail(smiley != NULL, NULL);
809
810 return smiley->checksum;
811 }
812
813 gconstpointer
814 purple_smiley_get_data(const PurpleSmiley *smiley, size_t *len)
815 {
816 g_return_val_if_fail(smiley != NULL, NULL);
817
818 if (smiley->img) {
819 if (len != NULL)
820 *len = purple_imgstore_get_size(smiley->img);
821
822 return purple_imgstore_get_data(smiley->img);
823 }
824
825 return NULL;
826 }
827
828 const char *
829 purple_smiley_get_extension(const PurpleSmiley *smiley)
830 {
831 if (smiley->img != NULL)
832 return purple_imgstore_get_extension(smiley->img);
833
834 return NULL;
835 }
836
837 char *purple_smiley_get_full_path(PurpleSmiley *smiley)
838 {
839 g_return_val_if_fail(smiley != NULL, NULL);
840
841 if (smiley->img == NULL)
842 return NULL;
843
844 return get_file_full_path(purple_imgstore_get_filename(smiley->img));
845 }
846
847 static void add_smiley_to_list(gpointer key, gpointer value, gpointer user_data)
848 {
849 GList** returninglist = (GList**)user_data;
850
851 *returninglist = g_list_append(*returninglist, value);
852 }
853
854 GList *
855 purple_smileys_get_all(void)
856 {
857 GList *returninglist = NULL;
858
859 g_hash_table_foreach(smiley_shortcut_index, add_smiley_to_list, &returninglist);
860
861 return returninglist;
862 }
863
864 PurpleSmiley *
865 purple_smileys_find_by_shortcut(const char *shortcut)
866 {
867 g_return_val_if_fail(shortcut != NULL, NULL);
868
869 return g_hash_table_lookup(smiley_shortcut_index, shortcut);
870 }
871
872 PurpleSmiley *
873 purple_smileys_find_by_checksum(const char *checksum)
874 {
875 g_return_val_if_fail(checksum != NULL, NULL);
876
877 return g_hash_table_lookup(smiley_checksum_index, checksum);
878 }
879
880 const char *
881 purple_smileys_get_storing_dir(void)
882 {
883 return smileys_dir;
884 }
885
886 void
887 purple_smileys_init()
888 {
889 smiley_shortcut_index = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
890 smiley_checksum_index = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
891
892 smileys_dir = g_build_filename(purple_user_dir(), SMILEYS_DEFAULT_FOLDER, NULL);
893
894 purple_smileys_load();
895 }
896
897 void
898 purple_smileys_uninit()
899 {
900 if (save_timer != 0) {
901 purple_timeout_remove(save_timer);
902 save_timer = 0;
903 sync_smileys();
904 }
905
906 g_hash_table_destroy(smiley_shortcut_index);
907 g_hash_table_destroy(smiley_checksum_index);
908 g_free(smileys_dir);
909 }
910