source: skype/skype.c @ 89d6845

Last change on this file since 89d6845 was 89d6845, checked in by Miklos Vajna <vmiklos@…>, at 2011-01-02T02:32:38Z

Parse the user list of each group

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