source: skype/skype.c @ bb9a15f

Last change on this file since bb9a15f was 7670b02, checked in by Miklos Vajna <vmiklos@…>, at 2010-08-03T12:39:52Z

build for <1.3

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