5351
|
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 }
|