~micahg/ubuntu/natty/pidgin/2.7.9-2

« back to all changes in this revision

Viewing changes to libpurple/protocols/msn/slp.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2010-12-02 16:45:52 UTC
  • mfrom: (2.3.14 sid)
  • Revision ID: james.westby@ubuntu.com-20101202164552-z64wykojzacbb546
Tags: 1:2.7.7-1ubuntu1
New upstream version, drop msn workaround

Show diffs side-by-side

added added

removed removed

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