Mercurial > pidgin
comparison src/protocols/msn/buddyicon.c @ 5351:2aa7e4237142
[gaim-migrate @ 5727]
Buddy icon support!
The MSN protocol does not support this, but it does allow for different
content-types, which no client (except a couple broken ones I can name)
will see. So, I managed to extend the protocol a bit to do buddy icons.
It should work like AIM. Setup your icon in your account editor, and
message somebody. If they change their icon, however, you will have to
close the conversation window, re-open it, and send another message. That's
just how it has to work for now, I'm afraid.
Oh, and another thing. MSNP7 (P6 as well? Not sure) times out inactive
conversations after 5 minutes. Right now, you're seeing "User has closed
the conversation window" messages, but they're really not. So, we now print
out a message saying it timed out. Ugly, yes, but unless we have both
messages, there's confusion. Oh well! Kick the hay!
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Sat, 10 May 2003 23:55:18 +0000 |
parents | |
children | 6a57fd9b45e1 |
comparison
equal
deleted
inserted
replaced
5350:a6146cbae03b | 5351:2aa7e4237142 |
---|---|
1 /** | |
2 * @file buddyicon.c Buddy icon support | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org> | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 */ | |
22 #include "msn.h" | |
23 #include "buddyicon.h" | |
24 | |
25 #define PACKET_LENGTH 1500 | |
26 | |
27 static const char alphabet[] = | |
28 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |
29 "0123456789+/"; | |
30 | |
31 static char * | |
32 __base64_enc(const char *data, int len) | |
33 { | |
34 char *dest; | |
35 char *buf; | |
36 | |
37 buf = dest = g_malloc(4 * len / 3 + 4); | |
38 | |
39 /* Encode 3 bytes at a time */ | |
40 while (len >= 3) { | |
41 buf[0] = alphabet[(data[0] >> 2) & 0x3F]; | |
42 buf[1] = alphabet[((data[0] << 4) & 0x30) | ((data[1] >> 4) & 0x0F)]; | |
43 buf[2] = alphabet[((data[1] << 2) & 0x3C) | ((data[2] >> 6) & 0x03)]; | |
44 buf[3] = alphabet[data[2] & 0x3F]; | |
45 data += 3; | |
46 buf += 4; | |
47 len -= 3; | |
48 } | |
49 | |
50 if (len > 0) { | |
51 buf[0] = alphabet[(data[0] >> 2) & 0x3F]; | |
52 buf[1] = alphabet[(data[0] << 4) & 0x30]; | |
53 | |
54 if (len > 1) { | |
55 buf[1] += (data[1] >> 4) & 0x0F; | |
56 buf[2] = alphabet[(data[1] << 2) & 0x3C]; | |
57 } | |
58 | |
59 else | |
60 buf[2] = '='; | |
61 | |
62 buf[3] = '='; | |
63 buf += 4; | |
64 } | |
65 | |
66 *buf = '\0'; | |
67 | |
68 return dest; | |
69 } | |
70 | |
71 static gboolean | |
72 __get_buddy_icon_info(struct gaim_account *account, char **base64, | |
73 char **md5sum, int *file_size, int *base64_size) | |
74 { | |
75 FILE *fp; | |
76 struct stat sb; | |
77 md5_state_t st; | |
78 md5_byte_t di[16]; | |
79 | |
80 if (base64 != NULL) *base64 = NULL; | |
81 if (md5sum != NULL) *md5sum = NULL; | |
82 if (file_size != NULL) *file_size = 0; | |
83 if (base64_size != NULL) *base64_size = 0; | |
84 | |
85 if (!stat(account->iconfile, &sb)) { | |
86 if (file_size != NULL) | |
87 *file_size = sb.st_size; | |
88 | |
89 if ((fp = fopen(account->iconfile, "rb")) != NULL) { | |
90 char *buf = g_malloc(sb.st_size + 1); | |
91 char *temp; | |
92 | |
93 fread(buf, 1, sb.st_size, fp); | |
94 | |
95 buf[sb.st_size] = '\0'; | |
96 | |
97 temp = __base64_enc(buf, sb.st_size); | |
98 | |
99 if (base64_size != NULL) | |
100 *base64_size = strlen(temp); | |
101 | |
102 if (base64 != NULL) | |
103 *base64 = temp; | |
104 else | |
105 g_free(temp); | |
106 | |
107 if (md5sum != NULL) { | |
108 char buf2[3]; | |
109 int i; | |
110 | |
111 md5_init(&st); | |
112 md5_append(&st, (const md5_byte_t *)buf, sb.st_size); | |
113 md5_finish(&st, di); | |
114 | |
115 *md5sum = g_new0(char, 33); | |
116 | |
117 for (i = 0; i < 16; i++) { | |
118 g_snprintf(buf2, sizeof(buf2), "%02x", di[i]); | |
119 strcat(*md5sum, buf2); | |
120 } | |
121 } | |
122 | |
123 g_free(buf); | |
124 | |
125 fclose(fp); | |
126 } | |
127 else { | |
128 gaim_debug(GAIM_DEBUG_ERROR, "msn", | |
129 "Cannot open buddy icon file!\n"); | |
130 | |
131 return FALSE; | |
132 } | |
133 } | |
134 | |
135 return TRUE; | |
136 } | |
137 | |
138 static gboolean | |
139 __send_icon_data(MsnSwitchBoard *swboard, MsnBuddyIconXfer *buddyicon) | |
140 { | |
141 struct gaim_connection *gc = swboard->servconn->session->account->gc; | |
142 char buf[MSN_BUF_LEN]; | |
143 MsnMessage *msg; | |
144 int len; | |
145 | |
146 len = MIN(PACKET_LENGTH - 4, | |
147 buddyicon->total_size - buddyicon->bytes_xfer); | |
148 | |
149 strcpy(buf, "ICON"); | |
150 | |
151 strncat(buf, buddyicon->data + buddyicon->bytes_xfer, len); | |
152 | |
153 msg = msn_message_new(); | |
154 msn_message_set_content_type(msg, "application/x-buddyicon"); | |
155 msn_message_set_receiver(msg, buddyicon->user); | |
156 msn_message_set_charset(msg, NULL); | |
157 msn_message_set_attr(msg, "User-Agent", NULL); | |
158 | |
159 msn_message_set_body(msg, buf); | |
160 | |
161 if (!msn_switchboard_send_msg(swboard, msg)) { | |
162 msn_message_destroy(msg); | |
163 | |
164 msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); | |
165 swboard->buddy_icon_xfer = NULL; | |
166 | |
167 hide_login_progress(gc, _("Write error")); | |
168 signoff(gc); | |
169 | |
170 return FALSE; | |
171 } | |
172 | |
173 msn_message_destroy(msg); | |
174 | |
175 buddyicon->bytes_xfer += len; | |
176 | |
177 if (buddyicon->bytes_xfer == buddyicon->total_size) { | |
178 msg = msn_message_new(); | |
179 msn_message_set_content_type(msg, "application/x-buddyicon"); | |
180 msn_message_set_receiver(msg, buddyicon->user); | |
181 msn_message_set_charset(msg, NULL); | |
182 msn_message_set_attr(msg, "User-Agent", NULL); | |
183 | |
184 msn_message_set_body(msg, "Command: COMPLETE\r\n"); | |
185 | |
186 msn_switchboard_send_msg(swboard, msg); | |
187 | |
188 msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); | |
189 swboard->buddy_icon_xfer = NULL; | |
190 } | |
191 | |
192 return TRUE; | |
193 } | |
194 | |
195 static gboolean | |
196 __process_invite(MsnServConn *servconn, const MsnMessage *msg) | |
197 { | |
198 MsnSession *session = servconn->session; | |
199 struct gaim_connection *gc = session->account->gc; | |
200 MsnMessage *new_msg; | |
201 MsnSwitchBoard *swboard; | |
202 MsnBuddyIconXfer *buddyicon; | |
203 struct buddy *b; | |
204 GHashTable *table; | |
205 const char *command; | |
206 | |
207 table = msn_message_get_hashtable_from_body(msg); | |
208 | |
209 command = g_hash_table_lookup(table, "Command"); | |
210 | |
211 if (command == NULL) { | |
212 gaim_debug(GAIM_DEBUG_ERROR, "msn", | |
213 "Missing Command from buddy icon message.\n"); | |
214 return TRUE; | |
215 } | |
216 | |
217 if (!strcmp(command, "INVITE")) { | |
218 MsnUser *user; | |
219 const char *md5sum = g_hash_table_lookup(table, "MD5SUM"); | |
220 const char *size_s = g_hash_table_lookup(table, "File-Size"); | |
221 const char *base64_size_s = g_hash_table_lookup(table, "Base64-Size"); | |
222 const char *passport; | |
223 | |
224 if (md5sum == NULL) { | |
225 gaim_debug(GAIM_DEBUG_ERROR, "msn", | |
226 "Missing MD5SUM from buddy icon message.\n"); | |
227 | |
228 return TRUE; | |
229 } | |
230 | |
231 if (size_s == NULL) { | |
232 gaim_debug(GAIM_DEBUG_ERROR, "msn", | |
233 "Missing File-Size from buddy icon message.\n"); | |
234 | |
235 return TRUE; | |
236 } | |
237 | |
238 if (base64_size_s == NULL) { | |
239 gaim_debug(GAIM_DEBUG_ERROR, "msn", | |
240 "Missing Bas64-Size from buddy icon message.\n"); | |
241 | |
242 return TRUE; | |
243 } | |
244 | |
245 user = msn_message_get_sender(msg); | |
246 | |
247 passport = msn_user_get_passport(user); | |
248 | |
249 /* See if we actually need a new icon. */ | |
250 if ((b = gaim_find_buddy(gc->account, passport)) != NULL) { | |
251 const char *cur_md5sum; | |
252 | |
253 cur_md5sum = gaim_buddy_get_setting(b, "icon_checksum"); | |
254 | |
255 if (cur_md5sum != NULL && !strcmp(cur_md5sum, md5sum)) | |
256 return TRUE; | |
257 } | |
258 | |
259 /* Send a request for transfer. */ | |
260 new_msg = msn_message_new(); | |
261 msn_message_set_content_type(new_msg, "application/x-buddyicon"); | |
262 msn_message_set_receiver(new_msg, user); | |
263 msn_message_set_charset(new_msg, NULL); | |
264 msn_message_set_attr(new_msg, "User-Agent", NULL); | |
265 | |
266 msn_message_set_body(new_msg, "Command: REQUEST\r\n"); | |
267 | |
268 if ((swboard = msn_session_open_switchboard(session)) == NULL) { | |
269 msn_message_destroy(new_msg); | |
270 | |
271 hide_login_progress(gc, _("Write error")); | |
272 signoff(gc); | |
273 | |
274 return FALSE; | |
275 } | |
276 | |
277 swboard->hidden = TRUE; | |
278 msn_switchboard_set_user(swboard, user); | |
279 msn_switchboard_send_msg(swboard, new_msg); | |
280 | |
281 msn_message_destroy(new_msg); | |
282 | |
283 buddyicon = swboard->buddy_icon_xfer = msn_buddy_icon_xfer_new(); | |
284 | |
285 buddyicon->user = user; | |
286 msn_user_ref(buddyicon->user); | |
287 | |
288 buddyicon->md5sum = g_strdup(md5sum); | |
289 buddyicon->total_size = atoi(base64_size_s); | |
290 buddyicon->file_size = atoi(size_s); | |
291 | |
292 buddyicon->data = g_malloc(buddyicon->total_size + 1); | |
293 } | |
294 else if (!strcmp(command, "REQUEST")) { | |
295 swboard = (MsnSwitchBoard *)servconn->data; | |
296 | |
297 swboard->hidden = TRUE; | |
298 | |
299 swboard->buddy_icon_xfer = buddyicon = msn_buddy_icon_xfer_new(); | |
300 | |
301 if (!__get_buddy_icon_info(gc->account, | |
302 &buddyicon->data, | |
303 &buddyicon->md5sum, | |
304 &buddyicon->file_size, | |
305 &buddyicon->total_size)) { | |
306 | |
307 msn_buddy_icon_xfer_destroy(buddyicon); | |
308 | |
309 new_msg = msn_message_new(); | |
310 msn_message_set_content_type(new_msg, "application/x-buddyicon"); | |
311 msn_message_set_receiver(new_msg, msn_message_get_sender(msg)); | |
312 msn_message_set_charset(new_msg, NULL); | |
313 msn_message_set_attr(new_msg, "User-Agent", NULL); | |
314 | |
315 msn_message_set_body(new_msg, "Command: CANCEL\r\n"); | |
316 | |
317 if ((swboard = msn_session_open_switchboard(session)) == NULL) { | |
318 msn_message_destroy(new_msg); | |
319 | |
320 hide_login_progress(gc, _("Write error")); | |
321 signoff(gc); | |
322 | |
323 return FALSE; | |
324 } | |
325 | |
326 swboard->hidden = TRUE; | |
327 | |
328 msn_switchboard_send_msg(swboard, new_msg); | |
329 | |
330 msn_message_destroy(new_msg); | |
331 | |
332 msn_switchboard_destroy(swboard); | |
333 } | |
334 | |
335 return __send_icon_data(swboard, buddyicon); | |
336 } | |
337 else if (!strcmp(command, "ACK")) { | |
338 swboard = (MsnSwitchBoard *)servconn->data; | |
339 | |
340 buddyicon = swboard->buddy_icon_xfer; | |
341 | |
342 if (buddyicon != NULL) | |
343 return __send_icon_data(swboard, buddyicon); | |
344 } | |
345 else if (!strcmp(command, "COMPLETE")) { | |
346 const char *passport; | |
347 char *icon; | |
348 int icon_len; | |
349 | |
350 swboard = (MsnSwitchBoard *)servconn->data; | |
351 | |
352 buddyicon = swboard->buddy_icon_xfer; | |
353 | |
354 passport = msn_user_get_passport(buddyicon->user); | |
355 swboard->hidden = TRUE; | |
356 | |
357 frombase64(buddyicon->data, &icon, &icon_len); | |
358 | |
359 if ((b = gaim_find_buddy(gc->account, passport)) != NULL) { | |
360 gaim_buddy_set_setting(b, "icon_checksum", buddyicon->md5sum); | |
361 gaim_blist_save(); | |
362 } | |
363 | |
364 set_icon_data(gc, passport, icon, icon_len); | |
365 | |
366 g_free(icon); | |
367 | |
368 msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); | |
369 swboard->buddy_icon_xfer = NULL; | |
370 | |
371 msn_switchboard_destroy(swboard); | |
372 } | |
373 else if (!strcmp(command, "CANCEL")) { | |
374 swboard = (MsnSwitchBoard *)servconn->data; | |
375 | |
376 msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); | |
377 swboard->buddy_icon_xfer = NULL; | |
378 | |
379 msn_switchboard_destroy(swboard); | |
380 } | |
381 else { | |
382 gaim_debug(GAIM_DEBUG_ERROR, "msn", | |
383 "Unknown buddy icon message command: %s\n", command); | |
384 } | |
385 | |
386 return TRUE; | |
387 } | |
388 | |
389 static gboolean | |
390 __process_data(MsnServConn *servconn, const MsnMessage *msg) | |
391 { | |
392 struct gaim_connection *gc = servconn->session->account->gc; | |
393 MsnSwitchBoard *swboard; | |
394 MsnBuddyIconXfer *buddyicon; | |
395 MsnMessage *ack_msg; | |
396 const char *data; | |
397 int len; | |
398 | |
399 swboard = (MsnSwitchBoard *)servconn->data; | |
400 buddyicon = swboard->buddy_icon_xfer; | |
401 | |
402 data = msn_message_get_body(msg) + 4; | |
403 | |
404 len = strlen(data); | |
405 | |
406 /* Copy the data into our buffer. */ | |
407 strncpy(buddyicon->data + buddyicon->bytes_xfer, data, | |
408 buddyicon->total_size - buddyicon->bytes_xfer); | |
409 | |
410 buddyicon->bytes_xfer += len; | |
411 | |
412 /* Acknowledge this data. */ | |
413 ack_msg = msn_message_new(); | |
414 msn_message_set_content_type(ack_msg, "application/x-buddyicon"); | |
415 msn_message_set_receiver(ack_msg, msn_message_get_sender(msg)); | |
416 msn_message_set_charset(ack_msg, NULL); | |
417 msn_message_set_attr(ack_msg, "User-Agent", NULL); | |
418 msn_message_set_body(ack_msg, "Command: ACK\r\n"); | |
419 | |
420 if (!msn_switchboard_send_msg(swboard, ack_msg)) { | |
421 msn_message_destroy(ack_msg); | |
422 | |
423 msn_buddy_icon_xfer_destroy(swboard->buddy_icon_xfer); | |
424 swboard->buddy_icon_xfer = NULL; | |
425 | |
426 hide_login_progress(gc, _("Write error")); | |
427 signoff(gc); | |
428 | |
429 return FALSE; | |
430 } | |
431 | |
432 msn_message_destroy(ack_msg); | |
433 | |
434 return TRUE; | |
435 } | |
436 | |
437 MsnBuddyIconXfer * | |
438 msn_buddy_icon_xfer_new(void) | |
439 { | |
440 return g_new0(MsnBuddyIconXfer, 1); | |
441 } | |
442 | |
443 void | |
444 msn_buddy_icon_xfer_destroy(MsnBuddyIconXfer *xfer) | |
445 { | |
446 g_return_if_fail(xfer != NULL); | |
447 | |
448 if (xfer->user != NULL) | |
449 msn_user_unref(xfer->user); | |
450 | |
451 if (xfer->data != NULL) | |
452 g_free(xfer->data); | |
453 | |
454 g_free(xfer); | |
455 } | |
456 | |
457 gboolean | |
458 msn_buddy_icon_msg(MsnServConn *servconn, const MsnMessage *msg) | |
459 { | |
460 if (!strncmp(msn_message_get_body(msg), "ICON", 4)) | |
461 return __process_data(servconn, msg); | |
462 else | |
463 return __process_invite(servconn, msg); | |
464 } | |
465 | |
466 void | |
467 msn_buddy_icon_invite(MsnSwitchBoard *swboard) | |
468 { | |
469 struct gaim_account *account = swboard->servconn->session->account; | |
470 struct gaim_connection *gc = account->gc; | |
471 MsnMessage *msg; | |
472 char buf[MSN_BUF_LEN]; | |
473 char *md5sum; | |
474 int file_size, base64_size; | |
475 | |
476 g_return_if_fail(swboard != NULL); | |
477 | |
478 if (*account->iconfile == '\0') | |
479 return; /* We don't have an icon to send. */ | |
480 | |
481 if (!__get_buddy_icon_info(account, NULL, &md5sum, | |
482 &file_size, &base64_size)) { | |
483 return; | |
484 } | |
485 | |
486 msg = msn_message_new(); | |
487 msn_message_set_content_type(msg, "application/x-buddyicon"); | |
488 msn_message_set_receiver(msg, msn_message_get_sender(msg)); | |
489 msn_message_set_charset(msg, NULL); | |
490 msn_message_set_attr(msg, "User-Agent", NULL); | |
491 | |
492 g_snprintf(buf, sizeof(buf), | |
493 "Command: INVITE\r\n" | |
494 "MD5SUM: %s\r\n" | |
495 "File-Size: %d\r\n" | |
496 "Base64-Size: %d\r\n", | |
497 md5sum, file_size, base64_size); | |
498 | |
499 g_free(md5sum); | |
500 | |
501 msn_message_set_body(msg, buf); | |
502 | |
503 if (!msn_switchboard_send_msg(swboard, msg)) { | |
504 msn_message_destroy(msg); | |
505 | |
506 hide_login_progress(gc, _("Write error")); | |
507 signoff(gc); | |
508 | |
509 return; | |
510 } | |
511 | |
512 msn_message_destroy(msg); | |
513 } |