| |
1 /* |
| |
2 * This program is free software; you can redistribute it and/or modify |
| |
3 * it under the terms of the GNU General Public License as published by |
| |
4 * the Free Software Foundation; either version 2 of the License, or |
| |
5 * (at your option) any later version. |
| |
6 * |
| |
7 * This program is distributed in the hope that it will be useful, |
| |
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
10 * GNU Library General Public License for more details. |
| |
11 * |
| |
12 * You should have received a copy of the GNU General Public License |
| |
13 * along with this program; if not, write to the Free Software |
| |
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA |
| |
15 */ |
| |
16 |
| |
17 #include <stdlib.h> |
| |
18 #include <glib.h> |
| |
19 #include <string.h> |
| |
20 |
| |
21 #include "data.h" |
| |
22 #include "debug.h" |
| |
23 #include "xmlnode.h" |
| |
24 #include "util.h" |
| |
25 #include "conversation.h" |
| |
26 #include "iq.h" |
| |
27 |
| |
28 /* hash table to store locally supplied data objects, by conversation and |
| |
29 alt text (smiley shortcut) */ |
| |
30 static GHashTable *local_datas_by_alt = NULL; |
| |
31 |
| |
32 /* hash table to store locally supplied data objects, by content id */ |
| |
33 static GHashTable *local_datas_by_cid = NULL; |
| |
34 |
| |
35 /* remote supplied data objects by content id */ |
| |
36 static GHashTable *remote_datas_by_cid = NULL; |
| |
37 |
| |
38 |
| |
39 void |
| |
40 jabber_data_init(void) |
| |
41 { |
| |
42 /* setup hash tables for storing data instances here */ |
| |
43 purple_debug_info("jabber", "Setting up data handling\n"); |
| |
44 |
| |
45 local_datas_by_alt = g_hash_table_new(NULL, NULL); |
| |
46 local_datas_by_cid = g_hash_table_new(NULL, NULL); |
| |
47 remote_datas_by_cid = g_hash_table_new(NULL, NULL); |
| |
48 } |
| |
49 |
| |
50 JabberData * |
| |
51 jabber_data_create_from_data(gconstpointer rawdata, gsize size, const char *type, |
| |
52 const char *alt) |
| |
53 { |
| |
54 JabberData *data = g_new0(JabberData, 1); |
| |
55 gchar *checksum = purple_util_get_image_checksum(rawdata, size); |
| |
56 gchar cid[256]; |
| |
57 |
| |
58 /* is there some better way to generate a content ID? */ |
| |
59 g_snprintf(cid, sizeof(cid), "%s@%s", checksum, g_get_host_name()); |
| |
60 g_free(checksum); |
| |
61 |
| |
62 data->cid = g_strdup(cid); |
| |
63 data->type = g_strdup(type); |
| |
64 data->alt = g_strdup(alt); |
| |
65 data->size = size; |
| |
66 |
| |
67 data->data = g_memdup(rawdata, size); |
| |
68 |
| |
69 return data; |
| |
70 } |
| |
71 |
| |
72 JabberData * |
| |
73 jabber_data_create_from_xml(xmlnode *tag) |
| |
74 { |
| |
75 JabberData *data = g_new0(JabberData, 1); |
| |
76 gsize size; |
| |
77 gpointer raw_data = NULL; |
| |
78 |
| |
79 if (data == NULL) { |
| |
80 purple_debug_error("jabber", "Could not allocate data object\n"); |
| |
81 g_free(data); |
| |
82 return NULL; |
| |
83 } |
| |
84 |
| |
85 /* check if this is a "data" tag */ |
| |
86 if (strcmp(tag->name, "data") != 0) { |
| |
87 purple_debug_error("jabber", "Invalid data element"); |
| |
88 g_free(data); |
| |
89 return NULL; |
| |
90 } |
| |
91 |
| |
92 data->cid = g_strdup(xmlnode_get_attrib(tag, "cid")); |
| |
93 data->type = g_strdup(xmlnode_get_attrib(tag, "type")); |
| |
94 data->alt = g_strdup(xmlnode_get_attrib(tag, "alt")); |
| |
95 |
| |
96 raw_data = xmlnode_get_data(tag); |
| |
97 data->data = purple_base64_decode(raw_data, &size); |
| |
98 data->size = size; |
| |
99 |
| |
100 g_free(raw_data); |
| |
101 |
| |
102 return data; |
| |
103 } |
| |
104 |
| |
105 |
| |
106 void |
| |
107 jabber_data_delete(JabberData *data) |
| |
108 { |
| |
109 g_free(data->cid); |
| |
110 g_free(data->alt); |
| |
111 g_free(data->type); |
| |
112 g_free(data->data); |
| |
113 g_free(data); |
| |
114 } |
| |
115 |
| |
116 const char * |
| |
117 jabber_data_get_cid(const JabberData *data) |
| |
118 { |
| |
119 return data->cid; |
| |
120 } |
| |
121 |
| |
122 const char * |
| |
123 jabber_data_get_alt(const JabberData *data) |
| |
124 { |
| |
125 return data->alt; |
| |
126 } |
| |
127 |
| |
128 const char * |
| |
129 jabber_data_get_type(const JabberData *data) |
| |
130 { |
| |
131 return data->type; |
| |
132 } |
| |
133 |
| |
134 gsize |
| |
135 jabber_data_get_size(const JabberData *data) |
| |
136 { |
| |
137 return data->size; |
| |
138 } |
| |
139 |
| |
140 gpointer |
| |
141 jabber_data_get_data(const JabberData *data) |
| |
142 { |
| |
143 return data->data; |
| |
144 } |
| |
145 |
| |
146 xmlnode * |
| |
147 jabber_data_get_xml_definition(const JabberData *data) |
| |
148 { |
| |
149 xmlnode *tag = xmlnode_new("data"); |
| |
150 char *base64data = purple_base64_encode(data->data, data->size); |
| |
151 |
| |
152 xmlnode_set_namespace(tag, XEP_0231_NAMESPACE); |
| |
153 xmlnode_set_attrib(tag, "alt", data->alt); |
| |
154 xmlnode_set_attrib(tag, "cid", data->cid); |
| |
155 xmlnode_set_attrib(tag, "type", data->type); |
| |
156 |
| |
157 xmlnode_insert_data(tag, base64data, -1); |
| |
158 |
| |
159 g_free(base64data); |
| |
160 |
| |
161 return tag; |
| |
162 } |
| |
163 |
| |
164 xmlnode * |
| |
165 jabber_data_get_xhtml_im(const JabberData *data) |
| |
166 { |
| |
167 xmlnode *img = xmlnode_new("img"); |
| |
168 char src[128]; |
| |
169 |
| |
170 xmlnode_set_attrib(img, "alt", data->alt); |
| |
171 g_snprintf(src, sizeof(src), "cid:%s", data->cid); |
| |
172 xmlnode_set_attrib(img, "src", src); |
| |
173 |
| |
174 return img; |
| |
175 } |
| |
176 |
| |
177 xmlnode * |
| |
178 jabber_data_get_xml_request(const gchar *cid) |
| |
179 { |
| |
180 xmlnode *tag = xmlnode_new("data"); |
| |
181 |
| |
182 xmlnode_set_namespace(tag, XEP_0231_NAMESPACE); |
| |
183 xmlnode_set_attrib(tag, "cid", cid); |
| |
184 |
| |
185 return tag; |
| |
186 } |
| |
187 |
| |
188 const JabberData * |
| |
189 jabber_data_find_local_by_alt(const PurpleConversation *conv, const char *alt) |
| |
190 { |
| |
191 GHashTable *local_datas = g_hash_table_lookup(local_datas_by_alt, conv); |
| |
192 |
| |
193 if (local_datas) { |
| |
194 return g_hash_table_lookup(local_datas, alt); |
| |
195 } else { |
| |
196 return NULL; |
| |
197 } |
| |
198 } |
| |
199 |
| |
200 |
| |
201 const JabberData * |
| |
202 jabber_data_find_local_by_cid(const PurpleConversation *conv, const char *cid) |
| |
203 { |
| |
204 GHashTable *local_datas = g_hash_table_lookup(local_datas_by_cid, conv); |
| |
205 |
| |
206 if (local_datas) { |
| |
207 return g_hash_table_lookup(local_datas, cid); |
| |
208 } else { |
| |
209 return NULL; |
| |
210 } |
| |
211 } |
| |
212 |
| |
213 const JabberData * |
| |
214 jabber_data_find_remote_by_cid(const PurpleConversation *conv, const char *cid) |
| |
215 { |
| |
216 GHashTable *remote_datas = g_hash_table_lookup(remote_datas_by_cid, conv); |
| |
217 |
| |
218 if (remote_datas) { |
| |
219 return g_hash_table_lookup(remote_datas, cid); |
| |
220 } else { |
| |
221 return NULL; |
| |
222 } |
| |
223 } |
| |
224 |
| |
225 void |
| |
226 jabber_data_associate_local_with_conv(JabberData *data, PurpleConversation *conv) |
| |
227 { |
| |
228 GHashTable *datas_by_alt = g_hash_table_lookup(local_datas_by_alt, conv); |
| |
229 GHashTable *datas_by_cid = g_hash_table_lookup(local_datas_by_cid, conv); |
| |
230 |
| |
231 if (!datas_by_alt) { |
| |
232 datas_by_alt = g_hash_table_new(g_str_hash, g_str_equal); |
| |
233 g_hash_table_insert(local_datas_by_alt, conv, datas_by_alt); |
| |
234 } |
| |
235 |
| |
236 if (!datas_by_cid) { |
| |
237 datas_by_cid = g_hash_table_new(g_str_hash, g_str_equal); |
| |
238 g_hash_table_insert(local_datas_by_cid, conv, datas_by_cid); |
| |
239 } |
| |
240 |
| |
241 g_hash_table_insert(datas_by_alt, g_strdup(jabber_data_get_alt(data)), data); |
| |
242 g_hash_table_insert(datas_by_cid, g_strdup(jabber_data_get_cid(data)), data); |
| |
243 } |
| |
244 |
| |
245 void |
| |
246 jabber_data_associate_remote_with_conv(JabberData *data, PurpleConversation *conv) |
| |
247 { |
| |
248 GHashTable *datas_by_cid = g_hash_table_lookup(remote_datas_by_cid, conv); |
| |
249 |
| |
250 if (!datas_by_cid) { |
| |
251 datas_by_cid = g_hash_table_new(g_str_hash, g_str_equal); |
| |
252 g_hash_table_insert(remote_datas_by_cid, conv, datas_by_cid); |
| |
253 } |
| |
254 |
| |
255 g_hash_table_insert(datas_by_cid, g_strdup(jabber_data_get_cid(data)), data); |
| |
256 } |
| |
257 |
| |
258 static void |
| |
259 jabber_data_delete_from_hash_table(gpointer key, gpointer value, |
| |
260 gpointer user_data) |
| |
261 { |
| |
262 JabberData *data = (JabberData *) value; |
| |
263 jabber_data_delete(data); |
| |
264 g_free(key); |
| |
265 } |
| |
266 |
| |
267 void |
| |
268 jabber_data_delete_associated_with_conv(PurpleConversation *conv) |
| |
269 { |
| |
270 GHashTable *local_datas = g_hash_table_lookup(local_datas_by_cid, conv); |
| |
271 GHashTable *remote_datas = g_hash_table_lookup(remote_datas_by_cid, conv); |
| |
272 GHashTable *local_datas_alt = g_hash_table_lookup(local_datas_by_alt, conv); |
| |
273 |
| |
274 if (local_datas) { |
| |
275 g_hash_table_foreach(local_datas, jabber_data_delete_from_hash_table, |
| |
276 NULL); |
| |
277 g_hash_table_destroy(local_datas); |
| |
278 g_hash_table_remove(local_datas_by_cid, conv); |
| |
279 } |
| |
280 if (remote_datas) { |
| |
281 g_hash_table_foreach(remote_datas, jabber_data_delete_from_hash_table, |
| |
282 NULL); |
| |
283 g_hash_table_destroy(remote_datas); |
| |
284 g_hash_table_remove(remote_datas_by_cid, conv); |
| |
285 } |
| |
286 if (local_datas_alt) { |
| |
287 g_hash_table_destroy(local_datas_alt); |
| |
288 g_hash_table_remove(local_datas_by_alt, conv); |
| |
289 } |
| |
290 } |
| |
291 |
| |
292 void |
| |
293 jabber_data_parse(JabberStream *js, xmlnode *packet) |
| |
294 { |
| |
295 JabberIq *result = NULL; |
| |
296 const char *who = xmlnode_get_attrib(packet, "from"); |
| |
297 const PurpleConnection *gc = js->gc; |
| |
298 const PurpleAccount *account = purple_connection_get_account(gc); |
| |
299 const PurpleConversation *conv = |
| |
300 purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account); |
| |
301 xmlnode *data_node = xmlnode_get_child(packet, "data"); |
| |
302 const JabberData *data = |
| |
303 jabber_data_find_local_by_cid(conv, xmlnode_get_attrib(data_node, "cid")); |
| |
304 |
| |
305 if (!conv || !data) { |
| |
306 xmlnode *item_not_found = xmlnode_new("item-not-found"); |
| |
307 |
| |
308 result = jabber_iq_new(js, JABBER_IQ_ERROR); |
| |
309 xmlnode_set_attrib(result->node, "to", who); |
| |
310 xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); |
| |
311 xmlnode_insert_child(result->node, item_not_found); |
| |
312 } else { |
| |
313 result = jabber_iq_new(js, JABBER_IQ_RESULT); |
| |
314 xmlnode_set_attrib(result->node, "to", who); |
| |
315 xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); |
| |
316 xmlnode_insert_child(result->node, |
| |
317 jabber_data_get_xml_definition(data)); |
| |
318 } |
| |
319 jabber_iq_send(result); |
| |
320 } |