source: protocols/msn/ns.c @ 2730c79

Last change on this file since 2730c79 was 2730c79, checked in by dequis <dx@…>, at 2015-05-13T07:01:28Z

msn: Fix "ADL/RML command with invalid modification" errors

The previous value (7) was FL | AL | BL, and the block role was replaced
with "Hide" in msnp21, so the server was rejecting the parts of the ADL
that had it. And since adding blocked contacts isn't very useful anyway,
this is like silencing an annoying warning.

  • Property mode set to 100644
File size: 18.7 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* MSN module - Notification server callbacks                           */
8
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 with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#include <ctype.h>
27#include <sys/utsname.h>
28#include "nogaim.h"
29#include "msn.h"
30#include "md5.h"
31#include "sha1.h"
32#include "soap.h"
33#include "xmltree.h"
34
35static gboolean msn_ns_connected(gpointer data, gint source, b_input_condition cond);
36static gboolean msn_ns_callback(gpointer data, gint source, b_input_condition cond);
37
38static void msn_ns_send_adl_start(struct im_connection *ic);
39static void msn_ns_send_adl(struct im_connection *ic);
40static void msn_ns_structured_message(struct msn_data *md, char *msg, int msglen, char **cmd);
41static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action);
42static void msn_ns_nfy(struct msn_data *md, char *who, char **parts, char *action, gboolean is_put);
43
44int msn_ns_write(struct im_connection *ic, int fd, const char *fmt, ...)
45{
46        struct msn_data *md = ic->proto_data;
47        va_list params;
48        char *out;
49        size_t len;
50        int st;
51
52        va_start(params, fmt);
53        out = g_strdup_vprintf(fmt, params);
54        va_end(params);
55
56        if (fd < 0) {
57                fd = md->fd;
58        }
59
60        if (getenv("BITLBEE_DEBUG")) {
61                fprintf(stderr, "\x1b[91m>>>[NS%d] %s\n\x1b[97m", fd, out);
62        }
63
64        len = strlen(out);
65
66        if (md->is_http) {
67                st = len;
68                msn_gw_write(md->gw, out, len);
69        } else {
70                st = write(fd, out, len);
71        }
72
73        g_free(out);
74        if (st != len) {
75                imcb_error(ic, "Short write() to main server");
76                imc_logout(ic, TRUE);
77                return 0;
78        }
79
80        return 1;
81}
82
83gboolean msn_ns_connect(struct im_connection *ic, const char *host, int port)
84{
85        struct msn_data *md = ic->proto_data;
86
87        if (md->fd >= 0) {
88                closesocket(md->fd);
89        }
90
91        if (md->is_http) {
92                md->gw = msn_gw_new(ic);
93                md->gw->callback = msn_ns_callback;
94                msn_ns_connected(md, -1, B_EV_IO_READ);
95        } else {
96                md->fd = proxy_connect(host, port, msn_ns_connected, md);
97                if (md->fd < 0) {
98                        imcb_error(ic, "Could not connect to server");
99                        imc_logout(ic, TRUE);
100                        return FALSE;
101                }
102        }
103
104        return TRUE;
105}
106
107static gboolean msn_ns_connected(gpointer data, gint source, b_input_condition cond)
108{
109        struct msn_data *md = data;
110        struct im_connection *ic = md->ic;
111
112        if (source == -1 && !md->is_http) {
113                imcb_error(ic, "Could not connect to server");
114                imc_logout(ic, TRUE);
115                return FALSE;
116        }
117
118        g_free(md->rxq);
119        md->rxlen = 0;
120        md->rxq = g_new0(char, 1);
121
122        if (md->uuid == NULL) {
123                struct utsname name;
124                sha1_state_t sha[1];
125
126                /* UUID == SHA1("BitlBee" + my hostname + MSN username) */
127                sha1_init(sha);
128                sha1_append(sha, (void *) "BitlBee", 7);
129                if (uname(&name) == 0) {
130                        sha1_append(sha, (void *) name.nodename, strlen(name.nodename));
131                }
132                sha1_append(sha, (void *) ic->acc->user, strlen(ic->acc->user));
133                md->uuid = sha1_random_uuid(sha);
134                memcpy(md->uuid, "b171be3e", 8);   /* :-P */
135        }
136
137        if (msn_ns_write(ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER)) {
138                if (!md->is_http) {
139                        md->inpa = b_input_add(md->fd, B_EV_IO_READ, msn_ns_callback, md);
140                }
141                imcb_log(ic, "Connected to server, waiting for reply");
142        }
143
144        return FALSE;
145}
146
147void msn_ns_close(struct msn_data *md)
148{
149        if (md->gw) {
150                msn_gw_free(md->gw);
151        }
152        if (md->fd >= 0) {
153                closesocket(md->fd);
154                b_event_remove(md->inpa);
155        }
156
157        md->fd = md->inpa = -1;
158        g_free(md->rxq);
159        g_free(md->cmd_text);
160
161        md->rxlen = 0;
162        md->rxq = NULL;
163        md->cmd_text = NULL;
164}
165
166static gboolean msn_ns_callback(gpointer data, gint source, b_input_condition cond)
167{
168        struct msn_data *md = data;
169        struct im_connection *ic = md->ic;
170        char *bytes;
171        int st;
172
173        if (md->is_http) {
174                st = msn_gw_read(md->gw, &bytes);
175        } else {
176                bytes = g_malloc(1024);
177                st = read(md->fd, bytes, 1024);
178        }
179
180        if (st <= 0) {
181                imcb_error(ic, "Error while reading from server");
182                imc_logout(ic, TRUE);
183                g_free(bytes);
184                return FALSE;
185        }
186
187        msn_queue_feed(md, bytes, st);
188
189        g_free(bytes);
190
191        return msn_handler(md);
192}
193
194int msn_ns_command(struct msn_data *md, char **cmd, int num_parts)
195{
196        struct im_connection *ic = md->ic;
197
198        if (num_parts == 0) {
199                /* Hrrm... Empty command...? Ignore? */
200                return(1);
201        }
202
203        if (strcmp(cmd[0], "VER") == 0) {
204                if (cmd[2] && strncmp(cmd[2], MSNP_VER, 5) != 0) {
205                        imcb_error(ic, "Unsupported protocol");
206                        imc_logout(ic, FALSE);
207                        return(0);
208                }
209
210                return(msn_ns_write(ic, md->fd, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s VmVyc2lvbjogMQ0KWGZyQ291bnQ6IDINClhmclNlbnRVVENUaW1lOiA2MzU2MTQ3OTU5NzgzOTAwMDANCklzR2VvWGZyOiB0cnVlDQo=\r\n",
211                                    ++md->trId, ic->acc->user));
212        } else if (strcmp(cmd[0], "CVR") == 0) {
213                /* We don't give a damn about the information we just received */
214                return msn_ns_write(ic, md->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user);
215        } else if (strcmp(cmd[0], "XFR") == 0) {
216                char *server;
217                int port;
218
219                if (num_parts >= 6 && strcmp(cmd[2], "NS") == 0) {
220                        b_event_remove(md->inpa);
221                        md->inpa = -1;
222
223                        server = strchr(cmd[3], ':');
224                        if (!server) {
225                                imcb_error(ic, "Syntax error");
226                                imc_logout(ic, TRUE);
227                                return(0);
228                        }
229                        *server = 0;
230                        port = atoi(server + 1);
231                        server = cmd[3];
232
233                        imcb_log(ic, "Transferring to other server");
234                        return msn_ns_connect(ic, server, port);
235                } else {
236                        imcb_error(ic, "Syntax error");
237                        imc_logout(ic, TRUE);
238                        return(0);
239                }
240        } else if (strcmp(cmd[0], "USR") == 0) {
241                if (num_parts >= 6 && strcmp(cmd[2], "SSO") == 0 &&
242                    strcmp(cmd[3], "S") == 0) {
243                        g_free(md->pp_policy);
244                        md->pp_policy = g_strdup(cmd[4]);
245                        msn_soap_passport_sso_request(ic, cmd[5]);
246                } else if (strcmp(cmd[2], "OK") == 0) {
247                        /* If the number after the handle is 0, the e-mail
248                           address is unverified, which means we can't change
249                           the display name. */
250                        if (cmd[4][0] == '0') {
251                                md->flags |= MSN_EMAIL_UNVERIFIED;
252                        }
253
254                        imcb_log(ic, "Authenticated, getting buddy list");
255                        msn_soap_memlist_request(ic);
256                } else {
257                        imcb_error(ic, "Unknown authentication type");
258                        imc_logout(ic, FALSE);
259                        return(0);
260                }
261        } else if (strcmp(cmd[0], "MSG") == 0) {
262                if (num_parts < 4) {
263                        imcb_error(ic, "Syntax error");
264                        imc_logout(ic, TRUE);
265                        return(0);
266                }
267
268                md->msglen = atoi(cmd[3]);
269
270                if (md->msglen <= 0) {
271                        imcb_error(ic, "Syntax error");
272                        imc_logout(ic, TRUE);
273                        return(0);
274                }
275        } else if (strcmp(cmd[0], "ADL") == 0) {
276                if (num_parts >= 3 && strcmp(cmd[2], "OK") == 0) {
277                        msn_ns_send_adl(ic);
278                        return msn_ns_finish_login(ic);
279                } else if (num_parts >= 3) {
280                        md->msglen = atoi(cmd[2]);
281                }
282        } else if (strcmp(cmd[0], "CHL") == 0) {
283                char *resp;
284                int st;
285
286                if (num_parts < 3) {
287                        imcb_error(ic, "Syntax error");
288                        imc_logout(ic, TRUE);
289                        return(0);
290                }
291
292                resp = msn_p11_challenge(cmd[2]);
293
294                st =  msn_ns_write(ic, -1, "QRY %d %s %zd\r\n%s",
295                                   ++md->trId, MSNP11_PROD_ID,
296                                   strlen(resp), resp);
297                g_free(resp);
298                return st;
299        } else if (strcmp(cmd[0], "QRY") == 0) {
300                /* CONGRATULATIONS */
301        } else if (strcmp(cmd[0], "OUT") == 0) {
302                imcb_error(ic, "Session terminated by remote server (%s)", cmd[1] ? cmd[1] : "reason unknown");
303                imc_logout(ic, TRUE);
304                return(0);
305        } else if (strcmp(cmd[0], "GCF") == 0) {
306                /* Coming up is cmd[2] bytes of stuff we're supposed to
307                   censore. Meh. */
308                md->msglen = atoi(cmd[2]);
309        } else if ((strcmp(cmd[0], "NFY") == 0) || (strcmp(cmd[0], "SDG") == 0)) {
310                if (num_parts >= 3) {
311                        md->msglen = atoi(cmd[2]);
312                }
313        } else if (strcmp(cmd[0], "PUT") == 0) {
314                if (num_parts >= 4) {
315                        md->msglen = atoi(cmd[3]);
316                }
317        } else if (strcmp(cmd[0], "NOT") == 0) {
318                if (num_parts >= 2) {
319                        md->msglen = atoi(cmd[1]);
320                }
321        } else if (strcmp(cmd[0], "QNG") == 0) {
322                ic->flags |= OPT_PONGED;
323        } else if (g_ascii_isdigit(cmd[0][0])) {
324                int num = atoi(cmd[0]);
325                const struct msn_status_code *err = msn_status_by_number(num);
326
327                imcb_error(ic, "Error reported by MSN server: %s", err->text);
328
329                if (err->flags & STATUS_FATAL) {
330                        imc_logout(ic, TRUE);
331                        return(0);
332                }
333
334                /* Oh yes, errors can have payloads too now. Discard them for now. */
335                if (num_parts >= 3) {
336                        md->msglen = atoi(cmd[2]);
337                }
338        } else {
339                imcb_error(ic, "Received unknown command from main server: %s", cmd[0]);
340        }
341
342        return(1);
343}
344
345int msn_ns_message(struct msn_data *md, char *msg, int msglen, char **cmd, int num_parts)
346{
347        struct im_connection *ic = md->ic;
348        char *body;
349        int blen = 0;
350
351        if (!num_parts) {
352                return(1);
353        }
354
355        if ((body = strstr(msg, "\r\n\r\n"))) {
356                body += 4;
357                blen = msglen - (body - msg);
358        }
359
360        if (strcmp(cmd[0], "MSG") == 0) {
361                if (g_strcasecmp(cmd[1], "Hotmail") == 0) {
362                        char *ct = get_rfc822_header(msg, "Content-Type:", msglen);
363
364                        if (!ct) {
365                                return(1);
366                        }
367
368                        if (g_strncasecmp(ct, "application/x-msmsgssystemmessage", 33) == 0) {
369                                char *mtype;
370                                char *arg1;
371
372                                if (!body) {
373                                        return(1);
374                                }
375
376                                mtype = get_rfc822_header(body, "Type:", blen);
377                                arg1 = get_rfc822_header(body, "Arg1:", blen);
378
379                                if (mtype && strcmp(mtype, "1") == 0) {
380                                        if (arg1) {
381                                                imcb_log(ic, "The server is going down for maintenance in %s minutes.",
382                                                         arg1);
383                                        }
384                                }
385
386                                g_free(arg1);
387                                g_free(mtype);
388                        } else if (g_strncasecmp(ct, "text/x-msmsgsprofile", 20) == 0) {
389                                /* We don't care about this profile for now... */
390                        } else if (g_strncasecmp(ct, "text/x-msmsgsinitialemailnotification", 37) == 0) {
391                                if (set_getbool(&ic->acc->set, "mail_notifications")) {
392                                        char *inbox = get_rfc822_header(body, "Inbox-Unread:", blen);
393                                        char *folders = get_rfc822_header(body, "Folders-Unread:", blen);
394
395                                        if (inbox && folders) {
396                                                imcb_log(ic,
397                                                         "INBOX contains %s new messages, plus %s messages in other folders.", inbox,
398                                                         folders);
399                                        }
400
401                                        g_free(inbox);
402                                        g_free(folders);
403                                }
404                        } else if (g_strncasecmp(ct, "text/x-msmsgsemailnotification", 30) == 0) {
405                                if (set_getbool(&ic->acc->set, "mail_notifications")) {
406                                        char *from = get_rfc822_header(body, "From-Addr:", blen);
407                                        char *fromname = get_rfc822_header(body, "From:", blen);
408
409                                        if (from && fromname) {
410                                                imcb_log(ic, "Received an e-mail message from %s <%s>.", fromname,
411                                                         from);
412                                        }
413
414                                        g_free(from);
415                                        g_free(fromname);
416                                }
417                        } else if (g_strncasecmp(ct, "text/x-msmsgsactivemailnotification", 35) == 0) {
418                                /* Notification that a message has been read... Ignore it */
419                        } else {
420                                debug("Can't handle %s packet from notification server", ct);
421                        }
422
423                        g_free(ct);
424                }
425        } else if (strcmp(cmd[0], "ADL") == 0) {
426                struct xt_node *adl, *d, *c;
427
428                if (!(adl = xt_from_string(msg, msglen))) {
429                        return 1;
430                }
431
432                for (d = adl->children; d; d = d->next) {
433                        char *dn;
434                        if (strcmp(d->name, "d") != 0 ||
435                            (dn = xt_find_attr(d, "n")) == NULL) {
436                                continue;
437                        }
438                        for (c = d->children; c; c = c->next) {
439                                bee_user_t *bu;
440                                struct msn_buddy_data *bd;
441                                char *cn, *handle, *f, *l;
442                                int flags;
443
444                                if (strcmp(c->name, "c") != 0 ||
445                                    (l = xt_find_attr(c, "l")) == NULL ||
446                                    (cn = xt_find_attr(c, "n")) == NULL) {
447                                        continue;
448                                }
449
450                                /* FIXME: Use "t" here, guess I should just add it
451                                   as a prefix like elsewhere in the protocol. */
452                                handle = g_strdup_printf("%s@%s", cn, dn);
453                                if (!((bu = bee_user_by_handle(ic->bee, ic, handle)) ||
454                                      (bu = bee_user_new(ic->bee, ic, handle, 0)))) {
455                                        g_free(handle);
456                                        continue;
457                                }
458                                g_free(handle);
459                                bd = bu->data;
460
461                                if ((f = xt_find_attr(c, "f"))) {
462                                        http_decode(f);
463                                        imcb_rename_buddy(ic, bu->handle, f);
464                                }
465
466                                flags = atoi(l) & 15;
467                                if (bd->flags != flags) {
468                                        bd->flags = flags;
469                                        msn_buddy_ask(bu);
470                                }
471                        }
472                }
473        } else if ((strcmp(cmd[0], "SDG") == 0) || (strcmp(cmd[0], "NFY") == 0)) {
474                msn_ns_structured_message(md, msg, msglen, cmd);
475        }
476
477        return 1;
478}
479
480static void msn_ns_structured_message(struct msn_data *md, char *msg, int msglen, char **cmd)
481{
482        char **parts = NULL;
483        char *semicolon = NULL;
484        char *action = NULL;
485        char *from = NULL;
486        char *who = NULL;
487
488        parts = g_strsplit(msg, "\r\n\r\n", 4);
489
490        if (!(from = get_rfc822_header(parts[0], "From", 0))) {
491                goto cleanup;
492        }
493
494        /* either the semicolon or the end of the string */
495        semicolon = strchr(from, ';') ? : (from + strlen(from));
496
497        who = g_strndup(from + 2, semicolon - from - 2);
498
499        if ((strcmp(cmd[0], "SDG") == 0) && (action = get_rfc822_header(parts[2], "Message-Type", 0))) {
500                msn_ns_sdg(md, who, parts, action);
501
502        } else if ((strcmp(cmd[0], "NFY") == 0) && (action = get_rfc822_header(parts[2], "Uri", 0))) {
503                gboolean is_put = (strcmp(cmd[1], "PUT") == 0);
504                msn_ns_nfy(md, who, parts, action, is_put);
505        }
506
507cleanup:
508        g_strfreev(parts);
509        g_free(action);
510        g_free(from);
511        g_free(who);
512}
513
514static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action)
515{
516        struct im_connection *ic = md->ic;
517
518        if (strcmp(action, "Control/Typing") == 0) {
519                imcb_buddy_typing(ic, who, OPT_TYPING);
520        } else if (strcmp(action, "Text") == 0) {
521                imcb_buddy_msg(ic, who, parts[3], 0, 0);
522        }
523}
524
525static void msn_ns_nfy(struct msn_data *md, char *who, char **parts, char *action, gboolean is_put)
526{
527        struct im_connection *ic = md->ic;
528        struct xt_node *body = NULL;
529        struct xt_node *s = NULL;
530        const char *state = NULL;
531        char *nick = NULL;
532        char *psm = NULL;
533        int flags = OPT_LOGGED_IN;
534
535        if (strcmp(action, "/user") != 0) {
536                return;
537        }
538
539        if (!(body = xt_from_string(parts[3], 0))) {
540                goto cleanup;
541        }
542
543        s = body->children;
544        while ((s = xt_find_node(s, "s"))) {
545                struct xt_node *s2;
546                char *n = xt_find_attr(s, "n");  /* service name: IM, PE, etc */
547
548                if (strcmp(n, "IM") == 0) {
549                        /* IM has basic presence information */
550                        if (!is_put) {
551                                /* NFY DEL with a <s> usually means log out from the last endpoint */
552                                flags &= ~OPT_LOGGED_IN;
553                                break;
554                        }
555
556                        s2 = xt_find_node(s->children, "Status");
557                        if (s2 && s2->text_len) {
558                                const struct msn_away_state *msn_state = msn_away_state_by_code(s2->text);
559                                state = msn_state->name;
560                                if (msn_state != msn_away_state_list) {
561                                        flags |= OPT_AWAY;
562                                }
563                        }
564                } else if (strcmp(n, "PE") == 0) {
565                        if ((s2 = xt_find_node(s->children, "PSM")) && s2->text_len) {
566                                psm = s2->text;
567                        }
568                        if ((s2 = xt_find_node(s->children, "FriendlyName")) && s2->text_len) {
569                                nick = s2->text;
570                        }
571                }
572                s = s->next;
573        }
574
575        imcb_buddy_status(ic, who, flags, state, psm);
576
577        if (nick) {
578                imcb_rename_buddy(ic, who, nick);
579        }
580
581cleanup:
582        xt_free_node(body);
583}
584
585void msn_auth_got_passport_token(struct im_connection *ic, const char *token, const char *error)
586{
587        struct msn_data *md;
588
589        /* Dead connection? */
590        if (g_slist_find(msn_connections, ic) == NULL) {
591                return;
592        }
593
594        md = ic->proto_data;
595
596        if (token) {
597                msn_ns_write(ic, -1, "USR %d SSO S %s %s {%s}\r\n", ++md->trId, md->tokens[0], token, md->uuid);
598        } else {
599                imcb_error(ic, "Error during Passport authentication: %s", error);
600                imc_logout(ic, TRUE);
601        }
602}
603
604void msn_auth_got_contact_list(struct im_connection *ic)
605{
606        /* Dead connection? */
607        if (g_slist_find(msn_connections, ic) == NULL) {
608                return;
609        }
610
611        msn_ns_send_adl_start(ic);
612        msn_ns_finish_login(ic);
613}
614
615static gboolean msn_ns_send_adl_1(gpointer key, gpointer value, gpointer data)
616{
617        struct xt_node *adl = data, *d, *c, *s;
618        struct bee_user *bu = value;
619        struct msn_buddy_data *bd = bu->data;
620        struct msn_data *md = bu->ic->proto_data;
621        char handle[strlen(bu->handle) + 1];
622        char *domain;
623        char l[4];
624
625        if ((bd->flags & (MSN_BUDDY_FL | MSN_BUDDY_AL)) == 0 || (bd->flags & MSN_BUDDY_ADL_SYNCED)) {
626                return FALSE;
627        }
628
629        strcpy(handle, bu->handle);
630        if ((domain = strchr(handle, '@')) == NULL) {    /* WTF */
631                return FALSE;
632        }
633        *domain = '\0';
634        domain++;
635
636        if ((d = adl->children) == NULL ||
637            g_strcasecmp(xt_find_attr(d, "n"), domain) != 0) {
638                d = xt_new_node("d", NULL, NULL);
639                xt_add_attr(d, "n", domain);
640                xt_insert_child(adl, d);
641        }
642
643        g_snprintf(l, sizeof(l), "%d", bd->flags & (MSN_BUDDY_FL | MSN_BUDDY_AL));
644        c = xt_new_node("c", NULL, NULL);
645        xt_add_attr(c, "n", handle);
646        xt_add_attr(c, "t", "1");   /* FIXME: Network type, i.e. 32 for Y!MSG */
647        s = xt_new_node("s", NULL, NULL);
648        xt_add_attr(s, "n", "IM");
649        xt_add_attr(s, "l", l);
650        xt_insert_child(c, s);
651        xt_insert_child(d, c);
652
653        /* Do this in batches of 100. */
654        bd->flags |= MSN_BUDDY_ADL_SYNCED;
655        return (--md->adl_todo % 140) == 0;
656}
657
658static void msn_ns_send_adl(struct im_connection *ic)
659{
660        struct xt_node *adl;
661        struct msn_data *md = ic->proto_data;
662        char *adls;
663
664        adl = xt_new_node("ml", NULL, NULL);
665        xt_add_attr(adl, "l", "1");
666        g_tree_foreach(md->domaintree, msn_ns_send_adl_1, adl);
667        if (adl->children == NULL) {
668                /* This tells the caller that we're done now. */
669                md->adl_todo = -1;
670                xt_free_node(adl);
671                return;
672        }
673
674        adls = xt_to_string(adl);
675        xt_free_node(adl);
676        msn_ns_write(ic, -1, "ADL %d %zd\r\n%s", ++md->trId, strlen(adls), adls);
677        g_free(adls);
678}
679
680static void msn_ns_send_adl_start(struct im_connection *ic)
681{
682        struct msn_data *md;
683        GSList *l;
684
685        /* Dead connection? */
686        if (g_slist_find(msn_connections, ic) == NULL) {
687                return;
688        }
689
690        md = ic->proto_data;
691        md->adl_todo = 0;
692        for (l = ic->bee->users; l; l = l->next) {
693                bee_user_t *bu = l->data;
694                struct msn_buddy_data *bd = bu->data;
695
696                if (bu->ic != ic || (bd->flags & (MSN_BUDDY_FL | MSN_BUDDY_AL)) == 0) {
697                        continue;
698                }
699
700                bd->flags &= ~MSN_BUDDY_ADL_SYNCED;
701                md->adl_todo++;
702        }
703
704        msn_ns_send_adl(ic);
705}
706
707int msn_ns_finish_login(struct im_connection *ic)
708{
709        struct msn_data *md = ic->proto_data;
710
711        if (ic->flags & OPT_LOGGED_IN) {
712                return 1;
713        }
714
715        if (md->adl_todo < 0) {
716                md->flags |= MSN_DONE_ADL;
717        }
718
719        if ((md->flags & MSN_DONE_ADL) && (md->flags & MSN_GOT_PROFILE)) {
720                imcb_connected(ic);
721        }
722
723        return 1;
724}
725
726// TODO: typing notifications, nudges lol, etc
727int msn_ns_sendmessage(struct im_connection *ic, bee_user_t *bu, const char *text)
728{
729        struct msn_data *md = ic->proto_data;
730        int retval = 0;
731        char *buf;
732
733        if (strncmp(text, "\r\r\r", 3) == 0) {
734                /* Err. Shouldn't happen but I guess it can. Don't send others
735                   any of the "SHAKE THAT THING" messages. :-D */
736                return 1;
737        }
738
739        buf = g_strdup_printf(MSN_MESSAGE_HEADERS, bu->handle, ic->acc->user, md->uuid, strlen(text), text);
740        retval = msn_ns_write(ic, -1, "SDG %d %zd\r\n%s", ++md->trId, strlen(buf), buf);
741        g_free(buf);
742        return retval;
743}
Note: See TracBrowser for help on using the repository browser.