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 * Rewritten from scratch during Google Summer of Code 2012
8 * by Tomek Wasilczyk (http://www.wasilczyk.pl).
10 * Previously implemented by:
11 * - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
12 * - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
13 * - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
30 #include "image-prpl.h"
33 #include <glibcompat.h>
38 #include <image-store.h>
40 struct _ggp_image_session_data
42 GHashTable
*recv_images
;
43 GHashTable
*sent_images
;
49 gchar
*conv_name
; /* TODO: callback */
52 static void ggp_image_sent_free(gpointer _sent_image
)
54 ggp_image_sent
*sent_image
= _sent_image
;
55 g_object_unref(sent_image
->image
);
56 g_free(sent_image
->conv_name
);
60 static uint64_t ggp_image_params_to_id(uint32_t crc32
, uint32_t size
)
62 return ((uint64_t)crc32
<< 32) | size
;
65 static inline ggp_image_session_data
*
66 ggp_image_get_sdata(PurpleConnection
*gc
)
68 GGPInfo
*accdata
= purple_connection_get_protocol_data(gc
);
69 return accdata
->image_data
;
72 void ggp_image_setup(PurpleConnection
*gc
)
74 GGPInfo
*accdata
= purple_connection_get_protocol_data(gc
);
75 ggp_image_session_data
*sdata
= g_new0(ggp_image_session_data
, 1);
77 accdata
->image_data
= sdata
;
79 sdata
->recv_images
= g_hash_table_new_full(
80 g_int64_hash
, g_int64_equal
, g_free
, g_object_unref
);
81 sdata
->sent_images
= g_hash_table_new_full(
82 g_int64_hash
, g_int64_equal
, g_free
,
86 void ggp_image_cleanup(PurpleConnection
*gc
)
88 ggp_image_session_data
*sdata
= ggp_image_get_sdata(gc
);
90 g_hash_table_destroy(sdata
->recv_images
);
91 g_hash_table_destroy(sdata
->sent_images
);
95 ggp_image_prepare_result
96 ggp_image_prepare(PurpleConversation
*conv
, PurpleImage
*image
, uint64_t *id
)
98 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
99 ggp_image_session_data
*sdata
= ggp_image_get_sdata(gc
);
101 gconstpointer image_data
;
103 ggp_image_sent
*sent_image
;
105 g_return_val_if_fail(image
, GGP_IMAGE_PREPARE_FAILURE
);
107 image_size
= purple_image_get_data_size(image
);
109 if (image_size
> GGP_IMAGE_SIZE_MAX
) {
110 purple_debug_warning("gg", "ggp_image_prepare: image "
111 "is too big (max bytes: %d)\n", GGP_IMAGE_SIZE_MAX
);
112 return GGP_IMAGE_PREPARE_TOO_BIG
;
116 image_data
= purple_image_get_data(image
);
117 image_crc
= gg_crc32(0, image_data
, image_size
);
119 purple_debug_info("gg", "ggp_image_prepare: image prepared "
120 "[crc=%u, size=%" G_GSIZE_FORMAT
"]",
121 image_crc
, image_size
);
123 *id
= ggp_image_params_to_id(image_crc
, image_size
);
126 sent_image
= g_new(ggp_image_sent
, 1);
127 sent_image
->image
= image
;
128 sent_image
->conv_name
= g_strdup(purple_conversation_get_name(conv
));
129 g_hash_table_insert(sdata
->sent_images
, ggp_uint64dup(*id
),
132 return GGP_IMAGE_PREPARE_OK
;
135 void ggp_image_recv(PurpleConnection
*gc
,
136 const struct gg_event_image_reply
*image_reply
)
138 ggp_image_session_data
*sdata
= ggp_image_get_sdata(gc
);
142 id
= ggp_image_params_to_id(image_reply
->crc32
, image_reply
->size
);
143 img
= g_hash_table_lookup(sdata
->recv_images
, &id
);
145 purple_debug_warning("gg", "ggp_image_recv: "
146 "image " GGP_IMAGE_ID_FORMAT
" wasn't requested\n",
151 purple_debug_info("gg", "ggp_image_recv: got image "
152 "[crc=%u, size=%u, filename=%s, id=" GGP_IMAGE_ID_FORMAT
"]",
153 image_reply
->crc32
, image_reply
->size
,
154 image_reply
->filename
, id
);
156 img
= purple_image_new_from_data(
157 (const guint8
*)image_reply
->image
,
160 purple_image_set_friendly_filename(img
, image_reply
->filename
);
162 g_hash_table_insert(sdata
->recv_images
, &id
, img
);
165 void ggp_image_send(PurpleConnection
*gc
,
166 const struct gg_event_image_request
*image_request
)
168 GGPInfo
*accdata
= purple_connection_get_protocol_data(gc
);
169 ggp_image_session_data
*sdata
= ggp_image_get_sdata(gc
);
170 ggp_image_sent
*sent_image
;
171 PurpleConversation
*conv
;
175 purple_debug_info("gg", "ggp_image_send: got image request "
176 "[uin=%u, crc=%u, size=%u]\n",
177 image_request
->sender
,
178 image_request
->crc32
,
179 image_request
->size
);
181 id
= ggp_image_params_to_id(image_request
->crc32
, image_request
->size
);
183 sent_image
= g_hash_table_lookup(sdata
->sent_images
, &id
);
185 if (sent_image
== NULL
&& image_request
->sender
== ggp_str_to_uin(
186 purple_account_get_username(purple_connection_get_account(gc
))))
188 purple_debug_misc("gg", "ggp_image_send: requested image "
189 "not found, but this may be another session request\n");
192 if (sent_image
== NULL
) {
193 purple_debug_warning("gg", "ggp_image_send: requested image "
198 purple_debug_misc("gg", "ggp_image_send: requested image found "
199 "[id=" GGP_IMAGE_ID_FORMAT
", conv=%s]\n",
200 id
, sent_image
->conv_name
);
202 g_return_if_fail(sent_image
->image
);
204 /* TODO: check allowed recipients */
205 gg_filename
= g_strdup_printf(GGP_IMAGE_ID_FORMAT
, id
);
206 gg_image_reply(accdata
->session
, image_request
->sender
,
208 purple_image_get_data(sent_image
->image
),
209 purple_image_get_data_size(sent_image
->image
));
212 conv
= purple_conversations_find_with_account(
213 sent_image
->conv_name
,
214 purple_connection_get_account(gc
));
216 gchar
*msg
= g_strdup_printf(_("Image delivered to %u."),
217 image_request
->sender
);
218 purple_conversation_write_system_message(conv
, msg
,
219 PURPLE_MESSAGE_NO_LOG
| PURPLE_MESSAGE_NOTIFY
);
225 ggp_image_request(PurpleConnection
*gc
, uin_t uin
, uint64_t id
)
227 GGPInfo
*accdata
= purple_connection_get_protocol_data(gc
);
228 ggp_image_session_data
*sdata
= ggp_image_get_sdata(gc
);
230 uint32_t crc
= id
>> 32;
233 if (size
> GGP_IMAGE_SIZE_MAX
&& crc
<= GGP_IMAGE_SIZE_MAX
) {
235 purple_debug_warning("gg", "ggp_image_request: "
236 "crc and size are swapped!\n");
240 id
= ggp_image_params_to_id(crc
, size
);
243 img
= g_hash_table_lookup(sdata
->recv_images
, &id
);
245 purple_debug_info("gg", "ggp_image_request: "
246 "image " GGP_IMAGE_ID_FORMAT
" got from cache", id
);
251 g_hash_table_insert(sdata
->recv_images
, ggp_uint64dup(id
), NULL
);
253 purple_debug_info("gg", "ggp_image_request: requesting image "
254 GGP_IMAGE_ID_FORMAT
, id
);
255 if (gg_image_request(accdata
->session
, uin
, size
, crc
) != 0)
256 purple_debug_error("gg", "ggp_image_request: failed");