source: skype/skype.c @ e8e2892

Last change on this file since e8e2892 was e8e2892, checked in by Miklos Vajna <vmiklos@…>, at 2010-12-07T13:29:02Z

Do not crash when failing to connect to skyped

Hi,

when the skype plugin fails to connect to skyped,
it crashes. Not crashing is nicer, because that
results in an neat error message that connecting
to the server failed (and subsequent retries).

The attached patch fixes this crash.

  • Property mode set to 100644
File size: 33.6 KB
Line 
1/*
2 *  skype.c - Skype plugin for BitlBee
3 *
4 *  Copyright (c) 2007, 2008, 2009, 2010 by Miklos Vajna <vmiklos@frugalware.org>
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 *  USA.
20 */
21
22#define _XOPEN_SOURCE
23#define _BSD_SOURCE
24#include <poll.h>
25#include <bitlbee.h>
26#include <bitlbee/ssl_client.h>
27
28#define SKYPE_DEFAULT_SERVER "localhost"
29#define SKYPE_DEFAULT_PORT "2727"
30#define IRC_LINE_SIZE 1024
31#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
32
33/*
34 * Compatibility with BitlBee 1.3+
35 */
36#if BITLBEE_VERSION_CODE < BITLBEE_VER(1, 3, 0)
37#define B_EV_IO_READ GAIM_INPUT_READ
38#endif
39
40/*
41 * Enumerations
42 */
43
44enum {
45        SKYPE_CALL_RINGING = 1,
46        SKYPE_CALL_MISSED,
47        SKYPE_CALL_CANCELLED,
48        SKYPE_CALL_FINISHED,
49        SKYPE_CALL_REFUSED
50};
51
52enum {
53        SKYPE_FILETRANSFER_NEW = 1,
54        SKYPE_FILETRANSFER_FAILED
55};
56
57/*
58 * Structures
59 */
60
61struct skype_data {
62        struct im_connection *ic;
63        char *username;
64        /* The effective file descriptor. We store it here so any function can
65         * write() to it. */
66        int fd;
67        /* File descriptor returned by bitlbee. we store it so we know when
68         * we're connected and when we aren't. */
69        int bfd;
70        /* ssl_getfd() uses this to get the file desciptor. */
71        void *ssl;
72        /* When we receive a new message id, we query the properties, finally
73         * the chatname. Store the properties here so that we can use
74         * imcb_buddy_msg() when we got the chatname. */
75        char *handle;
76        /* List, because of multiline messages. */
77        GList *body;
78        char *type;
79        /* This is necessary because we send a notification when we get the
80         * handle. So we store the state here and then we can send a
81         * notification about the handle is in a given status. */
82        int call_status;
83        char *call_id;
84        char *call_duration;
85        /* If the call is outgoing or not */
86        int call_out;
87        /* Same for file transfers. */
88        int filetransfer_status;
89        /* Using /j #nick we want to have a groupchat with two people. Usually
90         * not (default). */
91        char *groupchat_with;
92        /* The user who invited us to the chat. */
93        char *adder;
94        /* If we are waiting for a confirmation about we changed the topic. */
95        int topic_wait;
96        /* These are used by the info command. */
97        char *info_fullname;
98        char *info_phonehome;
99        char *info_phoneoffice;
100        char *info_phonemobile;
101        char *info_nrbuddies;
102        char *info_tz;
103        char *info_seen;
104        char *info_birthday;
105        char *info_sex;
106        char *info_language;
107        char *info_country;
108        char *info_province;
109        char *info_city;
110        char *info_homepage;
111        char *info_about;
112        /* When a call fails, we get the reason and later we get the failure
113         * event, so store the failure code here till then */
114        int failurereason;
115        /* If this is just an update of an already received message. */
116        int is_edit;
117};
118
119struct skype_away_state {
120        char *code;
121        char *full_name;
122};
123
124struct skype_buddy_ask_data {
125        struct im_connection *ic;
126        /* This is also used for call IDs for simplicity */
127        char *handle;
128};
129
130/*
131 * Tables
132 */
133
134const struct skype_away_state skype_away_state_list[] = {
135        { "AWAY", "Away" },
136        { "NA", "Not available" },
137        { "DND", "Do Not Disturb" },
138        { "INVISIBLE", "Invisible" },
139        { "OFFLINE", "Offline" },
140        { "SKYPEME", "Skype Me" },
141        { "ONLINE", "Online" },
142        { NULL, NULL}
143};
144
145/*
146 * Functions
147 */
148
149int skype_write(struct im_connection *ic, char *buf, int len)
150{
151        struct skype_data *sd = ic->proto_data;
152        struct pollfd pfd[1];
153
154        if (!sd->ssl)
155                return FALSE;
156
157        pfd[0].fd = sd->fd;
158        pfd[0].events = POLLOUT;
159
160        /* This poll is necessary or we'll get a SIGPIPE when we write() to
161         * sd->fd. */
162        poll(pfd, 1, 1000);
163        if (pfd[0].revents & POLLHUP) {
164                imc_logout(ic, TRUE);
165                return FALSE;
166        }
167        ssl_write(sd->ssl, buf, len);
168
169        return TRUE;
170}
171
172int skype_printf(struct im_connection *ic, char *fmt, ...)
173{
174        va_list args;
175        char str[IRC_LINE_SIZE];
176
177        va_start(args, fmt);
178        vsnprintf(str, IRC_LINE_SIZE, fmt, args);
179        va_end(args);
180
181        return skype_write(ic, str, strlen(str));
182}
183
184static void skype_buddy_ask_yes(void *data)
185{
186        struct skype_buddy_ask_data *bla = data;
187        skype_printf(bla->ic, "SET USER %s ISAUTHORIZED TRUE",
188                bla->handle);
189        g_free(bla->handle);
190        g_free(bla);
191}
192
193static void skype_buddy_ask_no(void *data)
194{
195        struct skype_buddy_ask_data *bla = data;
196        skype_printf(bla->ic, "SET USER %s ISAUTHORIZED FALSE",
197                bla->handle);
198        g_free(bla->handle);
199        g_free(bla);
200}
201
202void skype_buddy_ask(struct im_connection *ic, char *handle, char *message)
203{
204        struct skype_buddy_ask_data *bla = g_new0(struct skype_buddy_ask_data,
205                1);
206        char *buf;
207
208        bla->ic = ic;
209        bla->handle = g_strdup(handle);
210
211        buf = g_strdup_printf("The user %s wants to add you to "
212                "his/her buddy list, saying: '%s'.", handle, message);
213        imcb_ask(ic, buf, bla, skype_buddy_ask_yes, skype_buddy_ask_no);
214        g_free(buf);
215}
216
217static void skype_call_ask_yes(void *data)
218{
219        struct skype_buddy_ask_data *bla = data;
220        skype_printf(bla->ic, "SET CALL %s STATUS INPROGRESS",
221                bla->handle);
222        g_free(bla->handle);
223        g_free(bla);
224}
225
226static void skype_call_ask_no(void *data)
227{
228        struct skype_buddy_ask_data *bla = data;
229        skype_printf(bla->ic, "SET CALL %s STATUS FINISHED",
230                bla->handle);
231        g_free(bla->handle);
232        g_free(bla);
233}
234
235void skype_call_ask(struct im_connection *ic, char *call_id, char *message)
236{
237        struct skype_buddy_ask_data *bla = g_new0(struct skype_buddy_ask_data,
238                1);
239
240        bla->ic = ic;
241        bla->handle = g_strdup(call_id);
242
243        imcb_ask(ic, message, bla, skype_call_ask_yes, skype_call_ask_no);
244}
245struct groupchat *skype_chat_by_name(struct im_connection *ic, char *name)
246{
247#if BITLBEE_VERSION_CODE < BITLBEE_VER(1, 3, 0)
248        struct groupchat *ret;
249
250        for (ret = ic->groupchats; ret; ret = ret->next)
251                if (strcmp(name, ret->title) == 0)
252                        break;
253
254        return ret;
255#else
256        return bee_chat_by_title(ic->bee, ic, name);
257#endif
258}
259
260static char *skype_call_strerror(int err)
261{
262        switch (err) {
263        case 1:
264                return "Miscellaneous error";
265        case 2:
266                return "User or phone number does not exist.";
267        case 3:
268                return "User is offline";
269        case 4:
270                return "No proxy found";
271        case 5:
272                return "Session terminated.";
273        case 6:
274                return "No common codec found.";
275        case 7:
276                return "Sound I/O error.";
277        case 8:
278                return "Problem with remote sound device.";
279        case 9:
280                return "Call blocked by recipient.";
281        case 10:
282                return "Recipient not a friend.";
283        case 11:
284                return "Current user not authorized by recipient.";
285        case 12:
286                return "Sound recording error.";
287        default:
288                return "Unknown error";
289        }
290}
291
292static void skype_parse_users(struct im_connection *ic, char *line)
293{
294        char **i, **nicks;
295
296        nicks = g_strsplit(line + 6, ", ", 0);
297        for (i = nicks; *i; i++)
298                skype_printf(ic, "GET USER %s ONLINESTATUS\n", *i);
299        g_strfreev(nicks);
300}
301
302static void skype_parse_user(struct im_connection *ic, char *line)
303{
304        int flags = 0;
305        char *ptr;
306        struct skype_data *sd = ic->proto_data;
307        char *user = strchr(line, ' ');
308        char *status = strrchr(line, ' ');
309
310        status++;
311        ptr = strchr(++user, ' ');
312        if (!ptr)
313                return;
314        *ptr = '\0';
315        ptr++;
316        if (!strncmp(ptr, "ONLINESTATUS ", 13)) {
317                        if (!strcmp(user, sd->username))
318                                return;
319                        if (!set_getbool(&ic->acc->set, "test_join")
320                                && !strcmp(user, "echo123"))
321                                return;
322                ptr = g_strdup_printf("%s@skype.com", user);
323                imcb_add_buddy(ic, ptr, NULL);
324                if (strcmp(status, "OFFLINE") && (strcmp(status, "SKYPEOUT") ||
325                        !set_getbool(&ic->acc->set, "skypeout_offline")))
326                        flags |= OPT_LOGGED_IN;
327                if (strcmp(status, "ONLINE") && strcmp(status, "SKYPEME"))
328                        flags |= OPT_AWAY;
329                imcb_buddy_status(ic, ptr, flags, NULL, NULL);
330                g_free(ptr);
331        } else if (!strncmp(ptr, "RECEIVEDAUTHREQUEST ", 20)) {
332                char *message = ptr + 20;
333                if (strlen(message))
334                        skype_buddy_ask(ic, user, message);
335        } else if (!strncmp(ptr, "BUDDYSTATUS ", 12)) {
336                char *st = ptr + 12;
337                if (!strcmp(st, "3")) {
338                        char *buf = g_strdup_printf("%s@skype.com", user);
339                        imcb_add_buddy(ic, buf, NULL);
340                        g_free(buf);
341                }
342        } else if (!strncmp(ptr, "MOOD_TEXT ", 10)) {
343                char *buf = g_strdup_printf("%s@skype.com", user);
344#if BITLBEE_VERSION_CODE < BITLBEE_VER(1, 3, 0)
345                user_t *u = user_findhandle(ic, buf);
346                g_free(buf);
347                buf = ptr + 10;
348                if (u) {
349                        if (u->status_msg)
350                                g_free(u->status_msg);
351                        if (strlen(buf))
352                                u->status_msg = g_strdup(buf);
353                        else
354                                u->status_msg = NULL;
355                }
356#else
357                bee_user_t *bu = bee_user_by_handle(ic->bee, ic, buf);
358                g_free(buf);
359                buf = ptr + 10;
360                if (bu) {
361                        imcb_buddy_status(ic, bu->handle, bu->flags, NULL,
362                                        *buf ? buf : NULL);
363                }
364#endif
365                if (set_getbool(&ic->acc->set, "show_moods"))
366                        imcb_log(ic, "User `%s' changed mood text to `%s'", user, buf);
367        } else if (!strncmp(ptr, "FULLNAME ", 9))
368                sd->info_fullname = g_strdup(ptr + 9);
369        else if (!strncmp(ptr, "PHONE_HOME ", 11))
370                sd->info_phonehome = g_strdup(ptr + 11);
371        else if (!strncmp(ptr, "PHONE_OFFICE ", 13))
372                sd->info_phoneoffice = g_strdup(ptr + 13);
373        else if (!strncmp(ptr, "PHONE_MOBILE ", 13))
374                sd->info_phonemobile = g_strdup(ptr + 13);
375        else if (!strncmp(ptr, "NROF_AUTHED_BUDDIES ", 20))
376                sd->info_nrbuddies = g_strdup(ptr + 20);
377        else if (!strncmp(ptr, "TIMEZONE ", 9))
378                sd->info_tz = g_strdup(ptr + 9);
379        else if (!strncmp(ptr, "LASTONLINETIMESTAMP ", 20))
380                sd->info_seen = g_strdup(ptr + 20);
381        else if (!strncmp(ptr, "BIRTHDAY ", 9))
382                sd->info_birthday = g_strdup(ptr + 9);
383        else if (!strncmp(ptr, "SEX ", 4))
384                sd->info_sex = g_strdup(ptr + 4);
385        else if (!strncmp(ptr, "LANGUAGE ", 9))
386                sd->info_language = g_strdup(ptr + 9);
387        else if (!strncmp(ptr, "COUNTRY ", 8))
388                sd->info_country = g_strdup(ptr + 8);
389        else if (!strncmp(ptr, "PROVINCE ", 9))
390                sd->info_province = g_strdup(ptr + 9);
391        else if (!strncmp(ptr, "CITY ", 5))
392                sd->info_city = g_strdup(ptr + 5);
393        else if (!strncmp(ptr, "HOMEPAGE ", 9))
394                sd->info_homepage = g_strdup(ptr + 9);
395        else if (!strncmp(ptr, "ABOUT ", 6)) {
396                sd->info_about = g_strdup(ptr + 6);
397
398                GString *st = g_string_new("Contact Information\n");
399                g_string_append_printf(st, "Skype Name: %s\n", user);
400                if (sd->info_fullname) {
401                        if (strlen(sd->info_fullname))
402                                g_string_append_printf(st, "Full Name: %s\n",
403                                        sd->info_fullname);
404                        g_free(sd->info_fullname);
405                }
406                if (sd->info_phonehome) {
407                        if (strlen(sd->info_phonehome))
408                                g_string_append_printf(st, "Home Phone: %s\n",
409                                        sd->info_phonehome);
410                        g_free(sd->info_phonehome);
411                }
412                if (sd->info_phoneoffice) {
413                        if (strlen(sd->info_phoneoffice))
414                                g_string_append_printf(st, "Office Phone: %s\n",
415                                        sd->info_phoneoffice);
416                        g_free(sd->info_phoneoffice);
417                }
418                if (sd->info_phonemobile) {
419                        if (strlen(sd->info_phonemobile))
420                                g_string_append_printf(st, "Mobile Phone: %s\n",
421                                        sd->info_phonemobile);
422                        g_free(sd->info_phonemobile);
423                }
424                g_string_append_printf(st, "Personal Information\n");
425                if (sd->info_nrbuddies) {
426                        if (strlen(sd->info_nrbuddies))
427                                g_string_append_printf(st,
428                                        "Contacts: %s\n", sd->info_nrbuddies);
429                        g_free(sd->info_nrbuddies);
430                }
431                if (sd->info_tz) {
432                        if (strlen(sd->info_tz)) {
433                                char ib[256];
434                                time_t t = time(NULL);
435                                t += atoi(sd->info_tz)-(60*60*24);
436                                struct tm *gt = gmtime(&t);
437                                strftime(ib, 256, "%H:%M:%S", gt);
438                                g_string_append_printf(st,
439                                        "Local Time: %s\n", ib);
440                        }
441                        g_free(sd->info_tz);
442                }
443                if (sd->info_seen) {
444                        if (strlen(sd->info_seen)) {
445                                char ib[256];
446                                time_t it = atoi(sd->info_seen);
447                                struct tm *tm = localtime(&it);
448                                strftime(ib, 256, ("%Y. %m. %d. %H:%M"), tm);
449                                g_string_append_printf(st,
450                                        "Last Seen: %s\n", ib);
451                        }
452                        g_free(sd->info_seen);
453                }
454                if (sd->info_birthday) {
455                        if (strlen(sd->info_birthday) &&
456                                strcmp(sd->info_birthday, "0")) {
457                                char ib[256];
458                                struct tm tm;
459                                strptime(sd->info_birthday, "%Y%m%d", &tm);
460                                strftime(ib, 256, "%B %d, %Y", &tm);
461                                g_string_append_printf(st,
462                                        "Birthday: %s\n", ib);
463
464                                strftime(ib, 256, "%Y", &tm);
465                                int year = atoi(ib);
466                                time_t t = time(NULL);
467                                struct tm *lt = localtime(&t);
468                                g_string_append_printf(st,
469                                        "Age: %d\n", lt->tm_year+1900-year);
470                        }
471                        g_free(sd->info_birthday);
472                }
473                if (sd->info_sex) {
474                        if (strlen(sd->info_sex)) {
475                                char *iptr = sd->info_sex;
476                                while (*iptr++)
477                                        *iptr = tolower(*iptr);
478                                g_string_append_printf(st,
479                                        "Gender: %s\n", sd->info_sex);
480                        }
481                        g_free(sd->info_sex);
482                }
483                if (sd->info_language) {
484                        if (strlen(sd->info_language)) {
485                                char *iptr = strchr(sd->info_language, ' ');
486                                if (iptr)
487                                        iptr++;
488                                else
489                                        iptr = sd->info_language;
490                                g_string_append_printf(st,
491                                        "Language: %s\n", iptr);
492                        }
493                        g_free(sd->info_language);
494                }
495                if (sd->info_country) {
496                        if (strlen(sd->info_country)) {
497                                char *iptr = strchr(sd->info_country, ' ');
498                                if (iptr)
499                                        iptr++;
500                                else
501                                        iptr = sd->info_country;
502                                g_string_append_printf(st,
503                                        "Country: %s\n", iptr);
504                        }
505                        g_free(sd->info_country);
506                }
507                if (sd->info_province) {
508                        if (strlen(sd->info_province))
509                                g_string_append_printf(st,
510                                        "Region: %s\n", sd->info_province);
511                        g_free(sd->info_province);
512                }
513                if (sd->info_city) {
514                        if (strlen(sd->info_city))
515                                g_string_append_printf(st,
516                                        "City: %s\n", sd->info_city);
517                        g_free(sd->info_city);
518                }
519                if (sd->info_homepage) {
520                        if (strlen(sd->info_homepage))
521                                g_string_append_printf(st,
522                                        "Homepage: %s\n", sd->info_homepage);
523                        g_free(sd->info_homepage);
524                }
525                if (sd->info_about) {
526                        if (strlen(sd->info_about))
527                                g_string_append_printf(st, "%s\n",
528                                        sd->info_about);
529                        g_free(sd->info_about);
530                }
531                imcb_log(ic, "%s", st->str);
532                g_string_free(st, TRUE);
533        }
534}
535
536static void skype_parse_chatmessage(struct im_connection *ic, char *line)
537{
538        struct skype_data *sd = ic->proto_data;
539        char buf[IRC_LINE_SIZE];
540        char *id = strchr(line, ' ');
541
542        if (!++id)
543                return;
544        char *info = strchr(id, ' ');
545
546        if (!info)
547                return;
548        *info = '\0';
549        info++;
550        if (!strcmp(info, "STATUS RECEIVED") || !strncmp(info, "EDITED_TIMESTAMP", 16)) {
551                /* New message ID:
552                 * (1) Request its from field
553                 * (2) Request its body
554                 * (3) Request its type
555                 * (4) Query chatname
556                 */
557                skype_printf(ic, "GET CHATMESSAGE %s FROM_HANDLE\n", id);
558                if (!strcmp(info, "STATUS RECEIVED"))
559                        skype_printf(ic, "GET CHATMESSAGE %s BODY\n", id);
560                else
561                        sd->is_edit = 1;
562                skype_printf(ic, "GET CHATMESSAGE %s TYPE\n", id);
563                skype_printf(ic, "GET CHATMESSAGE %s CHATNAME\n", id);
564        } else if (!strncmp(info, "FROM_HANDLE ", 12)) {
565                info += 12;
566                /* New from field value. Store
567                 * it, then we can later use it
568                 * when we got the message's
569                 * body. */
570                g_free(sd->handle);
571                sd->handle = g_strdup_printf("%s@skype.com", info);
572        } else if (!strncmp(info, "EDITED_BY ", 10)) {
573                info += 10;
574                /* This is the same as
575                 * FROM_HANDLE, except that we
576                 * never request these lines
577                 * from Skype, we just get
578                 * them. */
579                g_free(sd->handle);
580                sd->handle = g_strdup_printf("%s@skype.com", info);
581        } else if (!strncmp(info, "BODY ", 5)) {
582                info += 5;
583                sd->body = g_list_append(sd->body, g_strdup(info));
584        }       else if (!strncmp(info, "TYPE ", 5)) {
585                info += 5;
586                g_free(sd->type);
587                sd->type = g_strdup(info);
588        } else if (!strncmp(info, "CHATNAME ", 9)) {
589                info += 9;
590                if (sd->handle && sd->body && sd->type) {
591                        struct groupchat *gc = skype_chat_by_name(ic, info);
592                        int i;
593                        for (i = 0; i < g_list_length(sd->body); i++) {
594                                char *body = g_list_nth_data(sd->body, i);
595                                if (!strcmp(sd->type, "SAID") ||
596                                        !strcmp(sd->type, "EMOTED")) {
597                                        if (!strcmp(sd->type, "SAID")) {
598                                                if (!sd->is_edit)
599                                                        g_snprintf(buf, IRC_LINE_SIZE, "%s",
600                                                                body);
601                                                else {
602                                                        g_snprintf(buf, IRC_LINE_SIZE, "%s %s",
603                                                                set_getstr(&ic->acc->set, "edit_prefix"),
604                                                                body);
605                                                        sd->is_edit = 0;
606                                                }
607                                        } else
608                                                g_snprintf(buf, IRC_LINE_SIZE, "/me %s",
609                                                        body);
610                                        if (!gc)
611                                                /* Private message */
612                                                imcb_buddy_msg(ic,
613                                                        sd->handle, buf, 0, 0);
614                                        else
615                                                /* Groupchat message */
616                                                imcb_chat_msg(gc,
617                                                        sd->handle, buf, 0, 0);
618                                } else if (!strcmp(sd->type, "SETTOPIC") && gc)
619                                        imcb_chat_topic(gc,
620                                                sd->handle, body, 0);
621                                else if (!strcmp(sd->type, "LEFT") && gc)
622                                        imcb_chat_remove_buddy(gc,
623                                                sd->handle, NULL);
624                        }
625                        g_list_free(sd->body);
626                        sd->body = NULL;
627                }
628        }
629}
630
631static void skype_parse_call(struct im_connection *ic, char *line)
632{
633        struct skype_data *sd = ic->proto_data;
634        char *id = strchr(line, ' ');
635        char buf[IRC_LINE_SIZE];
636
637        if (!++id)
638                return;
639        char *info = strchr(id, ' ');
640
641        if (!info)
642                return;
643        *info = '\0';
644        info++;
645        if (!strncmp(info, "FAILUREREASON ", 14))
646                sd->failurereason = atoi(strchr(info, ' '));
647        else if (!strcmp(info, "STATUS RINGING")) {
648                if (sd->call_id)
649                        g_free(sd->call_id);
650                sd->call_id = g_strdup(id);
651                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
652                sd->call_status = SKYPE_CALL_RINGING;
653        } else if (!strcmp(info, "STATUS MISSED")) {
654                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
655                sd->call_status = SKYPE_CALL_MISSED;
656        } else if (!strcmp(info, "STATUS CANCELLED")) {
657                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
658                sd->call_status = SKYPE_CALL_CANCELLED;
659        } else if (!strcmp(info, "STATUS FINISHED")) {
660                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
661                sd->call_status = SKYPE_CALL_FINISHED;
662        } else if (!strcmp(info, "STATUS REFUSED")) {
663                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
664                sd->call_status = SKYPE_CALL_REFUSED;
665        } else if (!strcmp(info, "STATUS UNPLACED")) {
666                if (sd->call_id)
667                        g_free(sd->call_id);
668                /* Save the ID for later usage (Cancel/Finish). */
669                sd->call_id = g_strdup(id);
670                sd->call_out = TRUE;
671        } else if (!strcmp(info, "STATUS FAILED")) {
672                imcb_error(ic, "Call failed: %s",
673                        skype_call_strerror(sd->failurereason));
674                sd->call_id = NULL;
675        } else if (!strncmp(info, "DURATION ", 9)) {
676                if (sd->call_duration)
677                        g_free(sd->call_duration);
678                sd->call_duration = g_strdup(info+9);
679        } else if (!strncmp(info, "PARTNER_HANDLE ", 15)) {
680                info += 15;
681                if (!sd->call_status)
682                        return;
683                switch (sd->call_status) {
684                case SKYPE_CALL_RINGING:
685                        if (sd->call_out)
686                                imcb_log(ic, "You are currently ringing "
687                                        "the user %s.", info);
688                        else {
689                                g_snprintf(buf, IRC_LINE_SIZE,
690                                        "The user %s is currently ringing you.",
691                                        info);
692                                skype_call_ask(ic, sd->call_id, buf);
693                        }
694                        break;
695                case SKYPE_CALL_MISSED:
696                        imcb_log(ic, "You have missed a call from user %s.",
697                                info);
698                        break;
699                case SKYPE_CALL_CANCELLED:
700                        imcb_log(ic, "You cancelled the call to the user %s.",
701                                info);
702                        sd->call_status = 0;
703                        sd->call_out = FALSE;
704                        break;
705                case SKYPE_CALL_REFUSED:
706                        if (sd->call_out)
707                                imcb_log(ic, "The user %s refused the call.",
708                                        info);
709                        else
710                                imcb_log(ic,
711                                        "You refused the call from user %s.",
712                                        info);
713                        sd->call_out = FALSE;
714                        break;
715                case SKYPE_CALL_FINISHED:
716                        if (sd->call_duration)
717                                imcb_log(ic,
718                                        "You finished the call to the user %s "
719                                        "(duration: %s seconds).",
720                                        info, sd->call_duration);
721                        else
722                                imcb_log(ic,
723                                        "You finished the call to the user %s.",
724                                        info);
725                        sd->call_out = FALSE;
726                        break;
727                default:
728                        /* Don't be noisy, ignore other statuses for now. */
729                        break;
730                }
731                sd->call_status = 0;
732        }
733}
734
735static void skype_parse_filetransfer(struct im_connection *ic, char *line)
736{
737        struct skype_data *sd = ic->proto_data;
738        char *id = strchr(line, ' ');
739
740        if (!++id)
741                return;
742        char *info = strchr(id, ' ');
743
744        if (!info)
745                return;
746        *info = '\0';
747        info++;
748        if (!strcmp(info, "STATUS NEW")) {
749                skype_printf(ic, "GET FILETRANSFER %s PARTNER_HANDLE\n",
750                        id);
751                sd->filetransfer_status = SKYPE_FILETRANSFER_NEW;
752        } else if (!strcmp(info, "STATUS FAILED")) {
753                skype_printf(ic, "GET FILETRANSFER %s PARTNER_HANDLE\n",
754                        id);
755                sd->filetransfer_status = SKYPE_FILETRANSFER_FAILED;
756        } else if (!strncmp(info, "PARTNER_HANDLE ", 15)) {
757                info += 15;
758                if (!sd->filetransfer_status)
759                        return;
760                switch (sd->filetransfer_status) {
761                case SKYPE_FILETRANSFER_NEW:
762                        imcb_log(ic, "The user %s offered a new file for you.",
763                                info);
764                        break;
765                case SKYPE_FILETRANSFER_FAILED:
766                        imcb_log(ic, "Failed to transfer file from user %s.",
767                                info);
768                        break;
769                }
770                sd->filetransfer_status = 0;
771        }
772}
773
774static void skype_parse_chat(struct im_connection *ic, char *line)
775{
776        struct skype_data *sd = ic->proto_data;
777        char buf[IRC_LINE_SIZE];
778        char *id = strchr(line, ' ');
779
780        if (!++id)
781                return;
782        struct groupchat *gc;
783        char *info = strchr(id, ' ');
784
785        if (!info)
786                return;
787        *info = '\0';
788        info++;
789        /* Remove fake chat if we created one in skype_chat_with() */
790        gc = skype_chat_by_name(ic, "");
791        if (gc)
792                imcb_chat_free(gc);
793        if (!strcmp(info, "STATUS MULTI_SUBSCRIBED")) {
794                gc = imcb_chat_new(ic, id);
795                imcb_chat_name_hint(gc, id);
796                skype_printf(ic, "GET CHAT %s ADDER\n", id);
797                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
798        } else if (!strcmp(info, "STATUS DIALOG") && sd->groupchat_with) {
799                gc = imcb_chat_new(ic, id);
800                imcb_chat_name_hint(gc, id);
801                /* According to the docs this
802                 * is necessary. However it
803                 * does not seem the situation
804                 * and it would open an extra
805                 * window on our client, so
806                 * just leave it out. */
807                /*skype_printf(ic, "OPEN CHAT %s\n", id);*/
808                g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
809                                sd->groupchat_with);
810                imcb_chat_add_buddy(gc, buf);
811                imcb_chat_add_buddy(gc, sd->username);
812                g_free(sd->groupchat_with);
813                sd->groupchat_with = NULL;
814                skype_printf(ic, "GET CHAT %s ADDER\n", id);
815                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
816        } else if (!strcmp(info, "STATUS UNSUBSCRIBED")) {
817                gc = skype_chat_by_name(ic, id);
818                if (gc)
819                        gc->data = (void *)FALSE;
820        } else if (!strncmp(info, "ADDER ", 6)) {
821                info += 6;
822                g_free(sd->adder);
823                sd->adder = g_strdup_printf("%s@skype.com", info);
824        } else if (!strncmp(info, "TOPIC ", 6)) {
825                info += 6;
826                gc = skype_chat_by_name(ic, id);
827                if (gc && (sd->adder || sd->topic_wait)) {
828                        if (sd->topic_wait) {
829                                sd->adder = g_strdup(sd->username);
830                                sd->topic_wait = 0;
831                        }
832                        imcb_chat_topic(gc, sd->adder, info, 0);
833                        g_free(sd->adder);
834                        sd->adder = NULL;
835                }
836        } else if (!strncmp(info, "ACTIVEMEMBERS ", 14)) {
837                info += 14;
838                gc = skype_chat_by_name(ic, id);
839                /* Hack! We set ->data to TRUE
840                 * while we're on the channel
841                 * so that we won't rejoin
842                 * after a /part. */
843                if (!gc || gc->data)
844                        return;
845                char **members = g_strsplit(info, " ", 0);
846                int i;
847                for (i = 0; members[i]; i++) {
848                        if (!strcmp(members[i], sd->username))
849                                continue;
850                        g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
851                                        members[i]);
852                        if (!g_list_find_custom(gc->in_room, buf,
853                                (GCompareFunc)strcmp))
854                                imcb_chat_add_buddy(gc, buf);
855                }
856                imcb_chat_add_buddy(gc, sd->username);
857                g_strfreev(members);
858        }
859}
860
861static void skype_parse_password(struct im_connection *ic, char *line)
862{
863        if (!strncmp(line+9, "OK", 2))
864                imcb_connected(ic);
865        else {
866                imcb_error(ic, "Authentication Failed");
867                imc_logout(ic, TRUE);
868        }
869}
870
871static void skype_parse_profile(struct im_connection *ic, char *line)
872{
873        imcb_log(ic, "SkypeOut balance value is '%s'.", line+21);
874}
875
876static void skype_parse_ping(struct im_connection *ic, char *line)
877{
878        skype_printf(ic, "PONG\n");
879}
880
881static void skype_parse_chats(struct im_connection *ic, char *line)
882{
883        char **i;
884        char **chats = g_strsplit(line + 6, ", ", 0);
885
886        i = chats;
887        while (*i) {
888                skype_printf(ic, "GET CHAT %s STATUS\n", *i);
889                skype_printf(ic, "GET CHAT %s ACTIVEMEMBERS\n", *i);
890                i++;
891        }
892        g_strfreev(chats);
893}
894
895typedef void (*skype_parser)(struct im_connection *ic, char *line);
896
897static gboolean skype_read_callback(gpointer data, gint fd,
898                                    b_input_condition cond)
899{
900        struct im_connection *ic = data;
901        struct skype_data *sd = ic->proto_data;
902        char buf[IRC_LINE_SIZE];
903        int st, i;
904        char **lines, **lineptr, *line;
905        static struct parse_map {
906                char *k;
907                skype_parser v;
908        } parsers[] = {
909                { "USERS ", skype_parse_users },
910                { "USER ", skype_parse_user },
911                { "CHATMESSAGE ", skype_parse_chatmessage },
912                { "CALL ", skype_parse_call },
913                { "FILETRANSFER ", skype_parse_filetransfer },
914                { "CHAT ", skype_parse_chat },
915                { "PASSWORD ", skype_parse_password },
916                { "PROFILE PSTN_BALANCE ", skype_parse_profile },
917                { "PING", skype_parse_ping },
918                { "CHATS ", skype_parse_chats },
919        };
920
921        if (!sd || sd->fd == -1)
922                return FALSE;
923        /* Read the whole data. */
924        st = ssl_read(sd->ssl, buf, sizeof(buf));
925        if (st > 0) {
926                buf[st] = '\0';
927                /* Then split it up to lines. */
928                lines = g_strsplit(buf, "\n", 0);
929                lineptr = lines;
930                while ((line = *lineptr)) {
931                        if (!strlen(line))
932                                break;
933                        if (set_getbool(&ic->acc->set, "skypeconsole_receive"))
934                                imcb_buddy_msg(ic, "skypeconsole", line, 0, 0);
935                        for (i = 0; i < ARRAY_SIZE(parsers); i++)
936                                if (!strncmp(line, parsers[i].k,
937                                        strlen(parsers[i].k))) {
938                                        parsers[i].v(ic, line);
939                                        break;
940                                }
941                        lineptr++;
942                }
943                g_strfreev(lines);
944        } else if (st == 0 || (st < 0 && !sockerr_again())) {
945                closesocket(sd->fd);
946                sd->fd = -1;
947
948                imcb_error(ic, "Error while reading from server");
949                imc_logout(ic, TRUE);
950                return FALSE;
951        }
952        return TRUE;
953}
954
955gboolean skype_start_stream(struct im_connection *ic)
956{
957        struct skype_data *sd = ic->proto_data;
958        int st;
959
960        if (!sd)
961                return FALSE;
962
963        if (sd->bfd <= 0)
964                sd->bfd = b_input_add(sd->fd, B_EV_IO_READ,
965                        skype_read_callback, ic);
966
967        /* Log in */
968        skype_printf(ic, "USERNAME %s\n", ic->acc->user);
969        skype_printf(ic, "PASSWORD %s\n", ic->acc->pass);
970
971        /* This will download all buddies. */
972        st = skype_printf(ic, "SEARCH FRIENDS\n");
973        skype_printf(ic, "SET USERSTATUS ONLINE\n");
974
975        /* Auto join to bookmarked chats if requested.*/
976        if (set_getbool(&ic->acc->set, "auto_join"))
977                skype_printf(ic, "SEARCH BOOKMARKEDCHATS\n");
978        return st;
979}
980
981gboolean skype_connected(gpointer data, void *source, b_input_condition cond)
982{
983        struct im_connection *ic = data;
984        struct skype_data *sd = ic->proto_data;
985        if (!source) {
986                sd->ssl = NULL;
987                imcb_error(ic, "Could not connect to server");
988                imc_logout(ic, TRUE);
989                return FALSE;
990        }
991        imcb_log(ic, "Connected to server, logging in");
992        return skype_start_stream(ic);
993}
994
995static void skype_login(account_t *acc)
996{
997        struct im_connection *ic = imcb_new(acc);
998        struct skype_data *sd = g_new0(struct skype_data, 1);
999
1000        ic->proto_data = sd;
1001
1002        imcb_log(ic, "Connecting");
1003        sd->ssl = ssl_connect(set_getstr(&acc->set, "server"),
1004                set_getint(&acc->set, "port"), skype_connected, ic);
1005        sd->fd = sd->ssl ? ssl_getfd(sd->ssl) : -1;
1006        sd->username = g_strdup(acc->user);
1007
1008        sd->ic = ic;
1009
1010        if (set_getbool(&acc->set, "skypeconsole"))
1011                imcb_add_buddy(ic, "skypeconsole", NULL);
1012}
1013
1014static void skype_logout(struct im_connection *ic)
1015{
1016        struct skype_data *sd = ic->proto_data;
1017
1018        skype_printf(ic, "SET USERSTATUS OFFLINE\n");
1019
1020        g_free(sd->username);
1021        g_free(sd->handle);
1022        g_free(sd);
1023        ic->proto_data = NULL;
1024}
1025
1026static int skype_buddy_msg(struct im_connection *ic, char *who, char *message,
1027                           int flags)
1028{
1029        char *ptr, *nick;
1030        int st;
1031
1032        nick = g_strdup(who);
1033        ptr = strchr(nick, '@');
1034        if (ptr)
1035                *ptr = '\0';
1036
1037        if (!strncmp(who, "skypeconsole", 12))
1038                st = skype_printf(ic, "%s\n", message);
1039        else
1040                st = skype_printf(ic, "MESSAGE %s %s\n", nick, message);
1041        g_free(nick);
1042
1043        return st;
1044}
1045
1046const struct skype_away_state *skype_away_state_by_name(char *name)
1047{
1048        int i;
1049
1050        for (i = 0; skype_away_state_list[i].full_name; i++)
1051                if (g_strcasecmp(skype_away_state_list[i].full_name, name) == 0)
1052                        return skype_away_state_list + i;
1053
1054        return NULL;
1055}
1056
1057static void skype_set_away(struct im_connection *ic, char *state_txt,
1058                           char *message)
1059{
1060        const struct skype_away_state *state;
1061
1062        if (state_txt == NULL)
1063                state = skype_away_state_by_name("Online");
1064        else
1065                state = skype_away_state_by_name(state_txt);
1066        skype_printf(ic, "SET USERSTATUS %s\n", state->code);
1067}
1068
1069static GList *skype_away_states(struct im_connection *ic)
1070{
1071        static GList *l;
1072        int i;
1073
1074        if (l == NULL)
1075                for (i = 0; skype_away_state_list[i].full_name; i++)
1076                        l = g_list_append(l,
1077                                (void *)skype_away_state_list[i].full_name);
1078
1079        return l;
1080}
1081
1082static char *skype_set_display_name(set_t *set, char *value)
1083{
1084        account_t *acc = set->data;
1085        struct im_connection *ic = acc->ic;
1086
1087        skype_printf(ic, "SET PROFILE FULLNAME %s", value);
1088        return value;
1089}
1090
1091static char *skype_set_balance(set_t *set, char *value)
1092{
1093        account_t *acc = set->data;
1094        struct im_connection *ic = acc->ic;
1095
1096        skype_printf(ic, "GET PROFILE PSTN_BALANCE");
1097        return value;
1098}
1099
1100static char *skype_set_call(set_t *set, char *value)
1101{
1102        account_t *acc = set->data;
1103        struct im_connection *ic = acc->ic;
1104        struct skype_data *sd = ic->proto_data;
1105        char *nick, *ptr;
1106
1107        if (value) {
1108#if BITLBEE_VERSION_CODE < BITLBEE_VER(1, 3, 0)
1109                /* IRC stuff shouldn't be touched from inside IM modules. */
1110                user_t *u = user_find(acc->irc, value);
1111                /* We are starting a call */
1112                if (u)
1113                        nick = g_strdup(u->handle);
1114                else
1115#endif
1116                        nick = g_strdup(value);
1117                ptr = strchr(nick, '@');
1118                if (ptr)
1119                        *ptr = '\0';
1120
1121                skype_printf(ic, "CALL %s", nick);
1122                g_free(nick);
1123        } else {
1124                /* We are ending a call */
1125                if (sd->call_id) {
1126                        skype_printf(ic, "SET CALL %s STATUS FINISHED",
1127                                sd->call_id);
1128                        g_free(sd->call_id);
1129                        sd->call_id = NULL;
1130                } else
1131                        imcb_error(ic, "There are no active calls currently.");
1132        }
1133        return value;
1134}
1135
1136static void skype_add_buddy(struct im_connection *ic, char *who, char *group)
1137{
1138        char *nick, *ptr;
1139
1140        nick = g_strdup(who);
1141        ptr = strchr(nick, '@');
1142        if (ptr)
1143                *ptr = '\0';
1144        skype_printf(ic, "SET USER %s BUDDYSTATUS 2 Please authorize me\n",
1145                nick);
1146        g_free(nick);
1147}
1148
1149static void skype_remove_buddy(struct im_connection *ic, char *who, char *group)
1150{
1151        char *nick, *ptr;
1152
1153        nick = g_strdup(who);
1154        ptr = strchr(nick, '@');
1155        if (ptr)
1156                *ptr = '\0';
1157        skype_printf(ic, "SET USER %s BUDDYSTATUS 1\n", nick);
1158        g_free(nick);
1159}
1160
1161void skype_chat_msg(struct groupchat *gc, char *message, int flags)
1162{
1163        struct im_connection *ic = gc->ic;
1164        skype_printf(ic, "CHATMESSAGE %s %s\n", gc->title, message);
1165}
1166
1167void skype_chat_leave(struct groupchat *gc)
1168{
1169        struct im_connection *ic = gc->ic;
1170        skype_printf(ic, "ALTER CHAT %s LEAVE\n", gc->title);
1171        gc->data = (void *)TRUE;
1172}
1173
1174void skype_chat_invite(struct groupchat *gc, char *who, char *message)
1175{
1176        struct im_connection *ic = gc->ic;
1177        char *ptr, *nick;
1178        nick = g_strdup(message);
1179        ptr = strchr(nick, '@');
1180        if (ptr)
1181                *ptr = '\0';
1182        skype_printf(ic, "ALTER CHAT %s ADDMEMBERS %s\n", gc->title, nick);
1183        g_free(nick);
1184}
1185
1186void skype_chat_topic(struct groupchat *gc, char *message)
1187{
1188        struct im_connection *ic = gc->ic;
1189        struct skype_data *sd = ic->proto_data;
1190        skype_printf(ic, "ALTER CHAT %s SETTOPIC %s\n",
1191                gc->title, message);
1192        sd->topic_wait = 1;
1193}
1194
1195struct groupchat *skype_chat_with(struct im_connection *ic, char *who)
1196{
1197        struct skype_data *sd = ic->proto_data;
1198        char *ptr, *nick;
1199        nick = g_strdup(who);
1200        ptr = strchr(nick, '@');
1201        if (ptr)
1202                *ptr = '\0';
1203        skype_printf(ic, "CHAT CREATE %s\n", nick);
1204        sd->groupchat_with = g_strdup(nick);
1205        g_free(nick);
1206        /* We create a fake chat for now. We will replace it with a real one in
1207         * the real callback. */
1208        return imcb_chat_new(ic, "");
1209}
1210
1211static void skype_get_info(struct im_connection *ic, char *who)
1212{
1213        char *ptr, *nick;
1214        nick = g_strdup(who);
1215        ptr = strchr(nick, '@');
1216        if (ptr)
1217                *ptr = '\0';
1218        skype_printf(ic, "GET USER %s FULLNAME\n", nick);
1219        skype_printf(ic, "GET USER %s PHONE_HOME\n", nick);
1220        skype_printf(ic, "GET USER %s PHONE_OFFICE\n", nick);
1221        skype_printf(ic, "GET USER %s PHONE_MOBILE\n", nick);
1222        skype_printf(ic, "GET USER %s NROF_AUTHED_BUDDIES\n", nick);
1223        skype_printf(ic, "GET USER %s TIMEZONE\n", nick);
1224        skype_printf(ic, "GET USER %s LASTONLINETIMESTAMP\n", nick);
1225        skype_printf(ic, "GET USER %s BIRTHDAY\n", nick);
1226        skype_printf(ic, "GET USER %s SEX\n", nick);
1227        skype_printf(ic, "GET USER %s LANGUAGE\n", nick);
1228        skype_printf(ic, "GET USER %s COUNTRY\n", nick);
1229        skype_printf(ic, "GET USER %s PROVINCE\n", nick);
1230        skype_printf(ic, "GET USER %s CITY\n", nick);
1231        skype_printf(ic, "GET USER %s HOMEPAGE\n", nick);
1232        skype_printf(ic, "GET USER %s ABOUT\n", nick);
1233}
1234
1235static void skype_set_my_name(struct im_connection *ic, char *info)
1236{
1237        skype_set_display_name(set_find(&ic->acc->set, "display_name"), info);
1238}
1239
1240static void skype_init(account_t *acc)
1241{
1242        set_t *s;
1243
1244        s = set_add(&acc->set, "server", SKYPE_DEFAULT_SERVER, set_eval_account,
1245                acc);
1246        s->flags |= ACC_SET_OFFLINE_ONLY;
1247
1248        s = set_add(&acc->set, "port", SKYPE_DEFAULT_PORT, set_eval_int, acc);
1249        s->flags |= ACC_SET_OFFLINE_ONLY;
1250
1251        s = set_add(&acc->set, "display_name", NULL, skype_set_display_name,
1252                acc);
1253        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1254
1255        s = set_add(&acc->set, "call", NULL, skype_set_call, acc);
1256        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1257
1258        s = set_add(&acc->set, "balance", NULL, skype_set_balance, acc);
1259        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1260
1261        s = set_add(&acc->set, "skypeout_offline", "true", set_eval_bool, acc);
1262
1263        s = set_add(&acc->set, "skypeconsole", "false", set_eval_bool, acc);
1264        s->flags |= ACC_SET_OFFLINE_ONLY;
1265
1266        s = set_add(&acc->set, "skypeconsole_receive", "false", set_eval_bool,
1267                acc);
1268        s->flags |= ACC_SET_OFFLINE_ONLY;
1269
1270        s = set_add(&acc->set, "auto_join", "false", set_eval_bool, acc);
1271        s->flags |= ACC_SET_OFFLINE_ONLY;
1272
1273        s = set_add(&acc->set, "test_join", "false", set_eval_bool, acc);
1274        s->flags |= ACC_SET_OFFLINE_ONLY;
1275
1276        s = set_add(&acc->set, "show_moods", "false", set_eval_bool, acc);
1277
1278        s = set_add(&acc->set, "edit_prefix", "EDIT:",
1279                        NULL, acc);
1280}
1281
1282void init_plugin(void)
1283{
1284        struct prpl *ret = g_new0(struct prpl, 1);
1285
1286        ret->name = "skype";
1287        ret->login = skype_login;
1288        ret->init = skype_init;
1289        ret->logout = skype_logout;
1290        ret->buddy_msg = skype_buddy_msg;
1291        ret->get_info = skype_get_info;
1292        ret->set_my_name = skype_set_my_name;
1293        ret->away_states = skype_away_states;
1294        ret->set_away = skype_set_away;
1295        ret->add_buddy = skype_add_buddy;
1296        ret->remove_buddy = skype_remove_buddy;
1297        ret->chat_msg = skype_chat_msg;
1298        ret->chat_leave = skype_chat_leave;
1299        ret->chat_invite = skype_chat_invite;
1300        ret->chat_with = skype_chat_with;
1301        ret->handle_cmp = g_strcasecmp;
1302        ret->chat_topic = skype_chat_topic;
1303        register_protocol(ret);
1304}
Note: See TracBrowser for help on using the repository browser.