source: protocols/skype/skype.c @ 2487112

Last change on this file since 2487112 was 2487112, checked in by Miklos Vajna <vmiklos@…>, at 2013-01-16T20:26:58Z

skype_parse_chat: read MEMBERS, not ACTIVEMEMBERS

This makes /invite working again. No idea when this got broken, but I
promise the next commit will add a tescase for it.

Also, with this, other groupchat members will be already present in the
gropuchat when we chain, finally solving the annoying fake joins
presenting till now.

  • Property mode set to 100644
File size: 41.0 KB
RevLine 
[7daec06]1/*
2 *  skype.c - Skype plugin for BitlBee
[5adcc65]3 *
[9ec6b36]4 *  Copyright (c) 2007-2013 by Miklos Vajna <vmiklos@vmiklos.hu>
[f06e3ac]5 *
[7daec06]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
[5adcc65]18 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
[7daec06]19 *  USA.
[f06e3ac]20 */
[7daec06]21
[f9c3e7b]22#define _XOPEN_SOURCE
[1f4fc80]23#define _BSD_SOURCE
[ed2e37f]24#include <poll.h>
[ff18fc1]25#include <stdio.h>
[f06e3ac]26#include <bitlbee.h>
[7f41495]27#include <ssl_client.h>
[f06e3ac]28
[f5aedd91]29#define SKYPE_DEFAULT_SERVER "localhost"
[4bbd9db]30#define SKYPE_DEFAULT_PORT "2727"
[c213d6b]31#define IRC_LINE_SIZE 1024
[ff436ba]32#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
[f06e3ac]33
[bbba374]34/*
35 * Enumerations
36 */
37
[359f4d9]38enum {
[df9255d]39        SKYPE_CALL_RINGING = 1,
[9db0234]40        SKYPE_CALL_MISSED,
[2eb4b1f]41        SKYPE_CALL_CANCELLED,
[acd9478]42        SKYPE_CALL_FINISHED,
[d87daf3]43        SKYPE_CALL_REFUSED
[5365f84]44};
[bbba374]45
[359f4d9]46enum {
[df9255d]47        SKYPE_FILETRANSFER_NEW = 1,
48        SKYPE_FILETRANSFER_FAILED
[5365f84]49};
[df9255d]50
[7daec06]51/*
52 * Structures
53 */
54
[5adcc65]55struct skype_data {
[f06e3ac]56        struct im_connection *ic;
[a3d6427]57        char *username;
[368861e]58        /* The effective file descriptor. We store it here so any function can
59         * write() to it. */
[f06e3ac]60        int fd;
[368861e]61        /* File descriptor returned by bitlbee. we store it so we know when
62         * we're connected and when we aren't. */
63        int bfd;
[c7304b2]64        /* ssl_getfd() uses this to get the file desciptor. */
65        void *ssl;
[2a0f99c]66        /* When we receive a new message id, we query the properties, finally
67         * the chatname. Store the properties here so that we can use
[c81d0ef]68         * imcb_buddy_msg() when we got the chatname. */
[77c1abe]69        char *handle;
[a7af5f0]70        /* List, because of multiline messages. */
71        GList *body;
[2a0f99c]72        char *type;
[bbba374]73        /* This is necessary because we send a notification when we get the
74         * handle. So we store the state here and then we can send a
75         * notification about the handle is in a given status. */
[359f4d9]76        int call_status;
[2eb4b1f]77        char *call_id;
[48181f0]78        char *call_duration;
[d87daf3]79        /* If the call is outgoing or not */
80        int call_out;
[df9255d]81        /* Same for file transfers. */
[359f4d9]82        int filetransfer_status;
[86278cd]83        /* Using /j #nick we want to have a groupchat with two people. Usually
84         * not (default). */
[5adcc65]85        char *groupchat_with;
[f8674db]86        /* The user who invited us to the chat. */
[5adcc65]87        char *adder;
[a5f76a2]88        /* If we are waiting for a confirmation about we changed the topic. */
89        int topic_wait;
[67454bd]90        /* These are used by the info command. */
91        char *info_fullname;
92        char *info_phonehome;
93        char *info_phoneoffice;
94        char *info_phonemobile;
95        char *info_nrbuddies;
96        char *info_tz;
97        char *info_seen;
98        char *info_birthday;
99        char *info_sex;
100        char *info_language;
101        char *info_country;
102        char *info_province;
103        char *info_city;
104        char *info_homepage;
105        char *info_about;
[b054fad]106        /* When a call fails, we get the reason and later we get the failure
107         * event, so store the failure code here till then */
108        int failurereason;
[d9ce18c]109        /* If this is just an update of an already received message. */
110        int is_edit;
[89d6845]111        /* List of struct skype_group* */
112        GList *groups;
[46641bf]113        /* Pending user which has to be added to the next group which is
114         * created. */
115        char *pending_user;
[f06e3ac]116};
117
[5adcc65]118struct skype_away_state {
[adce2de]119        char *code;
120        char *full_name;
121};
122
[5adcc65]123struct skype_buddy_ask_data {
[7daec06]124        struct im_connection *ic;
[e0074cb]125        /* This is also used for call IDs for simplicity */
[7daec06]126        char *handle;
127};
128
[89d6845]129struct skype_group {
130        int id;
131        char *name;
132        GList *users;
133};
134
[7daec06]135/*
136 * Tables
137 */
138
[5adcc65]139const struct skype_away_state skype_away_state_list[] = {
[bc744df]140        { "AWAY", "Away" },
141        { "NA", "Not available" },
142        { "DND", "Do Not Disturb" },
143        { "INVISIBLE", "Invisible" },
144        { "OFFLINE", "Offline" },
[4b740c2]145        { "SKYPEME", "Skype Me" },
146        { "ONLINE", "Online" },
[23411c6]147        { NULL, NULL}
[adce2de]148};
149
[7daec06]150/*
151 * Functions
152 */
[d3cbd17]153
[7c300bb]154int skype_write(struct im_connection *ic, char *buf, int len)
[f06e3ac]155{
156        struct skype_data *sd = ic->proto_data;
[1fb89e3]157        struct pollfd pfd[1];
158
[e8e2892]159        if (!sd->ssl)
160                return FALSE;
161
[1fb89e3]162        pfd[0].fd = sd->fd;
163        pfd[0].events = POLLOUT;
[f06e3ac]164
[7daec06]165        /* This poll is necessary or we'll get a SIGPIPE when we write() to
166         * sd->fd. */
[1fb89e3]167        poll(pfd, 1, 1000);
[5adcc65]168        if (pfd[0].revents & POLLHUP) {
169                imc_logout(ic, TRUE);
[1fb89e3]170                return FALSE;
171        }
[5adcc65]172        ssl_write(sd->ssl, buf, len);
[f06e3ac]173
[9fd4241]174        return TRUE;
[f06e3ac]175}
176
[1f4fc80]177int skype_printf(struct im_connection *ic, char *fmt, ...)
178{
179        va_list args;
180        char str[IRC_LINE_SIZE];
[5d9db76]181
[1f4fc80]182        va_start(args, fmt);
183        vsnprintf(str, IRC_LINE_SIZE, fmt, args);
184        va_end(args);
185
[7c300bb]186        return skype_write(ic, str, strlen(str));
[1f4fc80]187}
188
[5adcc65]189static void skype_buddy_ask_yes(void *data)
[d3cbd17]190{
[039116a]191        struct skype_buddy_ask_data *bla = data;
[1f4fc80]192        skype_printf(bla->ic, "SET USER %s ISAUTHORIZED TRUE",
[8c09bb3]193                bla->handle);
[d3cbd17]194        g_free(bla->handle);
195        g_free(bla);
196}
197
[5adcc65]198static void skype_buddy_ask_no(void *data)
[d3cbd17]199{
[039116a]200        struct skype_buddy_ask_data *bla = data;
[1f4fc80]201        skype_printf(bla->ic, "SET USER %s ISAUTHORIZED FALSE",
[8c09bb3]202                bla->handle);
[d3cbd17]203        g_free(bla->handle);
204        g_free(bla);
205}
206
[5adcc65]207void skype_buddy_ask(struct im_connection *ic, char *handle, char *message)
[d3cbd17]208{
[8c09bb3]209        struct skype_buddy_ask_data *bla = g_new0(struct skype_buddy_ask_data,
210                1);
[d3cbd17]211        char *buf;
212
213        bla->ic = ic;
214        bla->handle = g_strdup(handle);
215
[e1d6b38]216        buf = g_strdup_printf("The user %s wants to add you to his/her buddy list, saying: '%s'.", handle, message);
[5adcc65]217        imcb_ask(ic, buf, bla, skype_buddy_ask_yes, skype_buddy_ask_no);
218        g_free(buf);
[d3cbd17]219}
220
[5adcc65]221static void skype_call_ask_yes(void *data)
[e0074cb]222{
[039116a]223        struct skype_buddy_ask_data *bla = data;
[1f4fc80]224        skype_printf(bla->ic, "SET CALL %s STATUS INPROGRESS",
[8c09bb3]225                bla->handle);
[e0074cb]226        g_free(bla->handle);
227        g_free(bla);
228}
229
[5adcc65]230static void skype_call_ask_no(void *data)
[e0074cb]231{
[039116a]232        struct skype_buddy_ask_data *bla = data;
[1f4fc80]233        skype_printf(bla->ic, "SET CALL %s STATUS FINISHED",
[8c09bb3]234                bla->handle);
[e0074cb]235        g_free(bla->handle);
236        g_free(bla);
237}
238
[5adcc65]239void skype_call_ask(struct im_connection *ic, char *call_id, char *message)
[e0074cb]240{
[8c09bb3]241        struct skype_buddy_ask_data *bla = g_new0(struct skype_buddy_ask_data,
242                1);
[e0074cb]243
244        bla->ic = ic;
245        bla->handle = g_strdup(call_id);
246
[5adcc65]247        imcb_ask(ic, message, bla, skype_call_ask_yes, skype_call_ask_no);
[e0074cb]248}
[72aa7f0]249
[b054fad]250static char *skype_call_strerror(int err)
251{
[5adcc65]252        switch (err) {
253        case 1:
254                return "Miscellaneous error";
255        case 2:
256                return "User or phone number does not exist.";
257        case 3:
258                return "User is offline";
259        case 4:
260                return "No proxy found";
261        case 5:
262                return "Session terminated.";
263        case 6:
264                return "No common codec found.";
265        case 7:
266                return "Sound I/O error.";
267        case 8:
268                return "Problem with remote sound device.";
269        case 9:
270                return "Call blocked by recipient.";
271        case 10:
272                return "Recipient not a friend.";
273        case 11:
274                return "Current user not authorized by recipient.";
275        case 12:
276                return "Sound recording error.";
277        default:
278                return "Unknown error";
[b054fad]279        }
280}
281
[54ca269]282static char *skype_group_by_username(struct im_connection *ic, char *username)
283{
284        struct skype_data *sd = ic->proto_data;
285        int i, j;
286
287        /* NEEDSWORK: we just search for the first group of the user, multiple
288         * groups / user is not yet supported by BitlBee. */
289
290        for (i = 0; i < g_list_length(sd->groups); i++) {
291                struct skype_group *sg = g_list_nth_data(sd->groups, i);
292                for (j = 0; j < g_list_length(sg->users); j++) {
293                        if (!strcmp(g_list_nth_data(sg->users, j), username))
294                                return sg->name;
295                }
296        }
297        return NULL;
298}
299
[46e9822]300static struct skype_group *skype_group_by_name(struct im_connection *ic, char *name)
301{
302        struct skype_data *sd = ic->proto_data;
303        int i;
304
305        for (i = 0; i < g_list_length(sd->groups); i++) {
306                struct skype_group *sg = g_list_nth_data(sd->groups, i);
307                if (!strcmp(sg->name, name))
308                        return sg;
309        }
310        return NULL;
311}
312
[078b0b9]313static void skype_parse_users(struct im_connection *ic, char *line)
314{
[1f4fc80]315        char **i, **nicks;
[078b0b9]316
317        nicks = g_strsplit(line + 6, ", ", 0);
[1f4fc80]318        for (i = nicks; *i; i++)
319                skype_printf(ic, "GET USER %s ONLINESTATUS\n", *i);
[078b0b9]320        g_strfreev(nicks);
321}
322
[6e14204]323static void skype_parse_user(struct im_connection *ic, char *line)
324{
325        int flags = 0;
326        char *ptr;
327        struct skype_data *sd = ic->proto_data;
328        char *user = strchr(line, ' ');
329        char *status = strrchr(line, ' ');
330
331        status++;
332        ptr = strchr(++user, ' ');
333        if (!ptr)
334                return;
335        *ptr = '\0';
336        ptr++;
[49a3c02]337        if (!strncmp(ptr, "ONLINESTATUS ", 13)) {
338                        if (!strcmp(user, sd->username))
339                                return;
340                        if (!set_getbool(&ic->acc->set, "test_join")
341                                && !strcmp(user, "echo123"))
342                                return;
[6e14204]343                ptr = g_strdup_printf("%s@skype.com", user);
[54ca269]344                imcb_add_buddy(ic, ptr, skype_group_by_username(ic, user));
[62f51ee9]345                if (strcmp(status, "OFFLINE") && (strcmp(status, "SKYPEOUT") ||
346                        !set_getbool(&ic->acc->set, "skypeout_offline")))
[6e14204]347                        flags |= OPT_LOGGED_IN;
[62f51ee9]348                if (strcmp(status, "ONLINE") && strcmp(status, "SKYPEME"))
[6e14204]349                        flags |= OPT_AWAY;
350                imcb_buddy_status(ic, ptr, flags, NULL, NULL);
351                g_free(ptr);
352        } else if (!strncmp(ptr, "RECEIVEDAUTHREQUEST ", 20)) {
353                char *message = ptr + 20;
354                if (strlen(message))
355                        skype_buddy_ask(ic, user, message);
356        } else if (!strncmp(ptr, "BUDDYSTATUS ", 12)) {
357                char *st = ptr + 12;
358                if (!strcmp(st, "3")) {
359                        char *buf = g_strdup_printf("%s@skype.com", user);
[54ca269]360                        imcb_add_buddy(ic, buf, skype_group_by_username(ic, user));
[6e14204]361                        g_free(buf);
362                }
[78d22cd0]363        } else if (!strncmp(ptr, "MOOD_TEXT ", 10)) {
[4c674bb]364                char *buf = g_strdup_printf("%s@skype.com", user);
[78d22cd0]365                bee_user_t *bu = bee_user_by_handle(ic->bee, ic, buf);
366                g_free(buf);
367                buf = ptr + 10;
[451f121]368                if (bu)
[78d22cd0]369                        imcb_buddy_status(ic, bu->handle, bu->flags, NULL,
[4ab7225]370                                        *buf ? buf : NULL);
[78d22cd0]371                if (set_getbool(&ic->acc->set, "show_moods"))
372                        imcb_log(ic, "User `%s' changed mood text to `%s'", user, buf);
[3518933]373        } else if (!strncmp(ptr, "FULLNAME ", 9))
[d7938f9]374                sd->info_fullname = g_strdup(ptr + 9);
[6e14204]375        else if (!strncmp(ptr, "PHONE_HOME ", 11))
[d7938f9]376                sd->info_phonehome = g_strdup(ptr + 11);
[6e14204]377        else if (!strncmp(ptr, "PHONE_OFFICE ", 13))
[d7938f9]378                sd->info_phoneoffice = g_strdup(ptr + 13);
[6e14204]379        else if (!strncmp(ptr, "PHONE_MOBILE ", 13))
[d7938f9]380                sd->info_phonemobile = g_strdup(ptr + 13);
[6e14204]381        else if (!strncmp(ptr, "NROF_AUTHED_BUDDIES ", 20))
[d7938f9]382                sd->info_nrbuddies = g_strdup(ptr + 20);
[6e14204]383        else if (!strncmp(ptr, "TIMEZONE ", 9))
[d7938f9]384                sd->info_tz = g_strdup(ptr + 9);
[6e14204]385        else if (!strncmp(ptr, "LASTONLINETIMESTAMP ", 20))
[d7938f9]386                sd->info_seen = g_strdup(ptr + 20);
[6e14204]387        else if (!strncmp(ptr, "SEX ", 4))
[d7938f9]388                sd->info_sex = g_strdup(ptr + 4);
[6e14204]389        else if (!strncmp(ptr, "LANGUAGE ", 9))
[d7938f9]390                sd->info_language = g_strdup(ptr + 9);
[6e14204]391        else if (!strncmp(ptr, "COUNTRY ", 8))
[d7938f9]392                sd->info_country = g_strdup(ptr + 8);
[6e14204]393        else if (!strncmp(ptr, "PROVINCE ", 9))
[d7938f9]394                sd->info_province = g_strdup(ptr + 9);
[6e14204]395        else if (!strncmp(ptr, "CITY ", 5))
[d7938f9]396                sd->info_city = g_strdup(ptr + 5);
[6e14204]397        else if (!strncmp(ptr, "HOMEPAGE ", 9))
[d7938f9]398                sd->info_homepage = g_strdup(ptr + 9);
[6e14204]399        else if (!strncmp(ptr, "ABOUT ", 6)) {
[85341dd]400                /* Support multiple about lines. */
401                if (!sd->info_about)
402                        sd->info_about = g_strdup(ptr + 6);
403                else {
404                        GString *st = g_string_new(sd->info_about);
405                        g_string_append_printf(st, "\n%s", ptr + 6);
406                        g_free(sd->info_about);
407                        sd->info_about = g_strdup(st->str);
408                        g_string_free(st, TRUE);
409                }
[e1d6b38]410        } else if (!strncmp(ptr, "BIRTHDAY ", 9)) {
[85341dd]411                sd->info_birthday = g_strdup(ptr + 9);
[6e14204]412
413                GString *st = g_string_new("Contact Information\n");
414                g_string_append_printf(st, "Skype Name: %s\n", user);
415                if (sd->info_fullname) {
416                        if (strlen(sd->info_fullname))
[62f51ee9]417                                g_string_append_printf(st, "Full Name: %s\n",
418                                        sd->info_fullname);
[6e14204]419                        g_free(sd->info_fullname);
[7c2daf5f]420                        sd->info_fullname = NULL;
[6e14204]421                }
422                if (sd->info_phonehome) {
423                        if (strlen(sd->info_phonehome))
[62f51ee9]424                                g_string_append_printf(st, "Home Phone: %s\n",
425                                        sd->info_phonehome);
[6e14204]426                        g_free(sd->info_phonehome);
[7c2daf5f]427                        sd->info_phonehome = NULL;
[6e14204]428                }
429                if (sd->info_phoneoffice) {
430                        if (strlen(sd->info_phoneoffice))
[62f51ee9]431                                g_string_append_printf(st, "Office Phone: %s\n",
432                                        sd->info_phoneoffice);
[6e14204]433                        g_free(sd->info_phoneoffice);
[7c2daf5f]434                        sd->info_phoneoffice = NULL;
[6e14204]435                }
436                if (sd->info_phonemobile) {
437                        if (strlen(sd->info_phonemobile))
[62f51ee9]438                                g_string_append_printf(st, "Mobile Phone: %s\n",
439                                        sd->info_phonemobile);
[6e14204]440                        g_free(sd->info_phonemobile);
[7c2daf5f]441                        sd->info_phonemobile = NULL;
[6e14204]442                }
443                g_string_append_printf(st, "Personal Information\n");
444                if (sd->info_nrbuddies) {
445                        if (strlen(sd->info_nrbuddies))
[62f51ee9]446                                g_string_append_printf(st,
447                                        "Contacts: %s\n", sd->info_nrbuddies);
[6e14204]448                        g_free(sd->info_nrbuddies);
[7c2daf5f]449                        sd->info_nrbuddies = NULL;
[6e14204]450                }
451                if (sd->info_tz) {
452                        if (strlen(sd->info_tz)) {
453                                char ib[256];
454                                time_t t = time(NULL);
455                                t += atoi(sd->info_tz)-(60*60*24);
456                                struct tm *gt = gmtime(&t);
457                                strftime(ib, 256, "%H:%M:%S", gt);
[62f51ee9]458                                g_string_append_printf(st,
459                                        "Local Time: %s\n", ib);
[6e14204]460                        }
461                        g_free(sd->info_tz);
[7c2daf5f]462                        sd->info_tz = NULL;
[6e14204]463                }
464                if (sd->info_seen) {
465                        if (strlen(sd->info_seen)) {
466                                char ib[256];
467                                time_t it = atoi(sd->info_seen);
468                                struct tm *tm = localtime(&it);
469                                strftime(ib, 256, ("%Y. %m. %d. %H:%M"), tm);
[62f51ee9]470                                g_string_append_printf(st,
471                                        "Last Seen: %s\n", ib);
[6e14204]472                        }
473                        g_free(sd->info_seen);
[7c2daf5f]474                        sd->info_seen = NULL;
[6e14204]475                }
476                if (sd->info_birthday) {
[62f51ee9]477                        if (strlen(sd->info_birthday) &&
478                                strcmp(sd->info_birthday, "0")) {
[6e14204]479                                char ib[256];
480                                struct tm tm;
481                                strptime(sd->info_birthday, "%Y%m%d", &tm);
482                                strftime(ib, 256, "%B %d, %Y", &tm);
[62f51ee9]483                                g_string_append_printf(st,
484                                        "Birthday: %s\n", ib);
[6e14204]485
486                                strftime(ib, 256, "%Y", &tm);
487                                int year = atoi(ib);
488                                time_t t = time(NULL);
489                                struct tm *lt = localtime(&t);
[62f51ee9]490                                g_string_append_printf(st,
491                                        "Age: %d\n", lt->tm_year+1900-year);
[6e14204]492                        }
493                        g_free(sd->info_birthday);
[7c2daf5f]494                        sd->info_birthday = NULL;
[6e14204]495                }
496                if (sd->info_sex) {
497                        if (strlen(sd->info_sex)) {
498                                char *iptr = sd->info_sex;
499                                while (*iptr++)
500                                        *iptr = tolower(*iptr);
[62f51ee9]501                                g_string_append_printf(st,
502                                        "Gender: %s\n", sd->info_sex);
[6e14204]503                        }
504                        g_free(sd->info_sex);
[7c2daf5f]505                        sd->info_sex = NULL;
[6e14204]506                }
507                if (sd->info_language) {
508                        if (strlen(sd->info_language)) {
509                                char *iptr = strchr(sd->info_language, ' ');
510                                if (iptr)
511                                        iptr++;
512                                else
513                                        iptr = sd->info_language;
[62f51ee9]514                                g_string_append_printf(st,
515                                        "Language: %s\n", iptr);
[6e14204]516                        }
517                        g_free(sd->info_language);
[7c2daf5f]518                        sd->info_language = NULL;
[6e14204]519                }
520                if (sd->info_country) {
521                        if (strlen(sd->info_country)) {
522                                char *iptr = strchr(sd->info_country, ' ');
523                                if (iptr)
524                                        iptr++;
525                                else
526                                        iptr = sd->info_country;
[62f51ee9]527                                g_string_append_printf(st,
528                                        "Country: %s\n", iptr);
[6e14204]529                        }
530                        g_free(sd->info_country);
[7c2daf5f]531                        sd->info_country = NULL;
[6e14204]532                }
533                if (sd->info_province) {
534                        if (strlen(sd->info_province))
[62f51ee9]535                                g_string_append_printf(st,
536                                        "Region: %s\n", sd->info_province);
[6e14204]537                        g_free(sd->info_province);
[7c2daf5f]538                        sd->info_province = NULL;
[6e14204]539                }
540                if (sd->info_city) {
541                        if (strlen(sd->info_city))
[62f51ee9]542                                g_string_append_printf(st,
543                                        "City: %s\n", sd->info_city);
[6e14204]544                        g_free(sd->info_city);
[7c2daf5f]545                        sd->info_city = NULL;
[6e14204]546                }
547                if (sd->info_homepage) {
548                        if (strlen(sd->info_homepage))
[62f51ee9]549                                g_string_append_printf(st,
550                                        "Homepage: %s\n", sd->info_homepage);
[6e14204]551                        g_free(sd->info_homepage);
[7c2daf5f]552                        sd->info_homepage = NULL;
[6e14204]553                }
554                if (sd->info_about) {
555                        if (strlen(sd->info_about))
[62f51ee9]556                                g_string_append_printf(st, "%s\n",
557                                        sd->info_about);
[6e14204]558                        g_free(sd->info_about);
[7c2daf5f]559                        sd->info_about = NULL;
[6e14204]560                }
561                imcb_log(ic, "%s", st->str);
562                g_string_free(st, TRUE);
563        }
564}
565
[e1d6b38]566static void skype_parse_chatmessage_said_emoted(struct im_connection *ic, struct groupchat *gc, char *body)
[6e14204]567{
568        struct skype_data *sd = ic->proto_data;
[c213d6b]569        char buf[IRC_LINE_SIZE];
[e1d6b38]570        if (!strcmp(sd->type, "SAID")) {
571                if (!sd->is_edit)
572                        g_snprintf(buf, IRC_LINE_SIZE, "%s", body);
573                else {
574                        g_snprintf(buf, IRC_LINE_SIZE, "%s %s", set_getstr(&ic->acc->set, "edit_prefix"), body);
575                        sd->is_edit = 0;
576                }
577        } else
578                g_snprintf(buf, IRC_LINE_SIZE, "/me %s", body);
579        if (!gc)
580                /* Private message */
581                imcb_buddy_msg(ic, sd->handle, buf, 0, 0);
582        else
583                /* Groupchat message */
584                imcb_chat_msg(gc, sd->handle, buf, 0, 0);
585}
586
587static void skype_parse_chatmessage(struct im_connection *ic, char *line)
588{
589        struct skype_data *sd = ic->proto_data;
[6e14204]590        char *id = strchr(line, ' ');
591
[6b9d22a]592        if (!++id)
593                return;
594        char *info = strchr(id, ' ');
595
596        if (!info)
597                return;
598        *info = '\0';
599        info++;
[7825f58]600        if (!strcmp(info, "STATUS RECEIVED") || !strncmp(info, "EDITED_TIMESTAMP", 16)) {
[6b9d22a]601                /* New message ID:
602                 * (1) Request its from field
603                 * (2) Request its body
604                 * (3) Request its type
605                 * (4) Query chatname
606                 */
[1f4fc80]607                skype_printf(ic, "GET CHATMESSAGE %s FROM_HANDLE\n", id);
[d1d5b34]608                if (!strcmp(info, "STATUS RECEIVED"))
609                        skype_printf(ic, "GET CHATMESSAGE %s BODY\n", id);
[d9ce18c]610                else
611                        sd->is_edit = 1;
[1f4fc80]612                skype_printf(ic, "GET CHATMESSAGE %s TYPE\n", id);
613                skype_printf(ic, "GET CHATMESSAGE %s CHATNAME\n", id);
[6b9d22a]614        } else if (!strncmp(info, "FROM_HANDLE ", 12)) {
615                info += 12;
616                /* New from field value. Store
617                 * it, then we can later use it
618                 * when we got the message's
619                 * body. */
620                g_free(sd->handle);
621                sd->handle = g_strdup_printf("%s@skype.com", info);
622        } else if (!strncmp(info, "EDITED_BY ", 10)) {
623                info += 10;
624                /* This is the same as
625                 * FROM_HANDLE, except that we
626                 * never request these lines
627                 * from Skype, we just get
628                 * them. */
629                g_free(sd->handle);
630                sd->handle = g_strdup_printf("%s@skype.com", info);
631        } else if (!strncmp(info, "BODY ", 5)) {
632                info += 5;
633                sd->body = g_list_append(sd->body, g_strdup(info));
634        }       else if (!strncmp(info, "TYPE ", 5)) {
635                info += 5;
636                g_free(sd->type);
637                sd->type = g_strdup(info);
[7825f58]638        } else if (!strncmp(info, "CHATNAME ", 9)) {
639                info += 9;
640                if (sd->handle && sd->body && sd->type) {
[451f121]641                        struct groupchat *gc = bee_chat_by_title(ic->bee, ic, info);
[7825f58]642                        int i;
643                        for (i = 0; i < g_list_length(sd->body); i++) {
644                                char *body = g_list_nth_data(sd->body, i);
645                                if (!strcmp(sd->type, "SAID") ||
646                                        !strcmp(sd->type, "EMOTED")) {
[e1d6b38]647                                        skype_parse_chatmessage_said_emoted(ic, gc, body);
[7825f58]648                                } else if (!strcmp(sd->type, "SETTOPIC") && gc)
649                                        imcb_chat_topic(gc,
650                                                sd->handle, body, 0);
651                                else if (!strcmp(sd->type, "LEFT") && gc)
652                                        imcb_chat_remove_buddy(gc,
653                                                sd->handle, NULL);
[6e14204]654                        }
[7825f58]655                        g_list_free(sd->body);
656                        sd->body = NULL;
657                }
[6e14204]658        }
659}
660
[9f2f25f]661static void skype_parse_call(struct im_connection *ic, char *line)
662{
663        struct skype_data *sd = ic->proto_data;
664        char *id = strchr(line, ' ');
[c213d6b]665        char buf[IRC_LINE_SIZE];
[9f2f25f]666
[6b9d22a]667        if (!++id)
668                return;
669        char *info = strchr(id, ' ');
670
671        if (!info)
672                return;
673        *info = '\0';
674        info++;
675        if (!strncmp(info, "FAILUREREASON ", 14))
676                sd->failurereason = atoi(strchr(info, ' '));
677        else if (!strcmp(info, "STATUS RINGING")) {
678                if (sd->call_id)
679                        g_free(sd->call_id);
680                sd->call_id = g_strdup(id);
[1f4fc80]681                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
[6b9d22a]682                sd->call_status = SKYPE_CALL_RINGING;
683        } else if (!strcmp(info, "STATUS MISSED")) {
[1f4fc80]684                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
[6b9d22a]685                sd->call_status = SKYPE_CALL_MISSED;
686        } else if (!strcmp(info, "STATUS CANCELLED")) {
[1f4fc80]687                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
[6b9d22a]688                sd->call_status = SKYPE_CALL_CANCELLED;
689        } else if (!strcmp(info, "STATUS FINISHED")) {
[1f4fc80]690                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
[6b9d22a]691                sd->call_status = SKYPE_CALL_FINISHED;
692        } else if (!strcmp(info, "STATUS REFUSED")) {
[1f4fc80]693                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
[6b9d22a]694                sd->call_status = SKYPE_CALL_REFUSED;
695        } else if (!strcmp(info, "STATUS UNPLACED")) {
696                if (sd->call_id)
697                        g_free(sd->call_id);
698                /* Save the ID for later usage (Cancel/Finish). */
699                sd->call_id = g_strdup(id);
700                sd->call_out = TRUE;
701        } else if (!strcmp(info, "STATUS FAILED")) {
[62f51ee9]702                imcb_error(ic, "Call failed: %s",
703                        skype_call_strerror(sd->failurereason));
[6b9d22a]704                sd->call_id = NULL;
705        } else if (!strncmp(info, "DURATION ", 9)) {
706                if (sd->call_duration)
707                        g_free(sd->call_duration);
708                sd->call_duration = g_strdup(info+9);
709        } else if (!strncmp(info, "PARTNER_HANDLE ", 15)) {
710                info += 15;
[62f51ee9]711                if (!sd->call_status)
712                        return;
713                switch (sd->call_status) {
714                case SKYPE_CALL_RINGING:
715                        if (sd->call_out)
[e1d6b38]716                                imcb_log(ic, "You are currently ringing the user %s.", info);
[62f51ee9]717                        else {
[c213d6b]718                                g_snprintf(buf, IRC_LINE_SIZE,
[62f51ee9]719                                        "The user %s is currently ringing you.",
720                                        info);
721                                skype_call_ask(ic, sd->call_id, buf);
[9f2f25f]722                        }
[62f51ee9]723                        break;
724                case SKYPE_CALL_MISSED:
725                        imcb_log(ic, "You have missed a call from user %s.",
726                                info);
727                        break;
728                case SKYPE_CALL_CANCELLED:
729                        imcb_log(ic, "You cancelled the call to the user %s.",
730                                info);
[6b9d22a]731                        sd->call_status = 0;
[62f51ee9]732                        sd->call_out = FALSE;
733                        break;
734                case SKYPE_CALL_REFUSED:
735                        if (sd->call_out)
736                                imcb_log(ic, "The user %s refused the call.",
737                                        info);
738                        else
739                                imcb_log(ic,
740                                        "You refused the call from user %s.",
741                                        info);
742                        sd->call_out = FALSE;
743                        break;
744                case SKYPE_CALL_FINISHED:
745                        if (sd->call_duration)
746                                imcb_log(ic,
747                                        "You finished the call to the user %s "
748                                        "(duration: %s seconds).",
749                                        info, sd->call_duration);
750                        else
751                                imcb_log(ic,
752                                        "You finished the call to the user %s.",
753                                        info);
754                        sd->call_out = FALSE;
755                        break;
756                default:
757                        /* Don't be noisy, ignore other statuses for now. */
758                        break;
[9f2f25f]759                }
[62f51ee9]760                sd->call_status = 0;
[9f2f25f]761        }
762}
763
[e200daf]764static void skype_parse_filetransfer(struct im_connection *ic, char *line)
765{
766        struct skype_data *sd = ic->proto_data;
767        char *id = strchr(line, ' ');
768
[6b9d22a]769        if (!++id)
770                return;
771        char *info = strchr(id, ' ');
772
773        if (!info)
774                return;
775        *info = '\0';
776        info++;
777        if (!strcmp(info, "STATUS NEW")) {
[1f4fc80]778                skype_printf(ic, "GET FILETRANSFER %s PARTNER_HANDLE\n",
[62f51ee9]779                        id);
[6b9d22a]780                sd->filetransfer_status = SKYPE_FILETRANSFER_NEW;
781        } else if (!strcmp(info, "STATUS FAILED")) {
[1f4fc80]782                skype_printf(ic, "GET FILETRANSFER %s PARTNER_HANDLE\n",
[62f51ee9]783                        id);
[6b9d22a]784                sd->filetransfer_status = SKYPE_FILETRANSFER_FAILED;
785        } else if (!strncmp(info, "PARTNER_HANDLE ", 15)) {
786                info += 15;
[62f51ee9]787                if (!sd->filetransfer_status)
788                        return;
789                switch (sd->filetransfer_status) {
790                case SKYPE_FILETRANSFER_NEW:
791                        imcb_log(ic, "The user %s offered a new file for you.",
792                                info);
793                        break;
794                case SKYPE_FILETRANSFER_FAILED:
795                        imcb_log(ic, "Failed to transfer file from user %s.",
796                                info);
797                        break;
[e200daf]798                }
[62f51ee9]799                sd->filetransfer_status = 0;
[e200daf]800        }
801}
802
[fbb15f2]803static struct skype_group *skype_group_by_id(struct im_connection *ic, int id)
804{
[89d6845]805        struct skype_data *sd = ic->proto_data;
806        int i;
807
808        for (i = 0; i < g_list_length(sd->groups); i++) {
809                struct skype_group *sg = (struct skype_group *)g_list_nth_data(sd->groups, i);
810
811                if (sg->id == id)
812                        return sg;
813        }
814        return NULL;
815}
816
[fbb15f2]817static void skype_group_free(struct skype_group *sg, gboolean usersonly)
818{
[89d6845]819        int i;
[fbb15f2]820
[89d6845]821        for (i = 0; i < g_list_length(sg->users); i++) {
822                char *user = g_list_nth_data(sg->users, i);
823                g_free(user);
824        }
825        sg->users = NULL;
826        if (usersonly)
827                return;
828        g_free(sg->name);
829        g_free(sg);
830}
831
[54ca269]832/* Update the group of each user in this group */
[fbb15f2]833static void skype_group_users(struct im_connection *ic, struct skype_group *sg)
834{
[54ca269]835        int i;
836
837        for (i = 0; i < g_list_length(sg->users); i++) {
838                char *user = g_list_nth_data(sg->users, i);
839                char *buf = g_strdup_printf("%s@skype.com", user);
840                imcb_add_buddy(ic, buf, sg->name);
841                g_free(buf);
842        }
843}
844
[89d6845]845static void skype_parse_group(struct im_connection *ic, char *line)
846{
847        struct skype_data *sd = ic->proto_data;
848        char *id = strchr(line, ' ');
849
850        if (!++id)
851                return;
852
853        char *info = strchr(id, ' ');
854
855        if (!info)
856                return;
857        *info = '\0';
858        info++;
859
860        if (!strncmp(info, "DISPLAYNAME ", 12)) {
861                info += 12;
862
863                /* Name given for a group ID: try to update it or insert a new
864                 * one if not found */
865                struct skype_group *sg = skype_group_by_id(ic, atoi(id));
866                if (sg) {
867                        g_free(sg->name);
868                        sg->name = g_strdup(info);
869                } else {
870                        sg = g_new0(struct skype_group, 1);
871                        sg->id = atoi(id);
872                        sg->name = g_strdup(info);
873                        sd->groups = g_list_append(sd->groups, sg);
874                }
875        } else if (!strncmp(info, "USERS ", 6)) {
876                struct skype_group *sg = skype_group_by_id(ic, atoi(id));
877
878                if (sg) {
879                        char **i;
880                        char **users = g_strsplit(info + 6, ", ", 0);
881
882                        skype_group_free(sg, TRUE);
883                        i = users;
884                        while (*i) {
885                                sg->users = g_list_append(sg->users, g_strdup(*i));
886                                i++;
887                        }
888                        g_strfreev(users);
[54ca269]889                        skype_group_users(ic, sg);
[89d6845]890                } else
[fbb15f2]891                        log_message(LOGLVL_ERROR,
892                                "No skype group with id %s. That's probably a bug.", id);
[46641bf]893        } else if (!strncmp(info, "NROFUSERS ", 10)) {
894                if (!sd->pending_user) {
895                        /* Number of users changed in this group, query its type to see
896                         * if it's a custom one we should care about. */
897                        skype_printf(ic, "GET GROUP %s TYPE", id);
898                        return;
899                }
900
901                /* This is a newly created group, we have a single user
902                 * to add. */
903                struct skype_group *sg = skype_group_by_id(ic, atoi(id));
904
905                if (sg) {
906                        skype_printf(ic, "ALTER GROUP %d ADDUSER %s", sg->id, sd->pending_user);
907                        g_free(sd->pending_user);
908                        sd->pending_user = NULL;
909                } else
910                        log_message(LOGLVL_ERROR,
911                                        "No skype group with id %s. That's probably a bug.", id);
[2b183e4]912        } else if (!strcmp(info, "TYPE CUSTOM_GROUP"))
[cb6d3c9]913                /* This one is interesting, query its users. */
914                skype_printf(ic, "GET GROUP %s USERS", id);
[89d6845]915}
916
[c35bf7a]917static void skype_parse_chat(struct im_connection *ic, char *line)
918{
919        struct skype_data *sd = ic->proto_data;
[c213d6b]920        char buf[IRC_LINE_SIZE];
[c35bf7a]921        char *id = strchr(line, ' ');
922
[6b9d22a]923        if (!++id)
924                return;
925        struct groupchat *gc;
926        char *info = strchr(id, ' ');
[c35bf7a]927
[6b9d22a]928        if (!info)
929                return;
930        *info = '\0';
931        info++;
932        /* Remove fake chat if we created one in skype_chat_with() */
[451f121]933        gc = bee_chat_by_title(ic->bee, ic, "");
[6b9d22a]934        if (gc)
935                imcb_chat_free(gc);
936        if (!strcmp(info, "STATUS MULTI_SUBSCRIBED")) {
[c573f1b]937                gc = bee_chat_by_title(ic->bee, ic, id);
938                if (!gc) {
939                        gc = imcb_chat_new(ic, id);
940                        imcb_chat_name_hint(gc, id);
941                }
[1f4fc80]942                skype_printf(ic, "GET CHAT %s ADDER\n", id);
943                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
[6b9d22a]944        } else if (!strcmp(info, "STATUS DIALOG") && sd->groupchat_with) {
945                gc = imcb_chat_new(ic, id);
[72b60c7e]946                imcb_chat_name_hint(gc, id);
[6b9d22a]947                /* According to the docs this
948                 * is necessary. However it
949                 * does not seem the situation
950                 * and it would open an extra
951                 * window on our client, so
952                 * just leave it out. */
[1f4fc80]953                /*skype_printf(ic, "OPEN CHAT %s\n", id);*/
[5d9db76]954                g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
955                                sd->groupchat_with);
[6b9d22a]956                imcb_chat_add_buddy(gc, buf);
957                imcb_chat_add_buddy(gc, sd->username);
958                g_free(sd->groupchat_with);
959                sd->groupchat_with = NULL;
[1f4fc80]960                skype_printf(ic, "GET CHAT %s ADDER\n", id);
961                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
[6b9d22a]962        } else if (!strcmp(info, "STATUS UNSUBSCRIBED")) {
[451f121]963                gc = bee_chat_by_title(ic->bee, ic, id);
[c35bf7a]964                if (gc)
[6b9d22a]965                        gc->data = (void *)FALSE;
966        } else if (!strncmp(info, "ADDER ", 6)) {
967                info += 6;
968                g_free(sd->adder);
969                sd->adder = g_strdup_printf("%s@skype.com", info);
970        } else if (!strncmp(info, "TOPIC ", 6)) {
971                info += 6;
[451f121]972                gc = bee_chat_by_title(ic->bee, ic, id);
[6b9d22a]973                if (gc && (sd->adder || sd->topic_wait)) {
974                        if (sd->topic_wait) {
975                                sd->adder = g_strdup(sd->username);
976                                sd->topic_wait = 0;
[c35bf7a]977                        }
[6b9d22a]978                        imcb_chat_topic(gc, sd->adder, info, 0);
979                        g_free(sd->adder);
980                        sd->adder = NULL;
981                }
[2487112]982        } else if (!strncmp(info, "MEMBERS ", 8)) {
983                info += 8;
[451f121]984                gc = bee_chat_by_title(ic->bee, ic, id);
[6b9d22a]985                /* Hack! We set ->data to TRUE
986                 * while we're on the channel
987                 * so that we won't rejoin
988                 * after a /part. */
[62f51ee9]989                if (!gc || gc->data)
990                        return;
991                char **members = g_strsplit(info, " ", 0);
992                int i;
993                for (i = 0; members[i]; i++) {
994                        if (!strcmp(members[i], sd->username))
995                                continue;
[5d9db76]996                        g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
997                                        members[i]);
[62f51ee9]998                        if (!g_list_find_custom(gc->in_room, buf,
999                                (GCompareFunc)strcmp))
1000                                imcb_chat_add_buddy(gc, buf);
[c35bf7a]1001                }
[62f51ee9]1002                imcb_chat_add_buddy(gc, sd->username);
1003                g_strfreev(members);
[c35bf7a]1004        }
1005}
1006
[2709f4c]1007static void skype_parse_password(struct im_connection *ic, char *line)
1008{
1009        if (!strncmp(line+9, "OK", 2))
1010                imcb_connected(ic);
1011        else {
1012                imcb_error(ic, "Authentication Failed");
1013                imc_logout(ic, TRUE);
1014        }
1015}
1016
[607f5e3]1017static void skype_parse_profile(struct im_connection *ic, char *line)
1018{
1019        imcb_log(ic, "SkypeOut balance value is '%s'.", line+21);
1020}
1021
1022static void skype_parse_ping(struct im_connection *ic, char *line)
1023{
[4ae3ffc]1024        /* Unused parameter */
1025        line = line;
[1f4fc80]1026        skype_printf(ic, "PONG\n");
[607f5e3]1027}
1028
1029static void skype_parse_chats(struct im_connection *ic, char *line)
1030{
1031        char **i;
1032        char **chats = g_strsplit(line + 6, ", ", 0);
1033
1034        i = chats;
1035        while (*i) {
[1f4fc80]1036                skype_printf(ic, "GET CHAT %s STATUS\n", *i);
1037                skype_printf(ic, "GET CHAT %s ACTIVEMEMBERS\n", *i);
[607f5e3]1038                i++;
1039        }
1040        g_strfreev(chats);
1041}
1042
[8b8d1bed]1043static void skype_parse_groups(struct im_connection *ic, char *line)
1044{
[3c7af69]1045        if (!set_getbool(&ic->acc->set, "read_groups"))
1046                return;
1047
[8b8d1bed]1048        char **i;
1049        char **groups = g_strsplit(line + 7, ", ", 0);
1050
1051        i = groups;
1052        while (*i) {
1053                skype_printf(ic, "GET GROUP %s DISPLAYNAME\n", *i);
1054                skype_printf(ic, "GET GROUP %s USERS\n", *i);
1055                i++;
1056        }
1057        g_strfreev(groups);
1058}
1059
[46e9822]1060static void skype_parse_alter_group(struct im_connection *ic, char *line)
1061{
1062        char *id = line + strlen("ALTER GROUP");
1063
1064        if (!++id)
1065                return;
1066
1067        char *info = strchr(id, ' ');
1068
1069        if (!info)
1070                return;
1071        *info = '\0';
1072        info++;
1073
1074        if (!strncmp(info, "ADDUSER ", 8)) {
1075                struct skype_group *sg = skype_group_by_id(ic, atoi(id));
1076
1077                info += 8;
1078                if (sg) {
1079                        char *buf = g_strdup_printf("%s@skype.com", info);
1080                        sg->users = g_list_append(sg->users, g_strdup(info));
1081                        imcb_add_buddy(ic, buf, sg->name);
1082                        g_free(buf);
1083                } else
1084                        log_message(LOGLVL_ERROR,
1085                                "No skype group with id %s. That's probably a bug.", id);
1086        }
1087}
1088
[ff436ba]1089typedef void (*skype_parser)(struct im_connection *ic, char *line);
1090
[8c09bb3]1091static gboolean skype_read_callback(gpointer data, gint fd,
1092                                    b_input_condition cond)
[1323e36]1093{
1094        struct im_connection *ic = data;
1095        struct skype_data *sd = ic->proto_data;
[c213d6b]1096        char buf[IRC_LINE_SIZE];
[ff436ba]1097        int st, i;
[6e14204]1098        char **lines, **lineptr, *line;
[ff436ba]1099        static struct parse_map {
1100                char *k;
1101                skype_parser v;
1102        } parsers[] = {
1103                { "USERS ", skype_parse_users },
1104                { "USER ", skype_parse_user },
1105                { "CHATMESSAGE ", skype_parse_chatmessage },
1106                { "CALL ", skype_parse_call },
1107                { "FILETRANSFER ", skype_parse_filetransfer },
1108                { "CHAT ", skype_parse_chat },
[89d6845]1109                { "GROUP ", skype_parse_group },
[ff436ba]1110                { "PASSWORD ", skype_parse_password },
1111                { "PROFILE PSTN_BALANCE ", skype_parse_profile },
1112                { "PING", skype_parse_ping },
1113                { "CHATS ", skype_parse_chats },
[8b8d1bed]1114                { "GROUPS ", skype_parse_groups },
[46e9822]1115                { "ALTER GROUP ", skype_parse_alter_group },
[ff436ba]1116        };
[1323e36]1117
[4ae3ffc]1118        /* Unused parameters */
1119        fd = fd;
1120        cond = cond;
1121
[5adcc65]1122        if (!sd || sd->fd == -1)
[1323e36]1123                return FALSE;
[7daec06]1124        /* Read the whole data. */
[5adcc65]1125        st = ssl_read(sd->ssl, buf, sizeof(buf));
1126        if (st > 0) {
[1323e36]1127                buf[st] = '\0';
[7daec06]1128                /* Then split it up to lines. */
[9fd4241]1129                lines = g_strsplit(buf, "\n", 0);
1130                lineptr = lines;
[5adcc65]1131                while ((line = *lineptr)) {
1132                        if (!strlen(line))
[9fd4241]1133                                break;
[b820226]1134                        if (set_getbool(&ic->acc->set, "skypeconsole_receive"))
1135                                imcb_buddy_msg(ic, "skypeconsole", line, 0, 0);
[6b9d22a]1136                        for (i = 0; i < ARRAY_SIZE(parsers); i++)
1137                                if (!strncmp(line, parsers[i].k,
1138                                        strlen(parsers[i].k))) {
[ff436ba]1139                                        parsers[i].v(ic, line);
1140                                        break;
1141                                }
[9fd4241]1142                        lineptr++;
1143                }
1144                g_strfreev(lines);
[5adcc65]1145        } else if (st == 0 || (st < 0 && !sockerr_again())) {
[58b65b33]1146                ssl_disconnect(sd->ssl);
[1323e36]1147                sd->fd = -1;
[58b65b33]1148                sd->ssl = NULL;
[1323e36]1149
[5adcc65]1150                imcb_error(ic, "Error while reading from server");
1151                imc_logout(ic, TRUE);
[1323e36]1152                return FALSE;
1153        }
1154        return TRUE;
1155}
1156
[5adcc65]1157gboolean skype_start_stream(struct im_connection *ic)
[f06e3ac]1158{
[1323e36]1159        struct skype_data *sd = ic->proto_data;
[f06e3ac]1160        int st;
1161
[5adcc65]1162        if (!sd)
[1fb89e3]1163                return FALSE;
1164
[5adcc65]1165        if (sd->bfd <= 0)
[7670b02]1166                sd->bfd = b_input_add(sd->fd, B_EV_IO_READ,
[8c09bb3]1167                        skype_read_callback, ic);
[1323e36]1168
[a0b206b]1169        /* Log in */
[1f4fc80]1170        skype_printf(ic, "USERNAME %s\n", ic->acc->user);
1171        skype_printf(ic, "PASSWORD %s\n", ic->acc->pass);
[a0b206b]1172
[8b8d1bed]1173        /* This will download all buddies and groups. */
[54ca269]1174        st = skype_printf(ic, "SEARCH GROUPS CUSTOM\n");
1175        skype_printf(ic, "SEARCH FRIENDS\n");
[8b8d1bed]1176
[1f4fc80]1177        skype_printf(ic, "SET USERSTATUS ONLINE\n");
[5acf9ab]1178
1179        /* Auto join to bookmarked chats if requested.*/
[1f4fc80]1180        if (set_getbool(&ic->acc->set, "auto_join"))
1181                skype_printf(ic, "SEARCH BOOKMARKEDCHATS\n");
[f06e3ac]1182        return st;
1183}
1184
[486ddb5]1185gboolean skype_connected(gpointer data, int returncode, void *source, b_input_condition cond)
[f06e3ac]1186{
1187        struct im_connection *ic = data;
[c7304b2]1188        struct skype_data *sd = ic->proto_data;
[4ae3ffc]1189
1190        /* Unused parameter */
1191        cond = cond;
1192
[5adcc65]1193        if (!source) {
[c7304b2]1194                sd->ssl = NULL;
[5adcc65]1195                imcb_error(ic, "Could not connect to server");
1196                imc_logout(ic, TRUE);
[c7304b2]1197                return FALSE;
1198        }
[5adcc65]1199        imcb_log(ic, "Connected to server, logging in");
[4ae3ffc]1200
[f06e3ac]1201        return skype_start_stream(ic);
1202}
1203
[5adcc65]1204static void skype_login(account_t *acc)
[f06e3ac]1205{
[5adcc65]1206        struct im_connection *ic = imcb_new(acc);
1207        struct skype_data *sd = g_new0(struct skype_data, 1);
[f06e3ac]1208
1209        ic->proto_data = sd;
1210
[5adcc65]1211        imcb_log(ic, "Connecting");
[8c09bb3]1212        sd->ssl = ssl_connect(set_getstr(&acc->set, "server"),
[a72dc2b]1213                set_getint(&acc->set, "port"), FALSE, skype_connected, ic);
[5adcc65]1214        sd->fd = sd->ssl ? ssl_getfd(sd->ssl) : -1;
1215        sd->username = g_strdup(acc->user);
[f06e3ac]1216
1217        sd->ic = ic;
[08a355b]1218
1219        if (set_getbool(&acc->set, "skypeconsole"))
1220                imcb_add_buddy(ic, "skypeconsole", NULL);
[f06e3ac]1221}
1222
[5adcc65]1223static void skype_logout(struct im_connection *ic)
[f06e3ac]1224{
1225        struct skype_data *sd = ic->proto_data;
[89d6845]1226        int i;
[98bca36]1227
[1f4fc80]1228        skype_printf(ic, "SET USERSTATUS OFFLINE\n");
[98bca36]1229
[e1d6b38]1230        while (ic->groupchats)
[bd11422]1231                imcb_chat_free(ic->groupchats->data);
1232
[89d6845]1233        for (i = 0; i < g_list_length(sd->groups); i++) {
1234                struct skype_group *sg = (struct skype_group *)g_list_nth_data(sd->groups, i);
1235                skype_group_free(sg, FALSE);
1236        }
[58b65b33]1237
1238        if (sd->ssl)
1239                ssl_disconnect(sd->ssl);
1240
[a3d6427]1241        g_free(sd->username);
[7613670]1242        g_free(sd->handle);
[f06e3ac]1243        g_free(sd);
[98bca36]1244        ic->proto_data = NULL;
[f06e3ac]1245}
1246
[8c09bb3]1247static int skype_buddy_msg(struct im_connection *ic, char *who, char *message,
1248                           int flags)
[93ece66]1249{
[1f4fc80]1250        char *ptr, *nick;
[93ece66]1251        int st;
1252
[4ae3ffc]1253        /* Unused parameter */
1254        flags = flags;
1255
[cbec0d6]1256        nick = g_strdup(who);
[77c1abe]1257        ptr = strchr(nick, '@');
[5adcc65]1258        if (ptr)
[0bb1b7f]1259                *ptr = '\0';
[93ece66]1260
[08a355b]1261        if (!strncmp(who, "skypeconsole", 12))
[1f4fc80]1262                st = skype_printf(ic, "%s\n", message);
[08a355b]1263        else
[1f4fc80]1264                st = skype_printf(ic, "MESSAGE %s %s\n", nick, message);
[77c1abe]1265        g_free(nick);
[93ece66]1266
1267        return st;
1268}
1269
[5adcc65]1270const struct skype_away_state *skype_away_state_by_name(char *name)
[23411c6]1271{
1272        int i;
1273
[5adcc65]1274        for (i = 0; skype_away_state_list[i].full_name; i++)
1275                if (g_strcasecmp(skype_away_state_list[i].full_name, name) == 0)
1276                        return skype_away_state_list + i;
[23411c6]1277
1278        return NULL;
1279}
1280
[8c09bb3]1281static void skype_set_away(struct im_connection *ic, char *state_txt,
1282                           char *message)
[f06e3ac]1283{
[23411c6]1284        const struct skype_away_state *state;
1285
[4ae3ffc]1286        /* Unused parameter */
1287        message = message;
1288
[4b740c2]1289        if (state_txt == NULL)
1290                state = skype_away_state_by_name("Online");
[23411c6]1291        else
[5adcc65]1292                state = skype_away_state_by_name(state_txt);
[1f4fc80]1293        skype_printf(ic, "SET USERSTATUS %s\n", state->code);
[f06e3ac]1294}
1295
[5adcc65]1296static GList *skype_away_states(struct im_connection *ic)
[f06e3ac]1297{
[5adcc65]1298        static GList *l;
[adce2de]1299        int i;
[5adcc65]1300
[4ae3ffc]1301        /* Unused parameter */
1302        ic = ic;
1303
[5adcc65]1304        if (l == NULL)
1305                for (i = 0; skype_away_state_list[i].full_name; i++)
[8c09bb3]1306                        l = g_list_append(l,
1307                                (void *)skype_away_state_list[i].full_name);
[5adcc65]1308
[f06e3ac]1309        return l;
1310}
1311
[5adcc65]1312static char *skype_set_display_name(set_t *set, char *value)
[93dffea]1313{
1314        account_t *acc = set->data;
1315        struct im_connection *ic = acc->ic;
1316
[1f4fc80]1317        skype_printf(ic, "SET PROFILE FULLNAME %s", value);
[5adcc65]1318        return value;
[93dffea]1319}
1320
[7764fb1]1321static char *skype_set_mood_text(set_t *set, char *value)
1322{
1323        account_t *acc = set->data;
1324        struct im_connection *ic = acc->ic;
1325
1326        skype_printf(ic, "SET PROFILE MOOD_TEXT %s", value);
1327        return value;
1328}
1329
[5adcc65]1330static char *skype_set_balance(set_t *set, char *value)
[2af671a]1331{
1332        account_t *acc = set->data;
1333        struct im_connection *ic = acc->ic;
1334
[1f4fc80]1335        skype_printf(ic, "GET PROFILE PSTN_BALANCE");
[5adcc65]1336        return value;
[2af671a]1337}
1338
[fbb15f2]1339static void skype_call(struct im_connection *ic, char *value)
1340{
[71c4bb6]1341        char *nick = g_strdup(value);
1342        char *ptr = strchr(nick, '@');
1343
1344        if (ptr)
1345                *ptr = '\0';
1346        skype_printf(ic, "CALL %s", nick);
1347        g_free(nick);
1348}
1349
1350static void skype_hangup(struct im_connection *ic)
1351{
1352        struct skype_data *sd = ic->proto_data;
1353
1354        if (sd->call_id) {
1355                skype_printf(ic, "SET CALL %s STATUS FINISHED",
1356                                sd->call_id);
1357                g_free(sd->call_id);
1358                sd->call_id = 0;
1359        } else
1360                imcb_error(ic, "There are no active calls currently.");
1361}
1362
[5adcc65]1363static char *skype_set_call(set_t *set, char *value)
[b68b023]1364{
1365        account_t *acc = set->data;
1366        struct im_connection *ic = acc->ic;
1367
[fbb15f2]1368        if (value)
[451f121]1369                skype_call(ic, value);
[fbb15f2]1370        else
[71c4bb6]1371                skype_hangup(ic);
[5adcc65]1372        return value;
[b68b023]1373}
1374
[5adcc65]1375static void skype_add_buddy(struct im_connection *ic, char *who, char *group)
[f06e3ac]1376{
[46641bf]1377        struct skype_data *sd = ic->proto_data;
[1f4fc80]1378        char *nick, *ptr;
[6627d92]1379
[cbec0d6]1380        nick = g_strdup(who);
[6627d92]1381        ptr = strchr(nick, '@');
[5adcc65]1382        if (ptr)
[6627d92]1383                *ptr = '\0';
[46e9822]1384
1385        if (!group) {
1386                skype_printf(ic, "SET USER %s BUDDYSTATUS 2 Please authorize me\n",
1387                                nick);
1388                g_free(nick);
1389        } else {
1390                struct skype_group *sg = skype_group_by_name(ic, group);
1391
1392                if (!sg) {
1393                        /* No such group, we need to create it, then have to
1394                         * add the user once it's created. */
[46641bf]1395                        skype_printf(ic, "CREATE GROUP %s", group);
1396                        sd->pending_user = g_strdup(nick);
[46e9822]1397                } else {
1398                        skype_printf(ic, "ALTER GROUP %d ADDUSER %s", sg->id, nick);
1399                }
1400        }
[f06e3ac]1401}
1402
[5adcc65]1403static void skype_remove_buddy(struct im_connection *ic, char *who, char *group)
[f06e3ac]1404{
[1f4fc80]1405        char *nick, *ptr;
[6627d92]1406
[4ae3ffc]1407        /* Unused parameter */
1408        group = group;
1409
[cbec0d6]1410        nick = g_strdup(who);
[6627d92]1411        ptr = strchr(nick, '@');
[5adcc65]1412        if (ptr)
[6627d92]1413                *ptr = '\0';
[1f4fc80]1414        skype_printf(ic, "SET USER %s BUDDYSTATUS 1\n", nick);
[6627d92]1415        g_free(nick);
[f06e3ac]1416}
1417
[5adcc65]1418void skype_chat_msg(struct groupchat *gc, char *message, int flags)
[66c9558]1419{
[79e20f9]1420        struct im_connection *ic = gc->ic;
[4ae3ffc]1421
1422        /* Unused parameter */
1423        flags = flags;
1424
[1f4fc80]1425        skype_printf(ic, "CHATMESSAGE %s %s\n", gc->title, message);
[66c9558]1426}
1427
[5adcc65]1428void skype_chat_leave(struct groupchat *gc)
[b01dc6c]1429{
1430        struct im_connection *ic = gc->ic;
[1f4fc80]1431        skype_printf(ic, "ALTER CHAT %s LEAVE\n", gc->title);
[5adcc65]1432        gc->data = (void *)TRUE;
[760319d]1433}
1434
1435void skype_chat_invite(struct groupchat *gc, char *who, char *message)
1436{
1437        struct im_connection *ic = gc->ic;
[1f4fc80]1438        char *ptr, *nick;
[4ae3ffc]1439
[17dd2ed]1440        nick = g_strdup(who);
[760319d]1441        ptr = strchr(nick, '@');
[5adcc65]1442        if (ptr)
[760319d]1443                *ptr = '\0';
[1f4fc80]1444        skype_printf(ic, "ALTER CHAT %s ADDMEMBERS %s\n", gc->title, nick);
[760319d]1445        g_free(nick);
[b01dc6c]1446}
1447
[09e2a69]1448void skype_chat_topic(struct groupchat *gc, char *message)
1449{
1450        struct im_connection *ic = gc->ic;
[a5f76a2]1451        struct skype_data *sd = ic->proto_data;
[1f4fc80]1452        skype_printf(ic, "ALTER CHAT %s SETTOPIC %s\n",
[8c09bb3]1453                gc->title, message);
[a5f76a2]1454        sd->topic_wait = 1;
[09e2a69]1455}
1456
[86278cd]1457struct groupchat *skype_chat_with(struct im_connection *ic, char *who)
1458{
1459        struct skype_data *sd = ic->proto_data;
[1f4fc80]1460        char *ptr, *nick;
[86278cd]1461        nick = g_strdup(who);
1462        ptr = strchr(nick, '@');
[5adcc65]1463        if (ptr)
[86278cd]1464                *ptr = '\0';
[1f4fc80]1465        skype_printf(ic, "CHAT CREATE %s\n", nick);
[86278cd]1466        sd->groupchat_with = g_strdup(nick);
1467        g_free(nick);
[5652d43]1468        /* We create a fake chat for now. We will replace it with a real one in
1469         * the real callback. */
[5adcc65]1470        return imcb_chat_new(ic, "");
[86278cd]1471}
1472
[67454bd]1473static void skype_get_info(struct im_connection *ic, char *who)
1474{
[1f4fc80]1475        char *ptr, *nick;
[67454bd]1476        nick = g_strdup(who);
1477        ptr = strchr(nick, '@');
[5adcc65]1478        if (ptr)
[67454bd]1479                *ptr = '\0';
[1f4fc80]1480        skype_printf(ic, "GET USER %s FULLNAME\n", nick);
1481        skype_printf(ic, "GET USER %s PHONE_HOME\n", nick);
1482        skype_printf(ic, "GET USER %s PHONE_OFFICE\n", nick);
1483        skype_printf(ic, "GET USER %s PHONE_MOBILE\n", nick);
1484        skype_printf(ic, "GET USER %s NROF_AUTHED_BUDDIES\n", nick);
1485        skype_printf(ic, "GET USER %s TIMEZONE\n", nick);
1486        skype_printf(ic, "GET USER %s LASTONLINETIMESTAMP\n", nick);
1487        skype_printf(ic, "GET USER %s SEX\n", nick);
1488        skype_printf(ic, "GET USER %s LANGUAGE\n", nick);
1489        skype_printf(ic, "GET USER %s COUNTRY\n", nick);
1490        skype_printf(ic, "GET USER %s PROVINCE\n", nick);
1491        skype_printf(ic, "GET USER %s CITY\n", nick);
1492        skype_printf(ic, "GET USER %s HOMEPAGE\n", nick);
1493        skype_printf(ic, "GET USER %s ABOUT\n", nick);
[85341dd]1494        /*
1495         * Hack: we query the bithday property which is always a single line,
1496         * so we can send the collected properties to the user when we have
1497         * this one.
1498         */
1499        skype_printf(ic, "GET USER %s BIRTHDAY\n", nick);
[67454bd]1500}
1501
[5adcc65]1502static void skype_set_my_name(struct im_connection *ic, char *info)
[93dffea]1503{
[5adcc65]1504        skype_set_display_name(set_find(&ic->acc->set, "display_name"), info);
[93dffea]1505}
1506
[5adcc65]1507static void skype_init(account_t *acc)
[93dffea]1508{
1509        set_t *s;
1510
[8c09bb3]1511        s = set_add(&acc->set, "server", SKYPE_DEFAULT_SERVER, set_eval_account,
1512                acc);
[93dffea]1513        s->flags |= ACC_SET_OFFLINE_ONLY;
1514
[5adcc65]1515        s = set_add(&acc->set, "port", SKYPE_DEFAULT_PORT, set_eval_int, acc);
[93dffea]1516        s->flags |= ACC_SET_OFFLINE_ONLY;
1517
[8c09bb3]1518        s = set_add(&acc->set, "display_name", NULL, skype_set_display_name,
1519                acc);
[93dffea]1520        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
[b68b023]1521
[7764fb1]1522        s = set_add(&acc->set, "mood_text", NULL, skype_set_mood_text, acc);
1523        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1524
[5adcc65]1525        s = set_add(&acc->set, "call", NULL, skype_set_call, acc);
[b68b023]1526        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
[2af671a]1527
[5adcc65]1528        s = set_add(&acc->set, "balance", NULL, skype_set_balance, acc);
[2af671a]1529        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
[bd417a1]1530
[5adcc65]1531        s = set_add(&acc->set, "skypeout_offline", "true", set_eval_bool, acc);
[08a355b]1532
[5adcc65]1533        s = set_add(&acc->set, "skypeconsole", "false", set_eval_bool, acc);
[08a355b]1534        s->flags |= ACC_SET_OFFLINE_ONLY;
[5acf9ab]1535
[8c09bb3]1536        s = set_add(&acc->set, "skypeconsole_receive", "false", set_eval_bool,
1537                acc);
[b820226]1538        s->flags |= ACC_SET_OFFLINE_ONLY;
1539
[5adcc65]1540        s = set_add(&acc->set, "auto_join", "false", set_eval_bool, acc);
[5acf9ab]1541        s->flags |= ACC_SET_OFFLINE_ONLY;
[f4d37c6]1542
[49a3c02]1543        s = set_add(&acc->set, "test_join", "false", set_eval_bool, acc);
1544        s->flags |= ACC_SET_OFFLINE_ONLY;
1545
[304aa33]1546        s = set_add(&acc->set, "show_moods", "false", set_eval_bool, acc);
1547
[f4d37c6]1548        s = set_add(&acc->set, "edit_prefix", "EDIT:",
[1e3120f]1549                        NULL, acc);
[3c7af69]1550
1551        s = set_add(&acc->set, "read_groups", "false", set_eval_bool, acc);
[93dffea]1552}
1553
[c6e0218]1554#if BITLBEE_VERSION_CODE > BITLBEE_VER(3, 0, 1)
[fbb15f2]1555GList *skype_buddy_action_list(bee_user_t *bu)
[71c4bb6]1556{
[fbb15f2]1557        static GList *ret;
[71c4bb6]1558
[a5e6aa1]1559        /* Unused parameter */
1560        bu = bu;
1561
[fbb15f2]1562        if (ret == NULL) {
[71c4bb6]1563                static const struct buddy_action ba[3] = {
1564                        {"CALL", "Initiate a call" },
1565                        {"HANGUP", "Hang up a call" },
1566                };
1567
[fbb15f2]1568                ret = g_list_prepend(ret, (void *) ba + 0);
[71c4bb6]1569        }
1570
1571        return ret;
1572}
1573
[fbb15f2]1574void *skype_buddy_action(struct bee_user *bu, const char *action, char * const args[], void *data)
[71c4bb6]1575{
[a5e6aa1]1576        /* Unused parameters */
1577        args = args;
1578        data = data;
1579
[fbb15f2]1580        if (!g_strcasecmp(action, "CALL"))
[71c4bb6]1581                skype_call(bu->ic, bu->handle);
[fbb15f2]1582        else if (!g_strcasecmp(action, "HANGUP"))
[71c4bb6]1583                skype_hangup(bu->ic);
1584
1585        return NULL;
1586}
1587#endif
1588
[f06e3ac]1589void init_plugin(void)
1590{
[5adcc65]1591        struct prpl *ret = g_new0(struct prpl, 1);
[f06e3ac]1592
1593        ret->name = "skype";
1594        ret->login = skype_login;
1595        ret->init = skype_init;
1596        ret->logout = skype_logout;
[93ece66]1597        ret->buddy_msg = skype_buddy_msg;
[67454bd]1598        ret->get_info = skype_get_info;
[93dffea]1599        ret->set_my_name = skype_set_my_name;
[f06e3ac]1600        ret->away_states = skype_away_states;
[7daec06]1601        ret->set_away = skype_set_away;
[f06e3ac]1602        ret->add_buddy = skype_add_buddy;
1603        ret->remove_buddy = skype_remove_buddy;
[66c9558]1604        ret->chat_msg = skype_chat_msg;
[b01dc6c]1605        ret->chat_leave = skype_chat_leave;
[760319d]1606        ret->chat_invite = skype_chat_invite;
[86278cd]1607        ret->chat_with = skype_chat_with;
[f06e3ac]1608        ret->handle_cmp = g_strcasecmp;
[09e2a69]1609        ret->chat_topic = skype_chat_topic;
[c6e0218]1610#if BITLBEE_VERSION_CODE > BITLBEE_VER(3, 0, 1)
[71c4bb6]1611        ret->buddy_action_list = skype_buddy_action_list;
1612        ret->buddy_action = skype_buddy_action;
1613#endif
[5adcc65]1614        register_protocol(ret);
[f06e3ac]1615}
Note: See TracBrowser for help on using the repository browser.