Mercurial > pidgin.yaz
comparison libpurple/protocols/msn/slp.c @ 31292:47b6eda87723
propagate from branch 'im.pidgin.pidgin' (head 07d0765c444a097af45c2650f54323afb900a07b)
to branch 'im.pidgin.soc.2010.msn-tlc' (head f3998422a4724ab424e4e2328f58fc0504856557)
author | masca@cpw.pidgin.im |
---|---|
date | Mon, 19 Jul 2010 21:11:32 +0000 |
parents | 7b1b7a4e0bb4 |
children | 23be655cc688 |
comparison
equal
deleted
inserted
replaced
30698:e874875a74a7 | 31292:47b6eda87723 |
---|---|
19 * | 19 * |
20 * You should have received a copy of the GNU General Public License | 20 * You should have received a copy of the GNU General Public License |
21 * along with this program; if not, write to the Free Software | 21 * along with this program; if not, write to the Free Software |
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA | 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
23 */ | 23 */ |
24 #include "msn.h" | 24 |
25 #include "internal.h" | |
26 #include "debug.h" | |
27 | |
25 #include "slp.h" | 28 #include "slp.h" |
26 #include "slpcall.h" | 29 #include "slpcall.h" |
27 #include "slpmsg.h" | 30 #include "slpmsg.h" |
28 #include "msnutils.h" | 31 #include "msnutils.h" |
29 | 32 |
30 #include "object.h" | 33 #include "object.h" |
31 #include "user.h" | 34 #include "user.h" |
32 #include "switchboard.h" | 35 #include "sbconn.h" |
33 #include "directconn.h" | 36 #include "directconn.h" |
34 | 37 #include "p2p.h" |
35 #include "smiley.h" | 38 #include "xfer.h" |
36 | 39 |
37 /* seconds to delay between sending buddy icon requests to the server. */ | 40 /* seconds to delay between sending buddy icon requests to the server. */ |
38 #define BUDDY_ICON_DELAY 20 | 41 #define BUDDY_ICON_DELAY 20 |
39 | 42 |
40 static void request_user_display(MsnUser *user); | |
41 | |
42 | |
43 /************************************************************************** | |
44 * Util | |
45 **************************************************************************/ | |
46 | |
47 static char * | |
48 get_token(const char *str, const char *start, const char *end) | |
49 { | |
50 const char *c, *c2; | |
51 | |
52 if ((c = strstr(str, start)) == NULL) | |
53 return NULL; | |
54 | |
55 c += strlen(start); | |
56 | |
57 if (end != NULL) | |
58 { | |
59 if ((c2 = strstr(c, end)) == NULL) | |
60 return NULL; | |
61 | |
62 return g_strndup(c, c2 - c); | |
63 } | |
64 else | |
65 { | |
66 /* This has to be changed */ | |
67 return g_strdup(c); | |
68 } | |
69 | |
70 } | |
71 | |
72 /************************************************************************** | |
73 * Xfer | |
74 **************************************************************************/ | |
75 | |
76 static void | |
77 msn_xfer_init(PurpleXfer *xfer) | |
78 { | |
79 MsnSlpCall *slpcall; | |
80 /* MsnSlpLink *slplink; */ | |
81 char *content; | |
82 | |
83 purple_debug_info("msn", "xfer_init\n"); | |
84 | |
85 slpcall = xfer->data; | |
86 | |
87 /* Send Ok */ | |
88 content = g_strdup_printf("SessionID: %lu\r\n\r\n", | |
89 slpcall->session_id); | |
90 | |
91 msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", | |
92 content); | |
93 | |
94 g_free(content); | |
95 msn_slplink_send_queued_slpmsgs(slpcall->slplink); | |
96 } | |
97 | |
98 void | |
99 msn_xfer_cancel(PurpleXfer *xfer) | |
100 { | |
101 MsnSlpCall *slpcall; | |
102 char *content; | |
103 | |
104 g_return_if_fail(xfer != NULL); | |
105 g_return_if_fail(xfer->data != NULL); | |
106 | |
107 slpcall = xfer->data; | |
108 | |
109 if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) | |
110 { | |
111 if (slpcall->started) | |
112 { | |
113 msn_slpcall_close(slpcall); | |
114 } | |
115 else | |
116 { | |
117 content = g_strdup_printf("SessionID: %lu\r\n\r\n", | |
118 slpcall->session_id); | |
119 | |
120 msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", | |
121 content); | |
122 | |
123 g_free(content); | |
124 msn_slplink_send_queued_slpmsgs(slpcall->slplink); | |
125 | |
126 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) | |
127 slpcall->wasted = TRUE; | |
128 else | |
129 msn_slpcall_destroy(slpcall); | |
130 } | |
131 } | |
132 } | |
133 | |
134 gssize | |
135 msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer) | |
136 { | |
137 MsnSlpCall *slpcall; | |
138 | |
139 g_return_val_if_fail(xfer != NULL, -1); | |
140 g_return_val_if_fail(data != NULL, -1); | |
141 g_return_val_if_fail(len > 0, -1); | |
142 | |
143 g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1); | |
144 | |
145 slpcall = xfer->data; | |
146 /* Not sure I trust it'll be there */ | |
147 g_return_val_if_fail(slpcall != NULL, -1); | |
148 | |
149 g_return_val_if_fail(slpcall->xfer_msg != NULL, -1); | |
150 | |
151 slpcall->u.outgoing.len = len; | |
152 slpcall->u.outgoing.data = data; | |
153 msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg); | |
154 msn_message_unref(slpcall->xfer_msg->msg); | |
155 return MIN(1202, len); | |
156 } | |
157 | |
158 gssize | |
159 msn_xfer_read(guchar **data, PurpleXfer *xfer) | |
160 { | |
161 MsnSlpCall *slpcall; | |
162 gsize len; | |
163 | |
164 g_return_val_if_fail(xfer != NULL, -1); | |
165 g_return_val_if_fail(data != NULL, -1); | |
166 | |
167 g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1); | |
168 | |
169 slpcall = xfer->data; | |
170 /* Not sure I trust it'll be there */ | |
171 g_return_val_if_fail(slpcall != NULL, -1); | |
172 | |
173 /* Just pass up the whole GByteArray. We'll make another. */ | |
174 *data = slpcall->u.incoming_data->data; | |
175 len = slpcall->u.incoming_data->len; | |
176 | |
177 g_byte_array_free(slpcall->u.incoming_data, FALSE); | |
178 slpcall->u.incoming_data = g_byte_array_new(); | |
179 | |
180 return len; | |
181 } | |
182 | |
183 void | |
184 msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session) | |
185 { | |
186 if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) && | |
187 (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) && | |
188 (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL)) | |
189 { | |
190 purple_xfer_cancel_remote(slpcall->xfer); | |
191 } | |
192 } | |
193 | |
194 void | |
195 msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body, | |
196 gsize size) | |
197 { | |
198 PurpleXfer *xfer = slpcall->xfer; | |
199 | |
200 purple_xfer_set_completed(xfer, TRUE); | |
201 purple_xfer_end(xfer); | |
202 } | |
203 | 43 |
204 /************************************************************************** | 44 /************************************************************************** |
205 * SLP Control | 45 * SLP Control |
206 **************************************************************************/ | 46 **************************************************************************/ |
207 | 47 |
243 slpmsg->text_body = TRUE; | 83 slpmsg->text_body = TRUE; |
244 | 84 |
245 msn_slplink_queue_slpmsg(slplink, slpmsg); | 85 msn_slplink_queue_slpmsg(slplink, slpmsg); |
246 } | 86 } |
247 | 87 |
248 /* XXX: this could be improved if we tracked custom smileys | |
249 * per-protocol, per-account, per-session or (ideally) per-conversation | |
250 */ | |
251 static PurpleStoredImage * | |
252 find_valid_emoticon(PurpleAccount *account, const char *path) | |
253 { | |
254 GList *smileys; | |
255 | |
256 if (!purple_account_get_bool(account, "custom_smileys", TRUE)) | |
257 return NULL; | |
258 | |
259 smileys = purple_smileys_get_all(); | |
260 | |
261 for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { | |
262 PurpleSmiley *smiley; | |
263 PurpleStoredImage *img; | |
264 | |
265 smiley = smileys->data; | |
266 img = purple_smiley_get_stored_image(smiley); | |
267 | |
268 if (purple_strequal(path, purple_imgstore_get_filename(img))) { | |
269 g_list_free(smileys); | |
270 return img; | |
271 } | |
272 | |
273 purple_imgstore_unref(img); | |
274 } | |
275 | |
276 purple_debug_error("msn", "Received illegal request for file %s\n", path); | |
277 return NULL; | |
278 } | |
279 | |
280 static char * | |
281 parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype) | |
282 { | |
283 char *nonce; | |
284 | |
285 *ntype = DC_NONCE_UNKNOWN; | |
286 | |
287 nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); | |
288 if (nonce) { | |
289 *ntype = DC_NONCE_SHA1; | |
290 } else { | |
291 guint32 n1, n6; | |
292 guint16 n2, n3, n4, n5; | |
293 nonce = get_token(content, "Nonce: {", "}\r\n"); | |
294 if (nonce | |
295 && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x", | |
296 &n1, &n2, &n3, &n4, &n5, &n6) == 6) { | |
297 *ntype = DC_NONCE_PLAIN; | |
298 g_free(nonce); | |
299 nonce = g_malloc(16); | |
300 *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1); | |
301 *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2); | |
302 *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3); | |
303 *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4); | |
304 *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5); | |
305 *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6); | |
306 } else { | |
307 /* Invalid nonce, so ignore request */ | |
308 g_free(nonce); | |
309 nonce = NULL; | |
310 } | |
311 } | |
312 | |
313 return nonce; | |
314 } | |
315 | |
316 static void | |
317 msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content) | |
318 { | |
319 /* A direct connection negotiation response */ | |
320 char *bridge; | |
321 char *nonce; | |
322 char *listening; | |
323 MsnDirectConn *dc = slpcall->slplink->dc; | |
324 MsnDirectConnNonceType ntype; | |
325 | |
326 purple_debug_info("msn", "process_transresp\n"); | |
327 | |
328 /* Direct connections are disabled. */ | |
329 if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE)) | |
330 return; | |
331 | |
332 g_return_if_fail(dc != NULL); | |
333 g_return_if_fail(dc->state == DC_STATE_CLOSED); | |
334 | |
335 bridge = get_token(content, "Bridge: ", "\r\n"); | |
336 nonce = parse_dc_nonce(content, &ntype); | |
337 listening = get_token(content, "Listening: ", "\r\n"); | |
338 if (listening && bridge && !strcmp(bridge, "TCPv1")) { | |
339 /* Ok, the client supports direct TCP connection */ | |
340 | |
341 /* We always need this. */ | |
342 if (ntype == DC_NONCE_SHA1) { | |
343 strncpy(dc->remote_nonce, nonce, 36); | |
344 dc->remote_nonce[36] = '\0'; | |
345 } | |
346 | |
347 if (!strcasecmp(listening, "false")) { | |
348 if (dc->listen_data != NULL) { | |
349 /* | |
350 * We'll listen for incoming connections but | |
351 * the listening socket isn't ready yet so we cannot | |
352 * send the INVITE packet now. Put the slpcall into waiting mode | |
353 * and let the callback send the invite. | |
354 */ | |
355 slpcall->wait_for_socket = TRUE; | |
356 | |
357 } else if (dc->listenfd != -1) { | |
358 /* The listening socket is ready. Send the INVITE here. */ | |
359 msn_dc_send_invite(dc); | |
360 | |
361 } else { | |
362 /* We weren't able to create a listener either. Use SB. */ | |
363 msn_dc_fallback_to_sb(dc); | |
364 } | |
365 | |
366 } else { | |
367 /* | |
368 * We should connect to the client so parse | |
369 * IP/port from response. | |
370 */ | |
371 char *ip, *port_str; | |
372 int port = 0; | |
373 | |
374 if (ntype == DC_NONCE_PLAIN) { | |
375 /* Only needed for listening side. */ | |
376 memcpy(dc->nonce, nonce, 16); | |
377 } | |
378 | |
379 /* Cancel any listen attempts because we don't need them. */ | |
380 if (dc->listenfd_handle != 0) { | |
381 purple_input_remove(dc->listenfd_handle); | |
382 dc->listenfd_handle = 0; | |
383 } | |
384 if (dc->connect_timeout_handle != 0) { | |
385 purple_timeout_remove(dc->connect_timeout_handle); | |
386 dc->connect_timeout_handle = 0; | |
387 } | |
388 if (dc->listenfd != -1) { | |
389 purple_network_remove_port_mapping(dc->listenfd); | |
390 close(dc->listenfd); | |
391 dc->listenfd = -1; | |
392 } | |
393 if (dc->listen_data != NULL) { | |
394 purple_network_listen_cancel(dc->listen_data); | |
395 dc->listen_data = NULL; | |
396 } | |
397 | |
398 /* Save external IP/port for later use. We'll try local connection first. */ | |
399 dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n"); | |
400 port_str = get_token(content, "IPv4External-Port: ", "\r\n"); | |
401 if (port_str) { | |
402 dc->ext_port = atoi(port_str); | |
403 g_free(port_str); | |
404 } | |
405 | |
406 ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); | |
407 port_str = get_token(content, "IPv4Internal-Port: ", "\r\n"); | |
408 if (port_str) { | |
409 port = atoi(port_str); | |
410 g_free(port_str); | |
411 } | |
412 | |
413 if (ip && port) { | |
414 /* Try internal address first */ | |
415 dc->connect_data = purple_proxy_connect( | |
416 NULL, | |
417 slpcall->slplink->session->account, | |
418 ip, | |
419 port, | |
420 msn_dc_connected_to_peer_cb, | |
421 dc | |
422 ); | |
423 | |
424 if (dc->connect_data) { | |
425 /* Add connect timeout handle */ | |
426 dc->connect_timeout_handle = purple_timeout_add_seconds( | |
427 DC_OUTGOING_TIMEOUT, | |
428 msn_dc_outgoing_connection_timeout_cb, | |
429 dc | |
430 ); | |
431 } else { | |
432 /* | |
433 * Connection failed | |
434 * Try external IP/port (if specified) | |
435 */ | |
436 msn_dc_outgoing_connection_timeout_cb(dc); | |
437 } | |
438 | |
439 } else { | |
440 /* | |
441 * Omitted or invalid internal IP address / port | |
442 * Try external IP/port (if specified) | |
443 */ | |
444 msn_dc_outgoing_connection_timeout_cb(dc); | |
445 } | |
446 | |
447 g_free(ip); | |
448 } | |
449 | |
450 } else { | |
451 /* | |
452 * Invalid direct connect invitation or | |
453 * TCP connection is not supported | |
454 */ | |
455 } | |
456 | |
457 g_free(listening); | |
458 g_free(nonce); | |
459 g_free(bridge); | |
460 | |
461 return; | |
462 } | |
463 | |
464 static void | |
465 got_sessionreq(MsnSlpCall *slpcall, const char *branch, | |
466 const char *euf_guid, const char *context) | |
467 { | |
468 gboolean accepted = FALSE; | |
469 | |
470 if (!strcmp(euf_guid, MSN_OBJ_GUID)) | |
471 { | |
472 /* Emoticon or UserDisplay */ | |
473 char *content; | |
474 gsize len; | |
475 MsnSlpLink *slplink; | |
476 MsnSlpMessage *slpmsg; | |
477 MsnObject *obj; | |
478 char *msnobj_data; | |
479 PurpleStoredImage *img = NULL; | |
480 int type; | |
481 | |
482 /* Send Ok */ | |
483 content = g_strdup_printf("SessionID: %lu\r\n\r\n", | |
484 slpcall->session_id); | |
485 | |
486 msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody", | |
487 content); | |
488 | |
489 g_free(content); | |
490 | |
491 slplink = slpcall->slplink; | |
492 | |
493 msnobj_data = (char *)purple_base64_decode(context, &len); | |
494 obj = msn_object_new_from_string(msnobj_data); | |
495 type = msn_object_get_type(obj); | |
496 g_free(msnobj_data); | |
497 if (type == MSN_OBJECT_EMOTICON) { | |
498 img = find_valid_emoticon(slplink->session->account, obj->location); | |
499 } else if (type == MSN_OBJECT_USERTILE) { | |
500 img = msn_object_get_image(obj); | |
501 if (img) | |
502 purple_imgstore_ref(img); | |
503 } | |
504 msn_object_destroy(obj); | |
505 | |
506 if (img != NULL) { | |
507 /* DATA PREP */ | |
508 slpmsg = msn_slpmsg_new(slplink); | |
509 slpmsg->slpcall = slpcall; | |
510 slpmsg->session_id = slpcall->session_id; | |
511 msn_slpmsg_set_body(slpmsg, NULL, 4); | |
512 slpmsg->info = "SLP DATA PREP"; | |
513 msn_slplink_queue_slpmsg(slplink, slpmsg); | |
514 | |
515 /* DATA */ | |
516 slpmsg = msn_slpmsg_new(slplink); | |
517 slpmsg->slpcall = slpcall; | |
518 slpmsg->flags = 0x20; | |
519 slpmsg->info = "SLP DATA"; | |
520 msn_slpmsg_set_image(slpmsg, img); | |
521 msn_slplink_queue_slpmsg(slplink, slpmsg); | |
522 purple_imgstore_unref(img); | |
523 | |
524 accepted = TRUE; | |
525 | |
526 } else { | |
527 purple_debug_error("msn", "Wrong object.\n"); | |
528 } | |
529 } | |
530 | |
531 else if (!strcmp(euf_guid, MSN_FT_GUID)) | |
532 { | |
533 /* File Transfer */ | |
534 PurpleAccount *account; | |
535 PurpleXfer *xfer; | |
536 MsnFileContext *header; | |
537 gsize bin_len; | |
538 guint32 file_size; | |
539 char *file_name; | |
540 | |
541 account = slpcall->slplink->session->account; | |
542 | |
543 slpcall->end_cb = msn_xfer_end_cb; | |
544 slpcall->branch = g_strdup(branch); | |
545 | |
546 slpcall->pending = TRUE; | |
547 | |
548 xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, | |
549 slpcall->slplink->remote_user); | |
550 | |
551 header = (MsnFileContext *)purple_base64_decode(context, &bin_len); | |
552 if (bin_len >= sizeof(MsnFileContext) - 1 && | |
553 (header->version == 2 || | |
554 (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) { | |
555 file_size = GUINT64_FROM_LE(header->file_size); | |
556 | |
557 file_name = g_convert((const gchar *)&header->file_name, | |
558 MAX_FILE_NAME_LEN * 2, | |
559 "UTF-8", "UTF-16LE", | |
560 NULL, NULL, NULL); | |
561 | |
562 purple_xfer_set_filename(xfer, file_name ? file_name : ""); | |
563 g_free(file_name); | |
564 purple_xfer_set_size(xfer, file_size); | |
565 purple_xfer_set_init_fnc(xfer, msn_xfer_init); | |
566 purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel); | |
567 purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel); | |
568 purple_xfer_set_read_fnc(xfer, msn_xfer_read); | |
569 purple_xfer_set_write_fnc(xfer, msn_xfer_write); | |
570 | |
571 slpcall->u.incoming_data = g_byte_array_new(); | |
572 | |
573 slpcall->xfer = xfer; | |
574 purple_xfer_ref(slpcall->xfer); | |
575 | |
576 xfer->data = slpcall; | |
577 | |
578 if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) { | |
579 purple_xfer_set_thumbnail(xfer, &header->preview, | |
580 bin_len - sizeof(MsnFileContext), | |
581 "image/png"); | |
582 } | |
583 | |
584 purple_xfer_request(xfer); | |
585 } | |
586 g_free(header); | |
587 | |
588 accepted = TRUE; | |
589 | |
590 } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) { | |
591 purple_debug_info("msn", "Cam request.\n"); | |
592 if (slpcall && slpcall->slplink && | |
593 slpcall->slplink->session) { | |
594 PurpleConversation *conv; | |
595 gchar *from = slpcall->slplink->remote_user; | |
596 conv = purple_find_conversation_with_account( | |
597 PURPLE_CONV_TYPE_IM, from, | |
598 slpcall->slplink->session->account); | |
599 if (conv) { | |
600 char *buf; | |
601 buf = g_strdup_printf( | |
602 _("%s requests to view your " | |
603 "webcam, but this request is " | |
604 "not yet supported."), from); | |
605 purple_conversation_write(conv, NULL, buf, | |
606 PURPLE_MESSAGE_SYSTEM | | |
607 PURPLE_MESSAGE_NOTIFY, | |
608 time(NULL)); | |
609 g_free(buf); | |
610 } | |
611 } | |
612 | |
613 } else if (!strcmp(euf_guid, MSN_CAM_GUID)) { | |
614 purple_debug_info("msn", "Cam invite.\n"); | |
615 if (slpcall && slpcall->slplink && | |
616 slpcall->slplink->session) { | |
617 PurpleConversation *conv; | |
618 gchar *from = slpcall->slplink->remote_user; | |
619 conv = purple_find_conversation_with_account( | |
620 PURPLE_CONV_TYPE_IM, from, | |
621 slpcall->slplink->session->account); | |
622 if (conv) { | |
623 char *buf; | |
624 buf = g_strdup_printf( | |
625 _("%s invited you to view his/her webcam, but " | |
626 "this is not yet supported."), from); | |
627 purple_conversation_write(conv, NULL, buf, | |
628 PURPLE_MESSAGE_SYSTEM | | |
629 PURPLE_MESSAGE_NOTIFY, | |
630 time(NULL)); | |
631 g_free(buf); | |
632 } | |
633 } | |
634 | |
635 } else | |
636 purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); | |
637 | |
638 if (!accepted) { | |
639 char *content = g_strdup_printf("SessionID: %lu\r\n\r\n", | |
640 slpcall->session_id); | |
641 msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content); | |
642 g_free(content); | |
643 } | |
644 } | |
645 | |
646 void | |
647 send_bye(MsnSlpCall *slpcall, const char *type) | |
648 { | |
649 MsnSlpLink *slplink; | |
650 PurpleAccount *account; | |
651 MsnSlpMessage *slpmsg; | |
652 char *header; | |
653 | |
654 slplink = slpcall->slplink; | |
655 | |
656 g_return_if_fail(slplink != NULL); | |
657 | |
658 account = slplink->session->account; | |
659 | |
660 header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0", | |
661 purple_account_get_username(account)); | |
662 | |
663 slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, | |
664 "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32", | |
665 type, | |
666 "\r\n"); | |
667 g_free(header); | |
668 | |
669 slpmsg->info = "SLP BYE"; | |
670 slpmsg->text_body = TRUE; | |
671 | |
672 msn_slplink_queue_slpmsg(slplink, slpmsg); | |
673 } | |
674 | |
675 static void | |
676 got_invite(MsnSlpCall *slpcall, | |
677 const char *branch, const char *type, const char *content) | |
678 { | |
679 MsnSlpLink *slplink; | |
680 | |
681 slplink = slpcall->slplink; | |
682 | |
683 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) | |
684 { | |
685 char *euf_guid, *context; | |
686 char *temp; | |
687 | |
688 euf_guid = get_token(content, "EUF-GUID: {", "}\r\n"); | |
689 | |
690 temp = get_token(content, "SessionID: ", "\r\n"); | |
691 if (temp != NULL) | |
692 slpcall->session_id = atoi(temp); | |
693 g_free(temp); | |
694 | |
695 temp = get_token(content, "AppID: ", "\r\n"); | |
696 if (temp != NULL) | |
697 slpcall->app_id = atoi(temp); | |
698 g_free(temp); | |
699 | |
700 context = get_token(content, "Context: ", "\r\n"); | |
701 | |
702 if (context != NULL) | |
703 got_sessionreq(slpcall, branch, euf_guid, context); | |
704 | |
705 g_free(context); | |
706 g_free(euf_guid); | |
707 } | |
708 else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) | |
709 { | |
710 /* A direct connection negotiation request */ | |
711 char *bridges; | |
712 char *nonce; | |
713 MsnDirectConnNonceType ntype; | |
714 | |
715 purple_debug_info("msn", "got_invite: transreqbody received\n"); | |
716 | |
717 /* Direct connections may be disabled. */ | |
718 if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) { | |
719 msn_slp_send_ok(slpcall, branch, | |
720 "application/x-msnmsgr-transrespbody", | |
721 "Bridge: TCPv1\r\n" | |
722 "Listening: false\r\n" | |
723 "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" | |
724 "\r\n"); | |
725 msn_slpcall_session_init(slpcall); | |
726 | |
727 return; | |
728 } | |
729 | |
730 /* Don't do anything if we already have a direct connection */ | |
731 if (slplink->dc != NULL) | |
732 return; | |
733 | |
734 bridges = get_token(content, "Bridges: ", "\r\n"); | |
735 nonce = parse_dc_nonce(content, &ntype); | |
736 if (bridges && strstr(bridges, "TCPv1") != NULL) { | |
737 /* | |
738 * Ok, the client supports direct TCP connection | |
739 * Try to create a listening port | |
740 */ | |
741 MsnDirectConn *dc; | |
742 | |
743 dc = msn_dc_new(slpcall); | |
744 if (ntype == DC_NONCE_PLAIN) { | |
745 /* There is only one nonce for plain auth. */ | |
746 dc->nonce_type = ntype; | |
747 memcpy(dc->nonce, nonce, 16); | |
748 } else if (ntype == DC_NONCE_SHA1) { | |
749 /* Each side has a nonce in SHA1 auth. */ | |
750 dc->nonce_type = ntype; | |
751 strncpy(dc->remote_nonce, nonce, 36); | |
752 dc->remote_nonce[36] = '\0'; | |
753 } | |
754 | |
755 dc->listen_data = purple_network_listen_range( | |
756 0, 0, | |
757 SOCK_STREAM, | |
758 msn_dc_listen_socket_created_cb, | |
759 dc | |
760 ); | |
761 | |
762 if (dc->listen_data == NULL) { | |
763 /* Listen socket creation failed */ | |
764 | |
765 purple_debug_info("msn", "got_invite: listening failed\n"); | |
766 | |
767 if (dc->nonce_type != DC_NONCE_PLAIN) | |
768 msn_slp_send_ok(slpcall, branch, | |
769 "application/x-msnmsgr-transrespbody", | |
770 "Bridge: TCPv1\r\n" | |
771 "Listening: false\r\n" | |
772 "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n" | |
773 "\r\n"); | |
774 else | |
775 msn_slp_send_ok(slpcall, branch, | |
776 "application/x-msnmsgr-transrespbody", | |
777 "Bridge: TCPv1\r\n" | |
778 "Listening: false\r\n" | |
779 "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" | |
780 "\r\n"); | |
781 | |
782 } else { | |
783 /* | |
784 * Listen socket created successfully. | |
785 * Don't send anything here because we don't know the parameters | |
786 * of the created socket yet. msn_dc_send_ok will be called from | |
787 * the callback function: dc_listen_socket_created_cb | |
788 */ | |
789 purple_debug_info("msn", "got_invite: listening socket created\n"); | |
790 | |
791 dc->send_connection_info_msg_cb = msn_dc_send_ok; | |
792 slpcall->wait_for_socket = TRUE; | |
793 } | |
794 | |
795 } else { | |
796 /* | |
797 * Invalid direct connect invitation or | |
798 * TCP connection is not supported. | |
799 */ | |
800 } | |
801 | |
802 g_free(nonce); | |
803 g_free(bridges); | |
804 } | |
805 else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) | |
806 { | |
807 /* A direct connection negotiation response */ | |
808 msn_slp_process_transresp(slpcall, content); | |
809 } | |
810 } | |
811 | |
812 static void | |
813 got_ok(MsnSlpCall *slpcall, | |
814 const char *type, const char *content) | |
815 { | |
816 g_return_if_fail(slpcall != NULL); | |
817 g_return_if_fail(type != NULL); | |
818 | |
819 if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) | |
820 { | |
821 char *content; | |
822 char *header; | |
823 char *nonce = NULL; | |
824 MsnSession *session = slpcall->slplink->session; | |
825 MsnSlpMessage *msg; | |
826 MsnDirectConn *dc; | |
827 MsnUser *user; | |
828 | |
829 if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) { | |
830 /* Don't attempt a direct connection if disabled. */ | |
831 msn_slpcall_session_init(slpcall); | |
832 return; | |
833 } | |
834 | |
835 if (slpcall->slplink->dc != NULL) { | |
836 /* If we already have an established direct connection | |
837 * then just start the transfer. | |
838 */ | |
839 msn_slpcall_session_init(slpcall); | |
840 return; | |
841 } | |
842 | |
843 user = msn_userlist_find_user(session->userlist, | |
844 slpcall->slplink->remote_user); | |
845 if (!user || !(user->clientid & 0xF0000000)) { | |
846 /* Just start a normal SB transfer. */ | |
847 msn_slpcall_session_init(slpcall); | |
848 return; | |
849 } | |
850 | |
851 /* Try direct file transfer by sending a second INVITE */ | |
852 dc = msn_dc_new(slpcall); | |
853 slpcall->branch = rand_guid(); | |
854 | |
855 dc->listen_data = purple_network_listen_range( | |
856 0, 0, | |
857 SOCK_STREAM, | |
858 msn_dc_listen_socket_created_cb, | |
859 dc | |
860 ); | |
861 | |
862 header = g_strdup_printf( | |
863 "INVITE MSNMSGR:%s MSNSLP/1.0", | |
864 slpcall->slplink->remote_user | |
865 ); | |
866 | |
867 if (dc->nonce_type == DC_NONCE_SHA1) | |
868 nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash); | |
869 | |
870 if (dc->listen_data == NULL) { | |
871 /* Listen socket creation failed */ | |
872 purple_debug_info("msn", "got_ok: listening failed\n"); | |
873 | |
874 content = g_strdup_printf( | |
875 "Bridges: TCPv1\r\n" | |
876 "NetID: %u\r\n" | |
877 "Conn-Type: IP-Restrict-NAT\r\n" | |
878 "UPnPNat: false\r\n" | |
879 "ICF: false\r\n" | |
880 "%s" | |
881 "\r\n", | |
882 | |
883 rand() % G_MAXUINT32, | |
884 nonce ? nonce : "" | |
885 ); | |
886 | |
887 } else { | |
888 /* Listen socket created successfully. */ | |
889 purple_debug_info("msn", "got_ok: listening socket created\n"); | |
890 | |
891 content = g_strdup_printf( | |
892 "Bridges: TCPv1\r\n" | |
893 "NetID: 0\r\n" | |
894 "Conn-Type: Direct-Connect\r\n" | |
895 "UPnPNat: false\r\n" | |
896 "ICF: false\r\n" | |
897 "%s" | |
898 "\r\n", | |
899 | |
900 nonce ? nonce : "" | |
901 ); | |
902 } | |
903 | |
904 msg = msn_slpmsg_sip_new( | |
905 slpcall, | |
906 0, | |
907 header, | |
908 slpcall->branch, | |
909 "application/x-msnmsgr-transreqbody", | |
910 content | |
911 ); | |
912 msg->info = "DC INVITE"; | |
913 msg->text_body = TRUE; | |
914 g_free(nonce); | |
915 g_free(header); | |
916 g_free(content); | |
917 | |
918 msn_slplink_queue_slpmsg(slpcall->slplink, msg); | |
919 } | |
920 else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) | |
921 { | |
922 /* Do we get this? */ | |
923 purple_debug_info("msn", "OK with transreqbody\n"); | |
924 } | |
925 else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) | |
926 { | |
927 msn_slp_process_transresp(slpcall, content); | |
928 } | |
929 } | |
930 | |
931 static void | |
932 got_error(MsnSlpCall *slpcall, | |
933 const char *error, const char *type, const char *content) | |
934 { | |
935 /* It's not valid. Kill this off. */ | |
936 purple_debug_error("msn", "Received non-OK result: %s\n", | |
937 error ? error : "Unknown"); | |
938 | |
939 if (type && !strcmp(type, "application/x-msnmsgr-transreqbody")) { | |
940 MsnDirectConn *dc = slpcall->slplink->dc; | |
941 if (dc) { | |
942 msn_dc_fallback_to_sb(dc); | |
943 return; | |
944 } | |
945 } | |
946 | |
947 slpcall->wasted = TRUE; | |
948 } | |
949 | |
950 MsnSlpCall * | |
951 msn_slp_sip_recv(MsnSlpLink *slplink, const char *body) | |
952 { | |
953 MsnSlpCall *slpcall; | |
954 | |
955 if (body == NULL) | |
956 { | |
957 purple_debug_warning("msn", "received bogus message\n"); | |
958 return NULL; | |
959 } | |
960 | |
961 if (!strncmp(body, "INVITE", strlen("INVITE"))) | |
962 { | |
963 char *branch; | |
964 char *call_id; | |
965 char *content; | |
966 char *content_type; | |
967 | |
968 /* From: <msnmsgr:buddy@hotmail.com> */ | |
969 #if 0 | |
970 slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); | |
971 #endif | |
972 | |
973 branch = get_token(body, ";branch={", "}"); | |
974 | |
975 call_id = get_token(body, "Call-ID: {", "}"); | |
976 | |
977 #if 0 | |
978 long content_len = -1; | |
979 | |
980 temp = get_token(body, "Content-Length: ", "\r\n"); | |
981 if (temp != NULL) | |
982 content_len = atoi(temp); | |
983 g_free(temp); | |
984 #endif | |
985 content_type = get_token(body, "Content-Type: ", "\r\n"); | |
986 | |
987 content = get_token(body, "\r\n\r\n", NULL); | |
988 | |
989 slpcall = NULL; | |
990 if (branch && call_id) | |
991 { | |
992 slpcall = msn_slplink_find_slp_call(slplink, call_id); | |
993 if (slpcall) | |
994 { | |
995 g_free(slpcall->branch); | |
996 slpcall->branch = g_strdup(branch); | |
997 got_invite(slpcall, branch, content_type, content); | |
998 } | |
999 else if (content_type && content) | |
1000 { | |
1001 slpcall = msn_slpcall_new(slplink); | |
1002 slpcall->id = g_strdup(call_id); | |
1003 got_invite(slpcall, branch, content_type, content); | |
1004 } | |
1005 } | |
1006 | |
1007 g_free(call_id); | |
1008 g_free(branch); | |
1009 g_free(content_type); | |
1010 g_free(content); | |
1011 } | |
1012 else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 "))) | |
1013 { | |
1014 char *content; | |
1015 char *content_type; | |
1016 /* Make sure this is "OK" */ | |
1017 const char *status = body + strlen("MSNSLP/1.0 "); | |
1018 char *call_id; | |
1019 | |
1020 call_id = get_token(body, "Call-ID: {", "}"); | |
1021 slpcall = msn_slplink_find_slp_call(slplink, call_id); | |
1022 g_free(call_id); | |
1023 | |
1024 g_return_val_if_fail(slpcall != NULL, NULL); | |
1025 | |
1026 content_type = get_token(body, "Content-Type: ", "\r\n"); | |
1027 | |
1028 content = get_token(body, "\r\n\r\n", NULL); | |
1029 | |
1030 if (strncmp(status, "200 OK", 6)) | |
1031 { | |
1032 char *error = NULL; | |
1033 const char *c; | |
1034 | |
1035 /* Eww */ | |
1036 if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || | |
1037 (c = strchr(status, '\0'))) | |
1038 { | |
1039 size_t len = c - status; | |
1040 error = g_strndup(status, len); | |
1041 } | |
1042 | |
1043 got_error(slpcall, error, content_type, content); | |
1044 g_free(error); | |
1045 | |
1046 } else { | |
1047 /* Everything's just dandy */ | |
1048 got_ok(slpcall, content_type, content); | |
1049 } | |
1050 | |
1051 g_free(content_type); | |
1052 g_free(content); | |
1053 } | |
1054 else if (!strncmp(body, "BYE", strlen("BYE"))) | |
1055 { | |
1056 char *call_id; | |
1057 | |
1058 call_id = get_token(body, "Call-ID: {", "}"); | |
1059 slpcall = msn_slplink_find_slp_call(slplink, call_id); | |
1060 g_free(call_id); | |
1061 | |
1062 if (slpcall != NULL) | |
1063 slpcall->wasted = TRUE; | |
1064 | |
1065 /* msn_slpcall_destroy(slpcall); */ | |
1066 } | |
1067 else | |
1068 slpcall = NULL; | |
1069 | |
1070 return slpcall; | |
1071 } | |
1072 | |
1073 /************************************************************************** | 88 /************************************************************************** |
1074 * Msg Callbacks | 89 * Msg Callbacks |
1075 **************************************************************************/ | 90 **************************************************************************/ |
1076 | |
1077 void | |
1078 msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg) | |
1079 { | |
1080 MsnSession *session; | |
1081 MsnSlpLink *slplink; | |
1082 const char *data; | |
1083 gsize len; | |
1084 | |
1085 session = cmdproc->servconn->session; | |
1086 slplink = msn_session_get_slplink(session, msg->remote_user); | |
1087 | |
1088 if (slplink->swboard == NULL) | |
1089 { | |
1090 /* | |
1091 * We will need swboard in order to change its flags. If its | |
1092 * NULL, something has probably gone wrong earlier on. I | |
1093 * didn't want to do this, but MSN 7 is somehow causing us | |
1094 * to crash here, I couldn't reproduce it to debug more, | |
1095 * and people are reporting bugs. Hopefully this doesn't | |
1096 * cause more crashes. Stu. | |
1097 */ | |
1098 if (cmdproc->data == NULL) | |
1099 g_warning("msn_p2p_msg cmdproc->data was NULL\n"); | |
1100 else { | |
1101 slplink->swboard = (MsnSwitchBoard *)cmdproc->data; | |
1102 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); | |
1103 } | |
1104 } | |
1105 | |
1106 data = msn_message_get_bin_data(msg, &len); | |
1107 | |
1108 msn_slplink_process_msg(slplink, &msg->msnslp_header, data, len); | |
1109 } | |
1110 | |
1111 static void | |
1112 got_emoticon(MsnSlpCall *slpcall, | |
1113 const guchar *data, gsize size) | |
1114 { | |
1115 PurpleConversation *conv; | |
1116 MsnSwitchBoard *swboard; | |
1117 | |
1118 swboard = slpcall->slplink->swboard; | |
1119 conv = swboard->conv; | |
1120 | |
1121 if (conv) { | |
1122 /* FIXME: it would be better if we wrote the data as we received it | |
1123 instead of all at once, calling write multiple times and | |
1124 close once at the very end | |
1125 */ | |
1126 purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size); | |
1127 purple_conv_custom_smiley_close(conv, slpcall->data_info ); | |
1128 } | |
1129 if (purple_debug_is_verbose()) | |
1130 purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info); | |
1131 } | |
1132 | |
1133 void | |
1134 msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg) | |
1135 { | |
1136 MsnSession *session; | |
1137 MsnSlpLink *slplink; | |
1138 MsnSwitchBoard *swboard; | |
1139 MsnObject *obj; | |
1140 char **tokens; | |
1141 char *smile, *body_str; | |
1142 const char *body, *who, *sha1; | |
1143 guint tok; | |
1144 size_t body_len; | |
1145 | |
1146 PurpleConversation *conv; | |
1147 | |
1148 session = cmdproc->servconn->session; | |
1149 | |
1150 if (!purple_account_get_bool(session->account, "custom_smileys", TRUE)) | |
1151 return; | |
1152 | |
1153 swboard = cmdproc->data; | |
1154 conv = swboard->conv; | |
1155 | |
1156 body = msn_message_get_bin_data(msg, &body_len); | |
1157 if (!body || !body_len) | |
1158 return; | |
1159 body_str = g_strndup(body, body_len); | |
1160 | |
1161 /* MSN Messenger 7 may send more than one MSNObject in a single message... | |
1162 * Maybe 10 tokens is a reasonable max value. */ | |
1163 tokens = g_strsplit(body_str, "\t", 10); | |
1164 | |
1165 g_free(body_str); | |
1166 | |
1167 for (tok = 0; tok < 9; tok += 2) { | |
1168 if (tokens[tok] == NULL || tokens[tok + 1] == NULL) { | |
1169 break; | |
1170 } | |
1171 | |
1172 smile = tokens[tok]; | |
1173 obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1])); | |
1174 | |
1175 if (obj == NULL) | |
1176 break; | |
1177 | |
1178 who = msn_object_get_creator(obj); | |
1179 sha1 = msn_object_get_sha1(obj); | |
1180 | |
1181 slplink = msn_session_get_slplink(session, who); | |
1182 if (slplink->swboard != swboard) { | |
1183 if (slplink->swboard != NULL) | |
1184 /* | |
1185 * Apparently we're using a different switchboard now or | |
1186 * something? I don't know if this is normal, but it | |
1187 * definitely happens. So make sure the old switchboard | |
1188 * doesn't still have a reference to us. | |
1189 */ | |
1190 slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink); | |
1191 slplink->swboard = swboard; | |
1192 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); | |
1193 } | |
1194 | |
1195 /* If the conversation doesn't exist then this is a custom smiley | |
1196 * used in the first message in a MSN conversation: we need to create | |
1197 * the conversation now, otherwise the custom smiley won't be shown. | |
1198 * This happens because every GtkIMHtml has its own smiley tree: if | |
1199 * the conversation doesn't exist then we cannot associate the new | |
1200 * smiley with its GtkIMHtml widget. */ | |
1201 if (!conv) { | |
1202 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who); | |
1203 } | |
1204 | |
1205 if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) { | |
1206 msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj); | |
1207 } | |
1208 | |
1209 msn_object_destroy(obj); | |
1210 obj = NULL; | |
1211 who = NULL; | |
1212 sha1 = NULL; | |
1213 } | |
1214 g_strfreev(tokens); | |
1215 } | |
1216 | |
1217 static gboolean | |
1218 buddy_icon_cached(PurpleConnection *gc, MsnObject *obj) | |
1219 { | |
1220 PurpleAccount *account; | |
1221 PurpleBuddy *buddy; | |
1222 const char *old; | |
1223 const char *new; | |
1224 | |
1225 g_return_val_if_fail(obj != NULL, FALSE); | |
1226 | |
1227 account = purple_connection_get_account(gc); | |
1228 | |
1229 buddy = purple_find_buddy(account, msn_object_get_creator(obj)); | |
1230 if (buddy == NULL) | |
1231 return FALSE; | |
1232 | |
1233 old = purple_buddy_icons_get_checksum_for_user(buddy); | |
1234 new = msn_object_get_sha1(obj); | |
1235 | |
1236 if (new == NULL) | |
1237 return FALSE; | |
1238 | |
1239 /* If the old and new checksums are the same, and the file actually exists, | |
1240 * then return TRUE */ | |
1241 if (old != NULL && !strcmp(old, new)) | |
1242 return TRUE; | |
1243 | |
1244 return FALSE; | |
1245 } | |
1246 | |
1247 static void | |
1248 msn_release_buddy_icon_request(MsnUserList *userlist) | |
1249 { | |
1250 MsnUser *user; | |
1251 | |
1252 g_return_if_fail(userlist != NULL); | |
1253 | |
1254 if (purple_debug_is_verbose()) | |
1255 purple_debug_info("msn", "Releasing buddy icon request\n"); | |
1256 | |
1257 if (userlist->buddy_icon_window > 0) | |
1258 { | |
1259 GQueue *queue; | |
1260 PurpleAccount *account; | |
1261 const char *username; | |
1262 | |
1263 queue = userlist->buddy_icon_requests; | |
1264 | |
1265 if (g_queue_is_empty(userlist->buddy_icon_requests)) | |
1266 return; | |
1267 | |
1268 user = g_queue_pop_head(queue); | |
1269 | |
1270 account = userlist->session->account; | |
1271 username = user->passport; | |
1272 | |
1273 userlist->buddy_icon_window--; | |
1274 request_user_display(user); | |
1275 | |
1276 if (purple_debug_is_verbose()) | |
1277 purple_debug_info("msn", | |
1278 "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n", | |
1279 userlist->buddy_icon_window); | |
1280 } | |
1281 } | |
1282 | 91 |
1283 /* | 92 /* |
1284 * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next | 93 * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next |
1285 * buddy icon request if there is one. | 94 * buddy icon request if there is one. |
1286 */ | 95 */ |
1296 userlist->buddy_icon_request_timer = 0; | 105 userlist->buddy_icon_request_timer = 0; |
1297 | 106 |
1298 msn_release_buddy_icon_request(userlist); | 107 msn_release_buddy_icon_request(userlist); |
1299 | 108 |
1300 return FALSE; | 109 return FALSE; |
1301 } | |
1302 | |
1303 void | |
1304 msn_queue_buddy_icon_request(MsnUser *user) | |
1305 { | |
1306 PurpleAccount *account; | |
1307 MsnObject *obj; | |
1308 GQueue *queue; | |
1309 | |
1310 g_return_if_fail(user != NULL); | |
1311 | |
1312 account = user->userlist->session->account; | |
1313 | |
1314 obj = msn_user_get_object(user); | |
1315 | |
1316 if (obj == NULL) | |
1317 { | |
1318 purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL); | |
1319 return; | |
1320 } | |
1321 | |
1322 if (!buddy_icon_cached(account->gc, obj)) | |
1323 { | |
1324 MsnUserList *userlist; | |
1325 | |
1326 userlist = user->userlist; | |
1327 queue = userlist->buddy_icon_requests; | |
1328 | |
1329 if (purple_debug_is_verbose()) | |
1330 purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n", | |
1331 user->passport, userlist->buddy_icon_window); | |
1332 | |
1333 g_queue_push_tail(queue, user); | |
1334 | |
1335 if (userlist->buddy_icon_window > 0) | |
1336 msn_release_buddy_icon_request(userlist); | |
1337 } | |
1338 } | 110 } |
1339 | 111 |
1340 static void | 112 static void |
1341 got_user_display(MsnSlpCall *slpcall, | 113 got_user_display(MsnSlpCall *slpcall, |
1342 const guchar *data, gsize size) | 114 const guchar *data, gsize size) |
1403 userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, | 175 userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, |
1404 msn_release_buddy_icon_request_timeout, userlist); | 176 msn_release_buddy_icon_request_timeout, userlist); |
1405 } | 177 } |
1406 | 178 |
1407 static void | 179 static void |
1408 request_user_display(MsnUser *user) | 180 request_own_user_display(MsnUser *user) |
181 { | |
182 PurpleAccount *account; | |
183 MsnSession *session; | |
184 MsnObject *my_obj = NULL; | |
185 gconstpointer data = NULL; | |
186 const char *info; | |
187 size_t len = 0; | |
188 | |
189 if (purple_debug_is_verbose()) | |
190 purple_debug_info("msn", "Requesting our own user display\n"); | |
191 | |
192 session = user->userlist->session; | |
193 account = session->account; | |
194 my_obj = msn_user_get_object(user); | |
195 | |
196 if (my_obj != NULL) { | |
197 PurpleStoredImage *img = msn_object_get_image(my_obj); | |
198 data = purple_imgstore_get_data(img); | |
199 len = purple_imgstore_get_size(img); | |
200 info = msn_object_get_sha1(my_obj); | |
201 } | |
202 | |
203 purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info); | |
204 | |
205 /* Free one window slot */ | |
206 session->userlist->buddy_icon_window++; | |
207 | |
208 if (purple_debug_is_verbose()) | |
209 purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n", | |
210 session->userlist->buddy_icon_window); | |
211 | |
212 msn_release_buddy_icon_request(session->userlist); | |
213 } | |
214 | |
215 void | |
216 msn_request_user_display(MsnUser *user) | |
1409 { | 217 { |
1410 PurpleAccount *account; | 218 PurpleAccount *account; |
1411 MsnSession *session; | 219 MsnSession *session; |
1412 MsnSlpLink *slplink; | 220 MsnSlpLink *slplink; |
1413 MsnObject *obj; | 221 MsnObject *obj; |
1421 obj = msn_user_get_object(user); | 229 obj = msn_user_get_object(user); |
1422 | 230 |
1423 info = msn_object_get_sha1(obj); | 231 info = msn_object_get_sha1(obj); |
1424 | 232 |
1425 if (g_ascii_strcasecmp(user->passport, | 233 if (g_ascii_strcasecmp(user->passport, |
1426 purple_account_get_username(account))) | 234 purple_account_get_username(account))) |
1427 { | |
1428 msn_slplink_request_object(slplink, info, got_user_display, | 235 msn_slplink_request_object(slplink, info, got_user_display, |
1429 end_user_display, obj); | 236 end_user_display, obj); |
1430 } | |
1431 else | 237 else |
1432 { | 238 request_own_user_display(user); |
1433 MsnObject *my_obj = NULL; | 239 } |
1434 gconstpointer data = NULL; | 240 |
1435 size_t len = 0; | 241 static void |
1436 | 242 send_file_cb(MsnSlpCall *slpcall) |
1437 if (purple_debug_is_verbose()) | 243 { |
1438 purple_debug_info("msn", "Requesting our own user display\n"); | 244 MsnSlpMessage *slpmsg; |
1439 | 245 PurpleXfer *xfer; |
1440 my_obj = msn_user_get_object(session->user); | 246 |
1441 | 247 xfer = (PurpleXfer *)slpcall->xfer; |
1442 if (my_obj != NULL) | 248 if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED) |
1443 { | 249 return; |
1444 PurpleStoredImage *img = msn_object_get_image(my_obj); | 250 |
1445 data = purple_imgstore_get_data(img); | 251 purple_xfer_ref(xfer); |
1446 len = purple_imgstore_get_size(img); | 252 purple_xfer_start(xfer, -1, NULL, 0); |
1447 } | 253 if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) { |
1448 | 254 purple_xfer_unref(xfer); |
1449 purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info); | 255 return; |
1450 | 256 } |
1451 /* Free one window slot */ | 257 purple_xfer_unref(xfer); |
1452 session->userlist->buddy_icon_window++; | 258 |
1453 | 259 slpmsg = msn_slpmsg_file_new(slpcall, purple_xfer_get_size(xfer)); |
1454 if (purple_debug_is_verbose()) | 260 msn_slpmsg_set_slplink(slpmsg, slpcall->slplink); |
1455 purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n", | 261 |
1456 session->userlist->buddy_icon_window); | 262 msn_slplink_send_slpmsg(slpcall->slplink, slpmsg); |
1457 | 263 } |
1458 msn_release_buddy_icon_request(session->userlist); | 264 |
1459 } | 265 static gchar * |
1460 } | 266 gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path) |
267 { | |
268 gsize size = 0; | |
269 MsnFileContext *header; | |
270 gchar *u8 = NULL; | |
271 gchar *ret; | |
272 gunichar2 *uni = NULL; | |
273 glong currentChar = 0; | |
274 glong len = 0; | |
275 const char *preview; | |
276 gsize preview_len; | |
277 | |
278 size = purple_xfer_get_size(xfer); | |
279 | |
280 purple_xfer_prepare_thumbnail(xfer, "png"); | |
281 | |
282 if (!file_name) { | |
283 gchar *basename = g_path_get_basename(file_path); | |
284 u8 = purple_utf8_try_convert(basename); | |
285 g_free(basename); | |
286 file_name = u8; | |
287 } | |
288 | |
289 uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL); | |
290 | |
291 if (u8) { | |
292 g_free(u8); | |
293 file_name = NULL; | |
294 u8 = NULL; | |
295 } | |
296 | |
297 preview = purple_xfer_get_thumbnail(xfer, &preview_len); | |
298 header = g_malloc(sizeof(MsnFileContext) + preview_len); | |
299 | |
300 header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1); | |
301 header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */ | |
302 header->file_size = GUINT64_TO_LE(size); | |
303 if (preview) | |
304 header->type = GUINT32_TO_LE(0); | |
305 else | |
306 header->type = GUINT32_TO_LE(1); | |
307 | |
308 len = MIN(len, MAX_FILE_NAME_LEN); | |
309 for (currentChar = 0; currentChar < len; currentChar++) { | |
310 header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]); | |
311 } | |
312 memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2); | |
313 | |
314 memset(&header->unknown1, 0, sizeof(header->unknown1)); | |
315 header->unknown2 = GUINT32_TO_LE(0xffffffff); | |
316 if (preview) { | |
317 memcpy(&header->preview, preview, preview_len); | |
318 } | |
319 header->preview[preview_len] = '\0'; | |
320 | |
321 g_free(uni); | |
322 ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len); | |
323 g_free(header); | |
324 return ret; | |
325 } | |
326 | |
327 void | |
328 msn_request_ft(PurpleXfer *xfer) | |
329 { | |
330 MsnSlpCall *slpcall; | |
331 MsnSlpLink *slplink; | |
332 char *context; | |
333 const char *fn; | |
334 const char *fp; | |
335 | |
336 fn = purple_xfer_get_filename(xfer); | |
337 fp = purple_xfer_get_local_filename(xfer); | |
338 | |
339 slplink = xfer->data; | |
340 | |
341 g_return_if_fail(slplink != NULL); | |
342 g_return_if_fail(fp != NULL); | |
343 | |
344 slpcall = msn_slpcall_new(slplink); | |
345 msn_slpcall_init(slpcall, MSN_SLPCALL_DC); | |
346 | |
347 slpcall->session_init_cb = send_file_cb; | |
348 slpcall->end_cb = msn_xfer_end_cb; | |
349 slpcall->cb = msn_xfer_completed_cb; | |
350 slpcall->xfer = xfer; | |
351 purple_xfer_ref(slpcall->xfer); | |
352 | |
353 slpcall->pending = TRUE; | |
354 | |
355 purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel); | |
356 purple_xfer_set_read_fnc(xfer, msn_xfer_read); | |
357 purple_xfer_set_write_fnc(xfer, msn_xfer_write); | |
358 | |
359 xfer->data = slpcall; | |
360 | |
361 context = gen_context(xfer, fn, fp); | |
362 | |
363 msn_slpcall_invite(slpcall, MSN_FT_GUID, P2P_APPID_FILE, context); | |
364 msn_slplink_unref(slplink); | |
365 | |
366 g_free(context); | |
367 } | |
368 |