comparison libpurple/protocols/msnp9/slplink.c @ 21312:a07cfce78345

Add MSNP9 back as an alternative alongside the existing MSN prpl. Cowardly old fools like me who prefer the stability of our MSNP9 code over the features of MSNP14 can enable this using the --disable-msnp14 ./configure option. If we want to release from i.p.p and MSN stability is the only blocker, we can trivially flick the default to use MSNP9 in configure.ac
author Stu Tomlinson <stu@nosnilmot.com>
date Sun, 11 Nov 2007 12:57:52 +0000
parents
children 48abe30dc90a
comparison
equal deleted inserted replaced
21311:7d031cec5ba2 21312:a07cfce78345
1 /**
2 * @file slplink.c MSNSLP Link support
3 *
4 * purple
5 *
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
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
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 */
24 #include "msn.h"
25 #include "slplink.h"
26
27 #include "switchboard.h"
28 #include "slp.h"
29
30 void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
31
32 #ifdef MSN_DEBUG_SLP_FILES
33 static int m_sc = 0;
34 static int m_rc = 0;
35
36 static void
37 debug_msg_to_file(MsnMessage *msg, gboolean send)
38 {
39 char *tmp;
40 char *dir;
41 char *pload;
42 FILE *tf;
43 int c;
44 gsize pload_size;
45
46 dir = send ? "send" : "recv";
47 c = send ? m_sc++ : m_rc++;
48 tmp = g_strdup_printf("%s/msntest/%s/%03d", g_get_home_dir(), dir, c);
49 tf = g_fopen(tmp, "wb");
50 if (tf == NULL)
51 {
52 purple_debug_error("msn", "could not open debug file\n");
53 return;
54 }
55 pload = msn_message_gen_payload(msg, &pload_size);
56 fwrite(pload, 1, pload_size, tf);
57 fclose(tf);
58 g_free(tmp);
59 }
60 #endif
61
62 /**************************************************************************
63 * Main
64 **************************************************************************/
65
66 MsnSlpLink *
67 msn_slplink_new(MsnSession *session, const char *username)
68 {
69 MsnSlpLink *slplink;
70
71 g_return_val_if_fail(session != NULL, NULL);
72
73 slplink = g_new0(MsnSlpLink, 1);
74
75 #ifdef MSN_DEBUG_SLPLINK
76 purple_debug_info("msn", "slplink_new: slplink(%p)\n", slplink);
77 #endif
78
79 slplink->session = session;
80 slplink->slp_seq_id = rand() % 0xFFFFFF00 + 4;
81
82 slplink->local_user = g_strdup(msn_user_get_passport(session->user));
83 slplink->remote_user = g_strdup(username);
84
85 slplink->slp_msg_queue = g_queue_new();
86
87 session->slplinks =
88 g_list_append(session->slplinks, slplink);
89
90 return slplink;
91 }
92
93 void
94 msn_slplink_destroy(MsnSlpLink *slplink)
95 {
96 MsnSession *session;
97
98 #ifdef MSN_DEBUG_SLPLINK
99 purple_debug_info("msn", "slplink_destroy: slplink(%p)\n", slplink);
100 #endif
101
102 g_return_if_fail(slplink != NULL);
103
104 if (slplink->swboard != NULL)
105 slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
106
107 session = slplink->session;
108
109 if (slplink->local_user != NULL)
110 g_free(slplink->local_user);
111
112 if (slplink->remote_user != NULL)
113 g_free(slplink->remote_user);
114
115 if (slplink->directconn != NULL)
116 msn_directconn_destroy(slplink->directconn);
117
118 while (slplink->slp_calls != NULL)
119 msn_slp_call_destroy(slplink->slp_calls->data);
120
121 session->slplinks =
122 g_list_remove(session->slplinks, slplink);
123
124 g_free(slplink);
125 }
126
127 MsnSlpLink *
128 msn_session_find_slplink(MsnSession *session, const char *who)
129 {
130 GList *l;
131
132 for (l = session->slplinks; l != NULL; l = l->next)
133 {
134 MsnSlpLink *slplink;
135
136 slplink = l->data;
137
138 if (!strcmp(slplink->remote_user, who))
139 return slplink;
140 }
141
142 return NULL;
143 }
144
145 MsnSlpLink *
146 msn_session_get_slplink(MsnSession *session, const char *username)
147 {
148 MsnSlpLink *slplink;
149
150 g_return_val_if_fail(session != NULL, NULL);
151 g_return_val_if_fail(username != NULL, NULL);
152
153 slplink = msn_session_find_slplink(session, username);
154
155 if (slplink == NULL)
156 slplink = msn_slplink_new(session, username);
157
158 return slplink;
159 }
160
161 MsnSlpSession *
162 msn_slplink_find_slp_session(MsnSlpLink *slplink, long session_id)
163 {
164 GList *l;
165 MsnSlpSession *slpsession;
166
167 for (l = slplink->slp_sessions; l != NULL; l = l->next)
168 {
169 slpsession = l->data;
170
171 if (slpsession->id == session_id)
172 return slpsession;
173 }
174
175 return NULL;
176 }
177
178 void
179 msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall)
180 {
181 if (slplink->swboard != NULL)
182 slplink->swboard->flag |= MSN_SB_FLAG_FT;
183
184 slplink->slp_calls = g_list_append(slplink->slp_calls, slpcall);
185 }
186
187 void
188 msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall)
189 {
190 slplink->slp_calls = g_list_remove(slplink->slp_calls, slpcall);
191
192 /* The slplink has no slpcalls in it. If no one is using it, we might
193 * destroy the switchboard, but we should be careful not to use the slplink
194 * again. */
195 if (slplink->slp_calls == NULL)
196 {
197 if (slplink->swboard != NULL)
198 {
199 if (msn_switchboard_release(slplink->swboard, MSN_SB_FLAG_FT))
200 /* I'm not sure this is the best thing to do, but it's better
201 * than nothing. */
202 slpcall->slplink = NULL;
203 }
204 }
205 }
206
207 MsnSlpCall *
208 msn_slplink_find_slp_call(MsnSlpLink *slplink, const char *id)
209 {
210 GList *l;
211 MsnSlpCall *slpcall;
212
213 if (!id)
214 return NULL;
215
216 for (l = slplink->slp_calls; l != NULL; l = l->next)
217 {
218 slpcall = l->data;
219
220 if (slpcall->id && !strcmp(slpcall->id, id))
221 return slpcall;
222 }
223
224 return NULL;
225 }
226
227 MsnSlpCall *
228 msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id)
229 {
230 GList *l;
231 MsnSlpCall *slpcall;
232
233 for (l = slplink->slp_calls; l != NULL; l = l->next)
234 {
235 slpcall = l->data;
236
237 if (slpcall->session_id == id)
238 return slpcall;
239 }
240
241 return NULL;
242 }
243
244 void
245 msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg)
246 {
247 if (slplink->directconn != NULL)
248 {
249 msn_directconn_send_msg(slplink->directconn, msg);
250 }
251 else
252 {
253 if (slplink->swboard == NULL)
254 {
255 slplink->swboard = msn_session_get_swboard(slplink->session,
256 slplink->remote_user, MSN_SB_FLAG_FT);
257
258 if (slplink->swboard == NULL)
259 return;
260
261 /* If swboard is destroyed we will be too */
262 slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
263 }
264
265 msn_switchboard_send_msg(slplink->swboard, msg, TRUE);
266 }
267 }
268
269 /* We have received the message ack */
270 static void
271 msg_ack(MsnMessage *msg, void *data)
272 {
273 MsnSlpMessage *slpmsg;
274 long long real_size;
275
276 slpmsg = data;
277
278 real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size;
279
280 slpmsg->offset += msg->msnslp_header.length;
281
282 if (slpmsg->offset < real_size)
283 {
284 msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
285 }
286 else
287 {
288 /* The whole message has been sent */
289 if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
290 {
291 if (slpmsg->slpcall != NULL)
292 {
293 if (slpmsg->slpcall->cb)
294 slpmsg->slpcall->cb(slpmsg->slpcall,
295 NULL, 0);
296 }
297 }
298 }
299
300 slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
301 }
302
303 /* We have received the message nak. */
304 static void
305 msg_nak(MsnMessage *msg, void *data)
306 {
307 MsnSlpMessage *slpmsg;
308
309 slpmsg = data;
310
311 msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
312
313 slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
314 }
315
316 void
317 msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
318 {
319 MsnMessage *msg;
320 long long real_size;
321 size_t len = 0;
322
323 /* Maybe we will want to create a new msg for this slpmsg instead of
324 * reusing the same one all the time. */
325 msg = slpmsg->msg;
326
327 real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size;
328
329 if (slpmsg->offset < real_size)
330 {
331 if (slpmsg->fp)
332 {
333 char data[1202];
334 len = fread(data, 1, sizeof(data), slpmsg->fp);
335 msn_message_set_bin_data(msg, data, len);
336 }
337 else
338 {
339 len = slpmsg->size - slpmsg->offset;
340
341 if (len > 1202)
342 len = 1202;
343
344 msn_message_set_bin_data(msg, slpmsg->buffer + slpmsg->offset, len);
345 }
346
347 msg->msnslp_header.offset = slpmsg->offset;
348 msg->msnslp_header.length = len;
349 }
350
351 #ifdef MSN_DEBUG_SLP
352 msn_message_show_readable(msg, slpmsg->info, slpmsg->text_body);
353 #endif
354
355 #ifdef MSN_DEBUG_SLP_FILES
356 debug_msg_to_file(msg, TRUE);
357 #endif
358
359 slpmsg->msgs =
360 g_list_append(slpmsg->msgs, msg);
361 msn_slplink_send_msg(slplink, msg);
362
363 if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) &&
364 (slpmsg->slpcall != NULL))
365 {
366 slpmsg->slpcall->progress = TRUE;
367
368 if (slpmsg->slpcall->progress_cb != NULL)
369 {
370 slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
371 len, slpmsg->offset);
372 }
373 }
374
375 /* slpmsg->offset += len; */
376 }
377
378 void
379 msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
380 {
381 MsnMessage *msg;
382
383 slpmsg->msg = msg = msn_message_new_msnslp();
384
385 if (slpmsg->flags == 0x0)
386 {
387 msg->msnslp_header.session_id = slpmsg->session_id;
388 msg->msnslp_header.ack_id = rand() % 0xFFFFFF00;
389 }
390 else if (slpmsg->flags == 0x2)
391 {
392 msg->msnslp_header.session_id = slpmsg->session_id;
393 msg->msnslp_header.ack_id = slpmsg->ack_id;
394 msg->msnslp_header.ack_size = slpmsg->ack_size;
395 msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id;
396 }
397 else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
398 {
399 MsnSlpSession *slpsession;
400 slpsession = slpmsg->slpsession;
401
402 g_return_if_fail(slpsession != NULL);
403 msg->msnslp_header.session_id = slpsession->id;
404 msg->msnslp_footer.value = slpsession->app_id;
405 msg->msnslp_header.ack_id = rand() % 0xFFFFFF00;
406 }
407 else if (slpmsg->flags == 0x100)
408 {
409 msg->msnslp_header.ack_id = slpmsg->ack_id;
410 msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id;
411 msg->msnslp_header.ack_size = slpmsg->ack_size;
412 }
413
414 msg->msnslp_header.id = slpmsg->id;
415 msg->msnslp_header.flags = slpmsg->flags;
416
417 msg->msnslp_header.total_size = slpmsg->size;
418
419 msn_message_set_attr(msg, "P2P-Dest", slplink->remote_user);
420
421 msg->ack_cb = msg_ack;
422 msg->nak_cb = msg_nak;
423 msg->ack_data = slpmsg;
424
425 msn_slplink_send_msgpart(slplink, slpmsg);
426
427 msn_message_destroy(msg);
428 }
429
430 void
431 msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
432 {
433 slpmsg->id = slplink->slp_seq_id++;
434
435 g_queue_push_head(slplink->slp_msg_queue, slpmsg);
436 }
437
438 void
439 msn_slplink_send_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
440 {
441 slpmsg->id = slplink->slp_seq_id++;
442
443 msn_slplink_release_slpmsg(slplink, slpmsg);
444 }
445
446 void
447 msn_slplink_unleash(MsnSlpLink *slplink)
448 {
449 MsnSlpMessage *slpmsg;
450
451 /* Send the queued msgs in the order they came. */
452
453 while ((slpmsg = g_queue_pop_tail(slplink->slp_msg_queue)) != NULL)
454 {
455 msn_slplink_release_slpmsg(slplink, slpmsg);
456 }
457 }
458
459 void
460 msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg)
461 {
462 MsnSlpMessage *slpmsg;
463
464 slpmsg = msn_slpmsg_new(slplink);
465
466 slpmsg->session_id = msg->msnslp_header.session_id;
467 slpmsg->size = msg->msnslp_header.total_size;
468 slpmsg->flags = 0x02;
469 slpmsg->ack_id = msg->msnslp_header.id;
470 slpmsg->ack_sub_id = msg->msnslp_header.ack_id;
471 slpmsg->ack_size = msg->msnslp_header.total_size;
472
473 #ifdef MSN_DEBUG_SLP
474 slpmsg->info = "SLP ACK";
475 #endif
476
477 msn_slplink_send_slpmsg(slplink, slpmsg);
478 }
479
480 static void
481 send_file_cb(MsnSlpSession *slpsession)
482 {
483 MsnSlpCall *slpcall;
484 MsnSlpMessage *slpmsg;
485 struct stat st;
486 PurpleXfer *xfer;
487
488 slpcall = slpsession->slpcall;
489 slpmsg = msn_slpmsg_new(slpcall->slplink);
490 slpmsg->slpcall = slpcall;
491 slpmsg->flags = 0x1000030;
492 slpmsg->slpsession = slpsession;
493 #ifdef MSN_DEBUG_SLP
494 slpmsg->info = "SLP FILE";
495 #endif
496 xfer = (PurpleXfer *)slpcall->xfer;
497 purple_xfer_start(slpcall->xfer, 0, NULL, 0);
498 slpmsg->fp = xfer->dest_fp;
499 if (g_stat(purple_xfer_get_local_filename(xfer), &st) == 0)
500 slpmsg->size = st.st_size;
501 xfer->dest_fp = NULL; /* Disable double fclose() */
502
503 msn_slplink_send_slpmsg(slpcall->slplink, slpmsg);
504 }
505
506 void
507 msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
508 {
509 MsnSlpMessage *slpmsg;
510 const char *data;
511 gsize offset;
512 gsize len;
513
514 #ifdef MSN_DEBUG_SLP
515 msn_slpmsg_show(msg);
516 #endif
517
518 #ifdef MSN_DEBUG_SLP_FILES
519 debug_msg_to_file(msg, FALSE);
520 #endif
521
522 if (msg->msnslp_header.total_size < msg->msnslp_header.length)
523 {
524 purple_debug_error("msn", "This can't be good\n");
525 g_return_if_reached();
526 }
527
528 slpmsg = NULL;
529 data = msn_message_get_bin_data(msg, &len);
530
531 /*
532 OVERHEAD!
533 if (msg->msnslp_header.length < msg->msnslp_header.total_size)
534 */
535
536 offset = msg->msnslp_header.offset;
537
538 if (offset == 0)
539 {
540 slpmsg = msn_slpmsg_new(slplink);
541 slpmsg->id = msg->msnslp_header.id;
542 slpmsg->session_id = msg->msnslp_header.session_id;
543 slpmsg->size = msg->msnslp_header.total_size;
544 slpmsg->flags = msg->msnslp_header.flags;
545
546 if (slpmsg->session_id)
547 {
548 if (slpmsg->slpcall == NULL)
549 slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id);
550
551 if (slpmsg->slpcall != NULL)
552 {
553 if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
554 {
555 PurpleXfer *xfer;
556
557 xfer = slpmsg->slpcall->xfer;
558
559 if (xfer != NULL)
560 {
561 purple_xfer_start(slpmsg->slpcall->xfer,
562 0, NULL, 0);
563 slpmsg->fp = ((PurpleXfer *)slpmsg->slpcall->xfer)->dest_fp;
564 xfer->dest_fp = NULL; /* Disable double fclose() */
565 }
566 }
567 }
568 }
569 if (!slpmsg->fp && slpmsg->size)
570 {
571 slpmsg->buffer = g_try_malloc(slpmsg->size);
572 if (slpmsg->buffer == NULL)
573 {
574 purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n");
575 return;
576 }
577 }
578 }
579 else
580 {
581 slpmsg = msn_slplink_message_find(slplink, msg->msnslp_header.session_id, msg->msnslp_header.id);
582 }
583
584 if (slpmsg == NULL)
585 {
586 /* Probably the transfer was canceled */
587 purple_debug_error("msn", "Couldn't find slpmsg\n");
588 return;
589 }
590
591 if (slpmsg->fp)
592 {
593 /* fseek(slpmsg->fp, offset, SEEK_SET); */
594 len = fwrite(data, 1, len, slpmsg->fp);
595 }
596 else if (slpmsg->size)
597 {
598 if ((offset + len) > slpmsg->size)
599 {
600 purple_debug_error("msn", "Oversized slpmsg\n");
601 g_return_if_reached();
602 }
603 else
604 memcpy(slpmsg->buffer + offset, data, len);
605 }
606
607 if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) &&
608 (slpmsg->slpcall != NULL))
609 {
610 slpmsg->slpcall->progress = TRUE;
611
612 if (slpmsg->slpcall->progress_cb != NULL)
613 {
614 slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
615 len, offset);
616 }
617 }
618
619 #if 0
620 if (slpmsg->buffer == NULL)
621 return;
622 #endif
623
624 if (msg->msnslp_header.offset + msg->msnslp_header.length
625 >= msg->msnslp_header.total_size)
626 {
627 /* All the pieces of the slpmsg have been received */
628 MsnSlpCall *slpcall;
629
630 slpcall = msn_slp_process_msg(slplink, slpmsg);
631
632 if (slpmsg->flags == 0x100)
633 {
634 MsnDirectConn *directconn;
635
636 directconn = slplink->directconn;
637
638 if (!directconn->acked)
639 msn_directconn_send_handshake(directconn);
640 }
641 else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 ||
642 slpmsg->flags == 0x1000030)
643 {
644 /* Release all the messages and send the ACK */
645
646 msn_slplink_send_ack(slplink, msg);
647 msn_slplink_unleash(slplink);
648 }
649
650 msn_slpmsg_destroy(slpmsg);
651
652 if (slpcall != NULL && slpcall->wasted)
653 msn_slp_call_destroy(slpcall);
654 }
655 }
656
657 MsnSlpMessage *
658 msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id)
659 {
660 GList *e;
661
662 for (e = slplink->slp_msgs; e != NULL; e = e->next)
663 {
664 MsnSlpMessage *slpmsg = e->data;
665
666 if ((slpmsg->session_id == session_id) && (slpmsg->id == id))
667 return slpmsg;
668 }
669
670 return NULL;
671 }
672
673 typedef struct
674 {
675 guint32 length;
676 guint32 unk1;
677 guint32 file_size;
678 guint32 unk2;
679 guint32 unk3;
680 } MsnContextHeader;
681
682 #define MAX_FILE_NAME_LEN 0x226
683
684 static gchar *
685 gen_context(const char *file_name, const char *file_path)
686 {
687 struct stat st;
688 gsize size = 0;
689 MsnContextHeader header;
690 gchar *u8 = NULL;
691 guchar *base;
692 guchar *n;
693 gchar *ret;
694 gunichar2 *uni = NULL;
695 glong currentChar = 0;
696 glong uni_len = 0;
697 gsize len;
698
699 if (g_stat(file_path, &st) == 0)
700 size = st.st_size;
701
702 if(!file_name) {
703 u8 = purple_utf8_try_convert(g_basename(file_path));
704 file_name = u8;
705 }
706
707 uni = g_utf8_to_utf16(file_name, -1, NULL, &uni_len, NULL);
708
709 if(u8) {
710 g_free(u8);
711 file_name = NULL;
712 u8 = NULL;
713 }
714
715 len = sizeof(MsnContextHeader) + MAX_FILE_NAME_LEN + 4;
716
717 header.length = GUINT32_TO_LE(len);
718 header.unk1 = GUINT32_TO_LE(2);
719 header.file_size = GUINT32_TO_LE(size);
720 header.unk2 = GUINT32_TO_LE(0);
721 header.unk3 = GUINT32_TO_LE(0);
722
723 base = g_malloc(len + 1);
724 n = base;
725
726 memcpy(n, &header, sizeof(MsnContextHeader));
727 n += sizeof(MsnContextHeader);
728
729 memset(n, 0x00, MAX_FILE_NAME_LEN);
730 for(currentChar = 0; currentChar < uni_len; currentChar++) {
731 *((gunichar2 *)n + currentChar) = GUINT16_TO_LE(uni[currentChar]);
732 }
733 n += MAX_FILE_NAME_LEN;
734
735 memset(n, 0xFF, 4);
736 n += 4;
737
738 g_free(uni);
739 ret = purple_base64_encode(base, len);
740 g_free(base);
741 return ret;
742 }
743
744 void
745 msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer)
746 {
747 MsnSlpCall *slpcall;
748 char *context;
749 const char *fn;
750 const char *fp;
751
752 fn = purple_xfer_get_filename(xfer);
753 fp = purple_xfer_get_local_filename(xfer);
754
755 g_return_if_fail(slplink != NULL);
756 g_return_if_fail(fp != NULL);
757
758 slpcall = msn_slp_call_new(slplink);
759 msn_slp_call_init(slpcall, MSN_SLPCALL_DC);
760
761 slpcall->session_init_cb = send_file_cb;
762 slpcall->end_cb = msn_xfer_end_cb;
763 slpcall->progress_cb = msn_xfer_progress_cb;
764 slpcall->cb = msn_xfer_completed_cb;
765 slpcall->xfer = xfer;
766 purple_xfer_ref(slpcall->xfer);
767
768 slpcall->pending = TRUE;
769
770 purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel);
771
772 xfer->data = slpcall;
773
774 context = gen_context(fn, fp);
775
776 msn_slp_call_invite(slpcall, "5D3E02AB-6190-11D3-BBBB-00C04F795683", 2,
777 context);
778
779 g_free(context);
780 }
781
782 void
783 msn_slplink_request_object(MsnSlpLink *slplink,
784 const char *info,
785 MsnSlpCb cb,
786 MsnSlpEndCb end_cb,
787 const MsnObject *obj)
788 {
789 MsnSlpCall *slpcall;
790 char *msnobj_data;
791 char *msnobj_base64;
792
793 g_return_if_fail(slplink != NULL);
794 g_return_if_fail(obj != NULL);
795
796 msnobj_data = msn_object_to_string(obj);
797 msnobj_base64 = purple_base64_encode((const guchar *)msnobj_data, strlen(msnobj_data));
798 g_free(msnobj_data);
799
800 slpcall = msn_slp_call_new(slplink);
801 msn_slp_call_init(slpcall, MSN_SLPCALL_ANY);
802
803 slpcall->data_info = g_strdup(info);
804 slpcall->cb = cb;
805 slpcall->end_cb = end_cb;
806
807 msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1,
808 msnobj_base64);
809
810 g_free(msnobj_base64);
811 }