source: skype/skype.c @ 78d22cd0

Last change on this file since 78d22cd0 was 78d22cd0, checked in by Miklos Vajna <vmiklos@…>, at 2010-08-03T11:12:59Z

BitlBee 1.3.0 compatibility patch

  • Property mode set to 100644
File size: 33.7 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#ifndef GAIM_INPUT_READ
37#define GAIM_INPUT_READ B_EV_IO_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#if BITLBEE_VERSION_CODE >= BITLBEE_VER(1, 2, 6)
793                imcb_chat_name_hint(gc, id);
794#endif
795                skype_printf(ic, "GET CHAT %s ADDER\n", id);
796                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
797        } else if (!strcmp(info, "STATUS DIALOG") && sd->groupchat_with) {
798                gc = imcb_chat_new(ic, id);
799#if BITLBEE_VERSION_CODE >= BITLBEE_VER(1, 2, 6)
800                imcb_chat_name_hint(gc, id);
801#endif
802                /* According to the docs this
803                 * is necessary. However it
804                 * does not seem the situation
805                 * and it would open an extra
806                 * window on our client, so
807                 * just leave it out. */
808                /*skype_printf(ic, "OPEN CHAT %s\n", id);*/
809                g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
810                                sd->groupchat_with);
811                imcb_chat_add_buddy(gc, buf);
812                imcb_chat_add_buddy(gc, sd->username);
813                g_free(sd->groupchat_with);
814                sd->groupchat_with = NULL;
815                skype_printf(ic, "GET CHAT %s ADDER\n", id);
816                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
817        } else if (!strcmp(info, "STATUS UNSUBSCRIBED")) {
818                gc = skype_chat_by_name(ic, id);
819                if (gc)
820                        gc->data = (void *)FALSE;
821        } else if (!strncmp(info, "ADDER ", 6)) {
822                info += 6;
823                g_free(sd->adder);
824                sd->adder = g_strdup_printf("%s@skype.com", info);
825        } else if (!strncmp(info, "TOPIC ", 6)) {
826                info += 6;
827                gc = skype_chat_by_name(ic, id);
828                if (gc && (sd->adder || sd->topic_wait)) {
829                        if (sd->topic_wait) {
830                                sd->adder = g_strdup(sd->username);
831                                sd->topic_wait = 0;
832                        }
833                        imcb_chat_topic(gc, sd->adder, info, 0);
834                        g_free(sd->adder);
835                        sd->adder = NULL;
836                }
837        } else if (!strncmp(info, "ACTIVEMEMBERS ", 14)) {
838                info += 14;
839                gc = skype_chat_by_name(ic, id);
840                /* Hack! We set ->data to TRUE
841                 * while we're on the channel
842                 * so that we won't rejoin
843                 * after a /part. */
844                if (!gc || gc->data)
845                        return;
846                char **members = g_strsplit(info, " ", 0);
847                int i;
848                for (i = 0; members[i]; i++) {
849                        if (!strcmp(members[i], sd->username))
850                                continue;
851                        g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
852                                        members[i]);
853                        if (!g_list_find_custom(gc->in_room, buf,
854                                (GCompareFunc)strcmp))
855                                imcb_chat_add_buddy(gc, buf);
856                }
857                imcb_chat_add_buddy(gc, sd->username);
858                g_strfreev(members);
859        }
860}
861
862static void skype_parse_password(struct im_connection *ic, char *line)
863{
864        if (!strncmp(line+9, "OK", 2))
865                imcb_connected(ic);
866        else {
867                imcb_error(ic, "Authentication Failed");
868                imc_logout(ic, TRUE);
869        }
870}
871
872static void skype_parse_profile(struct im_connection *ic, char *line)
873{
874        imcb_log(ic, "SkypeOut balance value is '%s'.", line+21);
875}
876
877static void skype_parse_ping(struct im_connection *ic, char *line)
878{
879        skype_printf(ic, "PONG\n");
880}
881
882static void skype_parse_chats(struct im_connection *ic, char *line)
883{
884        char **i;
885        char **chats = g_strsplit(line + 6, ", ", 0);
886
887        i = chats;
888        while (*i) {
889                skype_printf(ic, "GET CHAT %s STATUS\n", *i);
890                skype_printf(ic, "GET CHAT %s ACTIVEMEMBERS\n", *i);
891                i++;
892        }
893        g_strfreev(chats);
894}
895
896typedef void (*skype_parser)(struct im_connection *ic, char *line);
897
898static gboolean skype_read_callback(gpointer data, gint fd,
899                                    b_input_condition cond)
900{
901        struct im_connection *ic = data;
902        struct skype_data *sd = ic->proto_data;
903        char buf[IRC_LINE_SIZE];
904        int st, i;
905        char **lines, **lineptr, *line;
906        static struct parse_map {
907                char *k;
908                skype_parser v;
909        } parsers[] = {
910                { "USERS ", skype_parse_users },
911                { "USER ", skype_parse_user },
912                { "CHATMESSAGE ", skype_parse_chatmessage },
913                { "CALL ", skype_parse_call },
914                { "FILETRANSFER ", skype_parse_filetransfer },
915                { "CHAT ", skype_parse_chat },
916                { "PASSWORD ", skype_parse_password },
917                { "PROFILE PSTN_BALANCE ", skype_parse_profile },
918                { "PING", skype_parse_ping },
919                { "CHATS ", skype_parse_chats },
920        };
921
922        if (!sd || sd->fd == -1)
923                return FALSE;
924        /* Read the whole data. */
925        st = ssl_read(sd->ssl, buf, sizeof(buf));
926        if (st > 0) {
927                buf[st] = '\0';
928                /* Then split it up to lines. */
929                lines = g_strsplit(buf, "\n", 0);
930                lineptr = lines;
931                while ((line = *lineptr)) {
932                        if (!strlen(line))
933                                break;
934                        if (set_getbool(&ic->acc->set, "skypeconsole_receive"))
935                                imcb_buddy_msg(ic, "skypeconsole", line, 0, 0);
936                        for (i = 0; i < ARRAY_SIZE(parsers); i++)
937                                if (!strncmp(line, parsers[i].k,
938                                        strlen(parsers[i].k))) {
939                                        parsers[i].v(ic, line);
940                                        break;
941                                }
942                        lineptr++;
943                }
944                g_strfreev(lines);
945        } else if (st == 0 || (st < 0 && !sockerr_again())) {
946                closesocket(sd->fd);
947                sd->fd = -1;
948
949                imcb_error(ic, "Error while reading from server");
950                imc_logout(ic, TRUE);
951                return FALSE;
952        }
953        return TRUE;
954}
955
956gboolean skype_start_stream(struct im_connection *ic)
957{
958        struct skype_data *sd = ic->proto_data;
959        int st;
960
961        if (!sd)
962                return FALSE;
963
964        if (sd->bfd <= 0)
965                sd->bfd = b_input_add(sd->fd, GAIM_INPUT_READ,
966                        skype_read_callback, ic);
967
968        /* Log in */
969        skype_printf(ic, "USERNAME %s\n", ic->acc->user);
970        skype_printf(ic, "PASSWORD %s\n", ic->acc->pass);
971
972        /* This will download all buddies. */
973        st = skype_printf(ic, "SEARCH FRIENDS\n");
974        skype_printf(ic, "SET USERSTATUS ONLINE\n");
975
976        /* Auto join to bookmarked chats if requested.*/
977        if (set_getbool(&ic->acc->set, "auto_join"))
978                skype_printf(ic, "SEARCH BOOKMARKEDCHATS\n");
979        return st;
980}
981
982gboolean skype_connected(gpointer data, void *source, b_input_condition cond)
983{
984        struct im_connection *ic = data;
985        struct skype_data *sd = ic->proto_data;
986        if (!source) {
987                sd->ssl = NULL;
988                imcb_error(ic, "Could not connect to server");
989                imc_logout(ic, TRUE);
990                return FALSE;
991        }
992        imcb_log(ic, "Connected to server, logging in");
993        return skype_start_stream(ic);
994}
995
996static void skype_login(account_t *acc)
997{
998        struct im_connection *ic = imcb_new(acc);
999        struct skype_data *sd = g_new0(struct skype_data, 1);
1000
1001        ic->proto_data = sd;
1002
1003        imcb_log(ic, "Connecting");
1004        sd->ssl = ssl_connect(set_getstr(&acc->set, "server"),
1005                set_getint(&acc->set, "port"), skype_connected, ic);
1006        sd->fd = sd->ssl ? ssl_getfd(sd->ssl) : -1;
1007        sd->username = g_strdup(acc->user);
1008
1009        sd->ic = ic;
1010
1011        if (set_getbool(&acc->set, "skypeconsole"))
1012                imcb_add_buddy(ic, "skypeconsole", NULL);
1013}
1014
1015static void skype_logout(struct im_connection *ic)
1016{
1017        struct skype_data *sd = ic->proto_data;
1018
1019        skype_printf(ic, "SET USERSTATUS OFFLINE\n");
1020
1021        g_free(sd->username);
1022        g_free(sd->handle);
1023        g_free(sd);
1024        ic->proto_data = NULL;
1025}
1026
1027static int skype_buddy_msg(struct im_connection *ic, char *who, char *message,
1028                           int flags)
1029{
1030        char *ptr, *nick;
1031        int st;
1032
1033        nick = g_strdup(who);
1034        ptr = strchr(nick, '@');
1035        if (ptr)
1036                *ptr = '\0';
1037
1038        if (!strncmp(who, "skypeconsole", 12))
1039                st = skype_printf(ic, "%s\n", message);
1040        else
1041                st = skype_printf(ic, "MESSAGE %s %s\n", nick, message);
1042        g_free(nick);
1043
1044        return st;
1045}
1046
1047const struct skype_away_state *skype_away_state_by_name(char *name)
1048{
1049        int i;
1050
1051        for (i = 0; skype_away_state_list[i].full_name; i++)
1052                if (g_strcasecmp(skype_away_state_list[i].full_name, name) == 0)
1053                        return skype_away_state_list + i;
1054
1055        return NULL;
1056}
1057
1058static void skype_set_away(struct im_connection *ic, char *state_txt,
1059                           char *message)
1060{
1061        const struct skype_away_state *state;
1062
1063        if (state_txt == NULL)
1064                state = skype_away_state_by_name("Online");
1065        else
1066                state = skype_away_state_by_name(state_txt);
1067        skype_printf(ic, "SET USERSTATUS %s\n", state->code);
1068}
1069
1070static GList *skype_away_states(struct im_connection *ic)
1071{
1072        static GList *l;
1073        int i;
1074
1075        if (l == NULL)
1076                for (i = 0; skype_away_state_list[i].full_name; i++)
1077                        l = g_list_append(l,
1078                                (void *)skype_away_state_list[i].full_name);
1079
1080        return l;
1081}
1082
1083static char *skype_set_display_name(set_t *set, char *value)
1084{
1085        account_t *acc = set->data;
1086        struct im_connection *ic = acc->ic;
1087
1088        skype_printf(ic, "SET PROFILE FULLNAME %s", value);
1089        return value;
1090}
1091
1092static char *skype_set_balance(set_t *set, char *value)
1093{
1094        account_t *acc = set->data;
1095        struct im_connection *ic = acc->ic;
1096
1097        skype_printf(ic, "GET PROFILE PSTN_BALANCE");
1098        return value;
1099}
1100
1101static char *skype_set_call(set_t *set, char *value)
1102{
1103        account_t *acc = set->data;
1104        struct im_connection *ic = acc->ic;
1105        struct skype_data *sd = ic->proto_data;
1106        char *nick, *ptr;
1107
1108        if (value) {
1109#if BITLBEE_VERSION_CODE < BITLBEE_VER(1, 3, 0)
1110                /* IRC stuff shouldn't be touched from inside IM modules. */
1111                user_t *u = user_find(acc->irc, value);
1112                /* We are starting a call */
1113                if (u)
1114                        nick = g_strdup(u->handle);
1115                else
1116#endif
1117                        nick = g_strdup(value);
1118                ptr = strchr(nick, '@');
1119                if (ptr)
1120                        *ptr = '\0';
1121
1122                skype_printf(ic, "CALL %s", nick);
1123                g_free(nick);
1124        } else {
1125                /* We are ending a call */
1126                if (sd->call_id) {
1127                        skype_printf(ic, "SET CALL %s STATUS FINISHED",
1128                                sd->call_id);
1129                        g_free(sd->call_id);
1130                        sd->call_id = NULL;
1131                } else
1132                        imcb_error(ic, "There are no active calls currently.");
1133        }
1134        return value;
1135}
1136
1137static void skype_add_buddy(struct im_connection *ic, char *who, char *group)
1138{
1139        char *nick, *ptr;
1140
1141        nick = g_strdup(who);
1142        ptr = strchr(nick, '@');
1143        if (ptr)
1144                *ptr = '\0';
1145        skype_printf(ic, "SET USER %s BUDDYSTATUS 2 Please authorize me\n",
1146                nick);
1147        g_free(nick);
1148}
1149
1150static void skype_remove_buddy(struct im_connection *ic, char *who, char *group)
1151{
1152        char *nick, *ptr;
1153
1154        nick = g_strdup(who);
1155        ptr = strchr(nick, '@');
1156        if (ptr)
1157                *ptr = '\0';
1158        skype_printf(ic, "SET USER %s BUDDYSTATUS 1\n", nick);
1159        g_free(nick);
1160}
1161
1162void skype_chat_msg(struct groupchat *gc, char *message, int flags)
1163{
1164        struct im_connection *ic = gc->ic;
1165        skype_printf(ic, "CHATMESSAGE %s %s\n", gc->title, message);
1166}
1167
1168void skype_chat_leave(struct groupchat *gc)
1169{
1170        struct im_connection *ic = gc->ic;
1171        skype_printf(ic, "ALTER CHAT %s LEAVE\n", gc->title);
1172        gc->data = (void *)TRUE;
1173}
1174
1175void skype_chat_invite(struct groupchat *gc, char *who, char *message)
1176{
1177        struct im_connection *ic = gc->ic;
1178        char *ptr, *nick;
1179        nick = g_strdup(message);
1180        ptr = strchr(nick, '@');
1181        if (ptr)
1182                *ptr = '\0';
1183        skype_printf(ic, "ALTER CHAT %s ADDMEMBERS %s\n", gc->title, nick);
1184        g_free(nick);
1185}
1186
1187void skype_chat_topic(struct groupchat *gc, char *message)
1188{
1189        struct im_connection *ic = gc->ic;
1190        struct skype_data *sd = ic->proto_data;
1191        skype_printf(ic, "ALTER CHAT %s SETTOPIC %s\n",
1192                gc->title, message);
1193        sd->topic_wait = 1;
1194}
1195
1196struct groupchat *skype_chat_with(struct im_connection *ic, char *who)
1197{
1198        struct skype_data *sd = ic->proto_data;
1199        char *ptr, *nick;
1200        nick = g_strdup(who);
1201        ptr = strchr(nick, '@');
1202        if (ptr)
1203                *ptr = '\0';
1204        skype_printf(ic, "CHAT CREATE %s\n", nick);
1205        sd->groupchat_with = g_strdup(nick);
1206        g_free(nick);
1207        /* We create a fake chat for now. We will replace it with a real one in
1208         * the real callback. */
1209        return imcb_chat_new(ic, "");
1210}
1211
1212static void skype_get_info(struct im_connection *ic, char *who)
1213{
1214        char *ptr, *nick;
1215        nick = g_strdup(who);
1216        ptr = strchr(nick, '@');
1217        if (ptr)
1218                *ptr = '\0';
1219        skype_printf(ic, "GET USER %s FULLNAME\n", nick);
1220        skype_printf(ic, "GET USER %s PHONE_HOME\n", nick);
1221        skype_printf(ic, "GET USER %s PHONE_OFFICE\n", nick);
1222        skype_printf(ic, "GET USER %s PHONE_MOBILE\n", nick);
1223        skype_printf(ic, "GET USER %s NROF_AUTHED_BUDDIES\n", nick);
1224        skype_printf(ic, "GET USER %s TIMEZONE\n", nick);
1225        skype_printf(ic, "GET USER %s LASTONLINETIMESTAMP\n", nick);
1226        skype_printf(ic, "GET USER %s BIRTHDAY\n", nick);
1227        skype_printf(ic, "GET USER %s SEX\n", nick);
1228        skype_printf(ic, "GET USER %s LANGUAGE\n", nick);
1229        skype_printf(ic, "GET USER %s COUNTRY\n", nick);
1230        skype_printf(ic, "GET USER %s PROVINCE\n", nick);
1231        skype_printf(ic, "GET USER %s CITY\n", nick);
1232        skype_printf(ic, "GET USER %s HOMEPAGE\n", nick);
1233        skype_printf(ic, "GET USER %s ABOUT\n", nick);
1234}
1235
1236static void skype_set_my_name(struct im_connection *ic, char *info)
1237{
1238        skype_set_display_name(set_find(&ic->acc->set, "display_name"), info);
1239}
1240
1241static void skype_init(account_t *acc)
1242{
1243        set_t *s;
1244
1245        s = set_add(&acc->set, "server", SKYPE_DEFAULT_SERVER, set_eval_account,
1246                acc);
1247        s->flags |= ACC_SET_OFFLINE_ONLY;
1248
1249        s = set_add(&acc->set, "port", SKYPE_DEFAULT_PORT, set_eval_int, acc);
1250        s->flags |= ACC_SET_OFFLINE_ONLY;
1251
1252        s = set_add(&acc->set, "display_name", NULL, skype_set_display_name,
1253                acc);
1254        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1255
1256        s = set_add(&acc->set, "call", NULL, skype_set_call, acc);
1257        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1258
1259        s = set_add(&acc->set, "balance", NULL, skype_set_balance, acc);
1260        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1261
1262        s = set_add(&acc->set, "skypeout_offline", "true", set_eval_bool, acc);
1263
1264        s = set_add(&acc->set, "skypeconsole", "false", set_eval_bool, acc);
1265        s->flags |= ACC_SET_OFFLINE_ONLY;
1266
1267        s = set_add(&acc->set, "skypeconsole_receive", "false", set_eval_bool,
1268                acc);
1269        s->flags |= ACC_SET_OFFLINE_ONLY;
1270
1271        s = set_add(&acc->set, "auto_join", "false", set_eval_bool, acc);
1272        s->flags |= ACC_SET_OFFLINE_ONLY;
1273
1274        s = set_add(&acc->set, "test_join", "false", set_eval_bool, acc);
1275        s->flags |= ACC_SET_OFFLINE_ONLY;
1276
1277        s = set_add(&acc->set, "show_moods", "false", set_eval_bool, acc);
1278
1279        s = set_add(&acc->set, "edit_prefix", "EDIT:",
1280                        NULL, acc);
1281}
1282
1283void init_plugin(void)
1284{
1285        struct prpl *ret = g_new0(struct prpl, 1);
1286
1287        ret->name = "skype";
1288        ret->login = skype_login;
1289        ret->init = skype_init;
1290        ret->logout = skype_logout;
1291        ret->buddy_msg = skype_buddy_msg;
1292        ret->get_info = skype_get_info;
1293        ret->set_my_name = skype_set_my_name;
1294        ret->away_states = skype_away_states;
1295        ret->set_away = skype_set_away;
1296        ret->add_buddy = skype_add_buddy;
1297        ret->remove_buddy = skype_remove_buddy;
1298        ret->chat_msg = skype_chat_msg;
1299        ret->chat_leave = skype_chat_leave;
1300        ret->chat_invite = skype_chat_invite;
1301        ret->chat_with = skype_chat_with;
1302        ret->handle_cmp = g_strcasecmp;
1303        ret->chat_topic = skype_chat_topic;
1304        register_protocol(ret);
1305}
Note: See TracBrowser for help on using the repository browser.