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 }