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
Line 
1/*
2 *  skype.c - Skype plugin for BitlBee
3 *
4 *  Copyright (c) 2007, 2008, 2009, 2010, 2011 by Miklos Vajna <vmiklos@frugalware.org>
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 *  USA.
20 */
21
22#define _XOPEN_SOURCE
23#define _BSD_SOURCE
24#include <poll.h>
25#include <bitlbee.h>
26#include <ssl_client.h>
27
28#define SKYPE_DEFAULT_SERVER "localhost"
29#define SKYPE_DEFAULT_PORT "2727"
30#define IRC_LINE_SIZE 1024
31#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
32
33/*
34 * Enumerations
35 */
36
37enum {
38        SKYPE_CALL_RINGING = 1,
39        SKYPE_CALL_MISSED,
40        SKYPE_CALL_CANCELLED,
41        SKYPE_CALL_FINISHED,
42        SKYPE_CALL_REFUSED
43};
44
45enum {
46        SKYPE_FILETRANSFER_NEW = 1,
47        SKYPE_FILETRANSFER_FAILED
48};
49
50/*
51 * Structures
52 */
53
54struct skype_data {
55        struct im_connection *ic;
56        char *username;
57        /* The effective file descriptor. We store it here so any function can
58         * write() to it. */
59        int fd;
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;
63        /* ssl_getfd() uses this to get the file desciptor. */
64        void *ssl;
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
67         * imcb_buddy_msg() when we got the chatname. */
68        char *handle;
69        /* List, because of multiline messages. */
70        GList *body;
71        char *type;
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. */
75        int call_status;
76        char *call_id;
77        char *call_duration;
78        /* If the call is outgoing or not */
79        int call_out;
80        /* Same for file transfers. */
81        int filetransfer_status;
82        /* Using /j #nick we want to have a groupchat with two people. Usually
83         * not (default). */
84        char *groupchat_with;
85        /* The user who invited us to the chat. */
86        char *adder;
87        /* If we are waiting for a confirmation about we changed the topic. */
88        int topic_wait;
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;
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;
108        /* If this is just an update of an already received message. */
109        int is_edit;
110        /* List of struct skype_group* */
111        GList *groups;
112};
113
114struct skype_away_state {
115        char *code;
116        char *full_name;
117};
118
119struct skype_buddy_ask_data {
120        struct im_connection *ic;
121        /* This is also used for call IDs for simplicity */
122        char *handle;
123};
124
125struct skype_group {
126        int id;
127        char *name;
128        GList *users;
129};
130
131/*
132 * Tables
133 */
134
135const struct skype_away_state skype_away_state_list[] = {
136        { "AWAY", "Away" },
137        { "NA", "Not available" },
138        { "DND", "Do Not Disturb" },
139        { "INVISIBLE", "Invisible" },
140        { "OFFLINE", "Offline" },
141        { "SKYPEME", "Skype Me" },
142        { "ONLINE", "Online" },
143        { NULL, NULL}
144};
145
146/*
147 * Functions
148 */
149
150int skype_write(struct im_connection *ic, char *buf, int len)
151{
152        struct skype_data *sd = ic->proto_data;
153        struct pollfd pfd[1];
154
155        if (!sd->ssl)
156                return FALSE;
157
158        pfd[0].fd = sd->fd;
159        pfd[0].events = POLLOUT;
160
161        /* This poll is necessary or we'll get a SIGPIPE when we write() to
162         * sd->fd. */
163        poll(pfd, 1, 1000);
164        if (pfd[0].revents & POLLHUP) {
165                imc_logout(ic, TRUE);
166                return FALSE;
167        }
168        ssl_write(sd->ssl, buf, len);
169
170        return TRUE;
171}
172
173int skype_printf(struct im_connection *ic, char *fmt, ...)
174{
175        va_list args;
176        char str[IRC_LINE_SIZE];
177
178        va_start(args, fmt);
179        vsnprintf(str, IRC_LINE_SIZE, fmt, args);
180        va_end(args);
181
182        return skype_write(ic, str, strlen(str));
183}
184
185static void skype_buddy_ask_yes(void *data)
186{
187        struct skype_buddy_ask_data *bla = data;
188        skype_printf(bla->ic, "SET USER %s ISAUTHORIZED TRUE",
189                bla->handle);
190        g_free(bla->handle);
191        g_free(bla);
192}
193
194static void skype_buddy_ask_no(void *data)
195{
196        struct skype_buddy_ask_data *bla = data;
197        skype_printf(bla->ic, "SET USER %s ISAUTHORIZED FALSE",
198                bla->handle);
199        g_free(bla->handle);
200        g_free(bla);
201}
202
203void skype_buddy_ask(struct im_connection *ic, char *handle, char *message)
204{
205        struct skype_buddy_ask_data *bla = g_new0(struct skype_buddy_ask_data,
206                1);
207        char *buf;
208
209        bla->ic = ic;
210        bla->handle = g_strdup(handle);
211
212        buf = g_strdup_printf("The user %s wants to add you to "
213                "his/her buddy list, saying: '%s'.", handle, message);
214        imcb_ask(ic, buf, bla, skype_buddy_ask_yes, skype_buddy_ask_no);
215        g_free(buf);
216}
217
218static void skype_call_ask_yes(void *data)
219{
220        struct skype_buddy_ask_data *bla = data;
221        skype_printf(bla->ic, "SET CALL %s STATUS INPROGRESS",
222                bla->handle);
223        g_free(bla->handle);
224        g_free(bla);
225}
226
227static void skype_call_ask_no(void *data)
228{
229        struct skype_buddy_ask_data *bla = data;
230        skype_printf(bla->ic, "SET CALL %s STATUS FINISHED",
231                bla->handle);
232        g_free(bla->handle);
233        g_free(bla);
234}
235
236void skype_call_ask(struct im_connection *ic, char *call_id, char *message)
237{
238        struct skype_buddy_ask_data *bla = g_new0(struct skype_buddy_ask_data,
239                1);
240
241        bla->ic = ic;
242        bla->handle = g_strdup(call_id);
243
244        imcb_ask(ic, message, bla, skype_call_ask_yes, skype_call_ask_no);
245}
246
247static char *skype_call_strerror(int err)
248{
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";
276        }
277}
278
279static void skype_parse_users(struct im_connection *ic, char *line)
280{
281        char **i, **nicks;
282
283        nicks = g_strsplit(line + 6, ", ", 0);
284        for (i = nicks; *i; i++)
285                skype_printf(ic, "GET USER %s ONLINESTATUS\n", *i);
286        g_strfreev(nicks);
287}
288
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++;
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;
309                ptr = g_strdup_printf("%s@skype.com", user);
310                imcb_add_buddy(ic, ptr, NULL);
311                if (strcmp(status, "OFFLINE") && (strcmp(status, "SKYPEOUT") ||
312                        !set_getbool(&ic->acc->set, "skypeout_offline")))
313                        flags |= OPT_LOGGED_IN;
314                if (strcmp(status, "ONLINE") && strcmp(status, "SKYPEME"))
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                }
329        } else if (!strncmp(ptr, "MOOD_TEXT ", 10)) {
330                char *buf = g_strdup_printf("%s@skype.com", user);
331                bee_user_t *bu = bee_user_by_handle(ic->bee, ic, buf);
332                g_free(buf);
333                buf = ptr + 10;
334                if (bu)
335                        imcb_buddy_status(ic, bu->handle, bu->flags, NULL,
336                                        *buf ? buf : NULL);
337                if (set_getbool(&ic->acc->set, "show_moods"))
338                        imcb_log(ic, "User `%s' changed mood text to `%s'", user, buf);
339        } else if (!strncmp(ptr, "FULLNAME ", 9))
340                sd->info_fullname = g_strdup(ptr + 9);
341        else if (!strncmp(ptr, "PHONE_HOME ", 11))
342                sd->info_phonehome = g_strdup(ptr + 11);
343        else if (!strncmp(ptr, "PHONE_OFFICE ", 13))
344                sd->info_phoneoffice = g_strdup(ptr + 13);
345        else if (!strncmp(ptr, "PHONE_MOBILE ", 13))
346                sd->info_phonemobile = g_strdup(ptr + 13);
347        else if (!strncmp(ptr, "NROF_AUTHED_BUDDIES ", 20))
348                sd->info_nrbuddies = g_strdup(ptr + 20);
349        else if (!strncmp(ptr, "TIMEZONE ", 9))
350                sd->info_tz = g_strdup(ptr + 9);
351        else if (!strncmp(ptr, "LASTONLINETIMESTAMP ", 20))
352                sd->info_seen = g_strdup(ptr + 20);
353        else if (!strncmp(ptr, "BIRTHDAY ", 9))
354                sd->info_birthday = g_strdup(ptr + 9);
355        else if (!strncmp(ptr, "SEX ", 4))
356                sd->info_sex = g_strdup(ptr + 4);
357        else if (!strncmp(ptr, "LANGUAGE ", 9))
358                sd->info_language = g_strdup(ptr + 9);
359        else if (!strncmp(ptr, "COUNTRY ", 8))
360                sd->info_country = g_strdup(ptr + 8);
361        else if (!strncmp(ptr, "PROVINCE ", 9))
362                sd->info_province = g_strdup(ptr + 9);
363        else if (!strncmp(ptr, "CITY ", 5))
364                sd->info_city = g_strdup(ptr + 5);
365        else if (!strncmp(ptr, "HOMEPAGE ", 9))
366                sd->info_homepage = g_strdup(ptr + 9);
367        else if (!strncmp(ptr, "ABOUT ", 6)) {
368                sd->info_about = g_strdup(ptr + 6);
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))
374                                g_string_append_printf(st, "Full Name: %s\n",
375                                        sd->info_fullname);
376                        g_free(sd->info_fullname);
377                }
378                if (sd->info_phonehome) {
379                        if (strlen(sd->info_phonehome))
380                                g_string_append_printf(st, "Home Phone: %s\n",
381                                        sd->info_phonehome);
382                        g_free(sd->info_phonehome);
383                }
384                if (sd->info_phoneoffice) {
385                        if (strlen(sd->info_phoneoffice))
386                                g_string_append_printf(st, "Office Phone: %s\n",
387                                        sd->info_phoneoffice);
388                        g_free(sd->info_phoneoffice);
389                }
390                if (sd->info_phonemobile) {
391                        if (strlen(sd->info_phonemobile))
392                                g_string_append_printf(st, "Mobile Phone: %s\n",
393                                        sd->info_phonemobile);
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))
399                                g_string_append_printf(st,
400                                        "Contacts: %s\n", sd->info_nrbuddies);
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);
410                                g_string_append_printf(st,
411                                        "Local Time: %s\n", ib);
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);
421                                g_string_append_printf(st,
422                                        "Last Seen: %s\n", ib);
423                        }
424                        g_free(sd->info_seen);
425                }
426                if (sd->info_birthday) {
427                        if (strlen(sd->info_birthday) &&
428                                strcmp(sd->info_birthday, "0")) {
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);
433                                g_string_append_printf(st,
434                                        "Birthday: %s\n", ib);
435
436                                strftime(ib, 256, "%Y", &tm);
437                                int year = atoi(ib);
438                                time_t t = time(NULL);
439                                struct tm *lt = localtime(&t);
440                                g_string_append_printf(st,
441                                        "Age: %d\n", lt->tm_year+1900-year);
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);
450                                g_string_append_printf(st,
451                                        "Gender: %s\n", sd->info_sex);
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;
462                                g_string_append_printf(st,
463                                        "Language: %s\n", iptr);
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;
474                                g_string_append_printf(st,
475                                        "Country: %s\n", iptr);
476                        }
477                        g_free(sd->info_country);
478                }
479                if (sd->info_province) {
480                        if (strlen(sd->info_province))
481                                g_string_append_printf(st,
482                                        "Region: %s\n", sd->info_province);
483                        g_free(sd->info_province);
484                }
485                if (sd->info_city) {
486                        if (strlen(sd->info_city))
487                                g_string_append_printf(st,
488                                        "City: %s\n", sd->info_city);
489                        g_free(sd->info_city);
490                }
491                if (sd->info_homepage) {
492                        if (strlen(sd->info_homepage))
493                                g_string_append_printf(st,
494                                        "Homepage: %s\n", sd->info_homepage);
495                        g_free(sd->info_homepage);
496                }
497                if (sd->info_about) {
498                        if (strlen(sd->info_about))
499                                g_string_append_printf(st, "%s\n",
500                                        sd->info_about);
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;
511        char buf[IRC_LINE_SIZE];
512        char *id = strchr(line, ' ');
513
514        if (!++id)
515                return;
516        char *info = strchr(id, ' ');
517
518        if (!info)
519                return;
520        *info = '\0';
521        info++;
522        if (!strcmp(info, "STATUS RECEIVED") || !strncmp(info, "EDITED_TIMESTAMP", 16)) {
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                 */
529                skype_printf(ic, "GET CHATMESSAGE %s FROM_HANDLE\n", id);
530                if (!strcmp(info, "STATUS RECEIVED"))
531                        skype_printf(ic, "GET CHATMESSAGE %s BODY\n", id);
532                else
533                        sd->is_edit = 1;
534                skype_printf(ic, "GET CHATMESSAGE %s TYPE\n", id);
535                skype_printf(ic, "GET CHATMESSAGE %s CHATNAME\n", id);
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);
560        } else if (!strncmp(info, "CHATNAME ", 9)) {
561                info += 9;
562                if (sd->handle && sd->body && sd->type) {
563                        struct groupchat *gc = bee_chat_by_title(ic->bee, ic, info);
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);
596                        }
597                        g_list_free(sd->body);
598                        sd->body = NULL;
599                }
600        }
601}
602
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, ' ');
607        char buf[IRC_LINE_SIZE];
608
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);
623                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
624                sd->call_status = SKYPE_CALL_RINGING;
625        } else if (!strcmp(info, "STATUS MISSED")) {
626                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
627                sd->call_status = SKYPE_CALL_MISSED;
628        } else if (!strcmp(info, "STATUS CANCELLED")) {
629                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
630                sd->call_status = SKYPE_CALL_CANCELLED;
631        } else if (!strcmp(info, "STATUS FINISHED")) {
632                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
633                sd->call_status = SKYPE_CALL_FINISHED;
634        } else if (!strcmp(info, "STATUS REFUSED")) {
635                skype_printf(ic, "GET CALL %s PARTNER_HANDLE\n", id);
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")) {
644                imcb_error(ic, "Call failed: %s",
645                        skype_call_strerror(sd->failurereason));
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;
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 {
661                                g_snprintf(buf, IRC_LINE_SIZE,
662                                        "The user %s is currently ringing you.",
663                                        info);
664                                skype_call_ask(ic, sd->call_id, buf);
665                        }
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);
674                        sd->call_status = 0;
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;
702                }
703                sd->call_status = 0;
704        }
705}
706
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
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")) {
721                skype_printf(ic, "GET FILETRANSFER %s PARTNER_HANDLE\n",
722                        id);
723                sd->filetransfer_status = SKYPE_FILETRANSFER_NEW;
724        } else if (!strcmp(info, "STATUS FAILED")) {
725                skype_printf(ic, "GET FILETRANSFER %s PARTNER_HANDLE\n",
726                        id);
727                sd->filetransfer_status = SKYPE_FILETRANSFER_FAILED;
728        } else if (!strncmp(info, "PARTNER_HANDLE ", 15)) {
729                info += 15;
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;
741                }
742                sd->filetransfer_status = 0;
743        }
744}
745
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
822static void skype_parse_chat(struct im_connection *ic, char *line)
823{
824        struct skype_data *sd = ic->proto_data;
825        char buf[IRC_LINE_SIZE];
826        char *id = strchr(line, ' ');
827
828        if (!++id)
829                return;
830        struct groupchat *gc;
831        char *info = strchr(id, ' ');
832
833        if (!info)
834                return;
835        *info = '\0';
836        info++;
837        /* Remove fake chat if we created one in skype_chat_with() */
838        gc = bee_chat_by_title(ic->bee, ic, "");
839        if (gc)
840                imcb_chat_free(gc);
841        if (!strcmp(info, "STATUS MULTI_SUBSCRIBED")) {
842                gc = imcb_chat_new(ic, id);
843                imcb_chat_name_hint(gc, id);
844                skype_printf(ic, "GET CHAT %s ADDER\n", id);
845                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
846        } else if (!strcmp(info, "STATUS DIALOG") && sd->groupchat_with) {
847                gc = imcb_chat_new(ic, id);
848                imcb_chat_name_hint(gc, id);
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. */
855                /*skype_printf(ic, "OPEN CHAT %s\n", id);*/
856                g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
857                                sd->groupchat_with);
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;
862                skype_printf(ic, "GET CHAT %s ADDER\n", id);
863                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
864        } else if (!strcmp(info, "STATUS UNSUBSCRIBED")) {
865                gc = bee_chat_by_title(ic->bee, ic, id);
866                if (gc)
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;
874                gc = bee_chat_by_title(ic->bee, ic, id);
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;
879                        }
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;
886                gc = bee_chat_by_title(ic->bee, ic, id);
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. */
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;
898                        g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com",
899                                        members[i]);
900                        if (!g_list_find_custom(gc->in_room, buf,
901                                (GCompareFunc)strcmp))
902                                imcb_chat_add_buddy(gc, buf);
903                }
904                imcb_chat_add_buddy(gc, sd->username);
905                g_strfreev(members);
906        }
907}
908
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
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{
926        /* Unused parameter */
927        line = line;
928        skype_printf(ic, "PONG\n");
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) {
938                skype_printf(ic, "GET CHAT %s STATUS\n", *i);
939                skype_printf(ic, "GET CHAT %s ACTIVEMEMBERS\n", *i);
940                i++;
941        }
942        g_strfreev(chats);
943}
944
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
959typedef void (*skype_parser)(struct im_connection *ic, char *line);
960
961static gboolean skype_read_callback(gpointer data, gint fd,
962                                    b_input_condition cond)
963{
964        struct im_connection *ic = data;
965        struct skype_data *sd = ic->proto_data;
966        char buf[IRC_LINE_SIZE];
967        int st, i;
968        char **lines, **lineptr, *line;
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 },
979                { "GROUP ", skype_parse_group },
980                { "PASSWORD ", skype_parse_password },
981                { "PROFILE PSTN_BALANCE ", skype_parse_profile },
982                { "PING", skype_parse_ping },
983                { "CHATS ", skype_parse_chats },
984                { "GROUPS ", skype_parse_groups },
985        };
986
987        /* Unused parameters */
988        fd = fd;
989        cond = cond;
990
991        if (!sd || sd->fd == -1)
992                return FALSE;
993        /* Read the whole data. */
994        st = ssl_read(sd->ssl, buf, sizeof(buf));
995        if (st > 0) {
996                buf[st] = '\0';
997                /* Then split it up to lines. */
998                lines = g_strsplit(buf, "\n", 0);
999                lineptr = lines;
1000                while ((line = *lineptr)) {
1001                        if (!strlen(line))
1002                                break;
1003                        if (set_getbool(&ic->acc->set, "skypeconsole_receive"))
1004                                imcb_buddy_msg(ic, "skypeconsole", line, 0, 0);
1005                        for (i = 0; i < ARRAY_SIZE(parsers); i++)
1006                                if (!strncmp(line, parsers[i].k,
1007                                        strlen(parsers[i].k))) {
1008                                        parsers[i].v(ic, line);
1009                                        break;
1010                                }
1011                        lineptr++;
1012                }
1013                g_strfreev(lines);
1014        } else if (st == 0 || (st < 0 && !sockerr_again())) {
1015                closesocket(sd->fd);
1016                sd->fd = -1;
1017
1018                imcb_error(ic, "Error while reading from server");
1019                imc_logout(ic, TRUE);
1020                return FALSE;
1021        }
1022        return TRUE;
1023}
1024
1025gboolean skype_start_stream(struct im_connection *ic)
1026{
1027        struct skype_data *sd = ic->proto_data;
1028        int st;
1029
1030        if (!sd)
1031                return FALSE;
1032
1033        if (sd->bfd <= 0)
1034                sd->bfd = b_input_add(sd->fd, B_EV_IO_READ,
1035                        skype_read_callback, ic);
1036
1037        /* Log in */
1038        skype_printf(ic, "USERNAME %s\n", ic->acc->user);
1039        skype_printf(ic, "PASSWORD %s\n", ic->acc->pass);
1040
1041        /* This will download all buddies and groups. */
1042        st = skype_printf(ic, "SEARCH FRIENDS\n");
1043        skype_printf(ic, "SEARCH GROUPS CUSTOM\n");
1044
1045        skype_printf(ic, "SET USERSTATUS ONLINE\n");
1046
1047        /* Auto join to bookmarked chats if requested.*/
1048        if (set_getbool(&ic->acc->set, "auto_join"))
1049                skype_printf(ic, "SEARCH BOOKMARKEDCHATS\n");
1050        return st;
1051}
1052
1053gboolean skype_connected(gpointer data, void *source, b_input_condition cond)
1054{
1055        struct im_connection *ic = data;
1056        struct skype_data *sd = ic->proto_data;
1057
1058        /* Unused parameter */
1059        cond = cond;
1060
1061        if (!source) {
1062                sd->ssl = NULL;
1063                imcb_error(ic, "Could not connect to server");
1064                imc_logout(ic, TRUE);
1065                return FALSE;
1066        }
1067        imcb_log(ic, "Connected to server, logging in");
1068
1069        return skype_start_stream(ic);
1070}
1071
1072static void skype_login(account_t *acc)
1073{
1074        struct im_connection *ic = imcb_new(acc);
1075        struct skype_data *sd = g_new0(struct skype_data, 1);
1076
1077        ic->proto_data = sd;
1078
1079        imcb_log(ic, "Connecting");
1080        sd->ssl = ssl_connect(set_getstr(&acc->set, "server"),
1081                set_getint(&acc->set, "port"), skype_connected, ic);
1082        sd->fd = sd->ssl ? ssl_getfd(sd->ssl) : -1;
1083        sd->username = g_strdup(acc->user);
1084
1085        sd->ic = ic;
1086
1087        if (set_getbool(&acc->set, "skypeconsole"))
1088                imcb_add_buddy(ic, "skypeconsole", NULL);
1089}
1090
1091static void skype_logout(struct im_connection *ic)
1092{
1093        struct skype_data *sd = ic->proto_data;
1094        int i;
1095
1096        skype_printf(ic, "SET USERSTATUS OFFLINE\n");
1097
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        }
1102        g_free(sd->username);
1103        g_free(sd->handle);
1104        g_free(sd);
1105        ic->proto_data = NULL;
1106}
1107
1108static int skype_buddy_msg(struct im_connection *ic, char *who, char *message,
1109                           int flags)
1110{
1111        char *ptr, *nick;
1112        int st;
1113
1114        /* Unused parameter */
1115        flags = flags;
1116
1117        nick = g_strdup(who);
1118        ptr = strchr(nick, '@');
1119        if (ptr)
1120                *ptr = '\0';
1121
1122        if (!strncmp(who, "skypeconsole", 12))
1123                st = skype_printf(ic, "%s\n", message);
1124        else
1125                st = skype_printf(ic, "MESSAGE %s %s\n", nick, message);
1126        g_free(nick);
1127
1128        return st;
1129}
1130
1131const struct skype_away_state *skype_away_state_by_name(char *name)
1132{
1133        int i;
1134
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;
1138
1139        return NULL;
1140}
1141
1142static void skype_set_away(struct im_connection *ic, char *state_txt,
1143                           char *message)
1144{
1145        const struct skype_away_state *state;
1146
1147        /* Unused parameter */
1148        message = message;
1149
1150        if (state_txt == NULL)
1151                state = skype_away_state_by_name("Online");
1152        else
1153                state = skype_away_state_by_name(state_txt);
1154        skype_printf(ic, "SET USERSTATUS %s\n", state->code);
1155}
1156
1157static GList *skype_away_states(struct im_connection *ic)
1158{
1159        static GList *l;
1160        int i;
1161
1162        /* Unused parameter */
1163        ic = ic;
1164
1165        if (l == NULL)
1166                for (i = 0; skype_away_state_list[i].full_name; i++)
1167                        l = g_list_append(l,
1168                                (void *)skype_away_state_list[i].full_name);
1169
1170        return l;
1171}
1172
1173static char *skype_set_display_name(set_t *set, char *value)
1174{
1175        account_t *acc = set->data;
1176        struct im_connection *ic = acc->ic;
1177
1178        skype_printf(ic, "SET PROFILE FULLNAME %s", value);
1179        return value;
1180}
1181
1182static char *skype_set_balance(set_t *set, char *value)
1183{
1184        account_t *acc = set->data;
1185        struct im_connection *ic = acc->ic;
1186
1187        skype_printf(ic, "GET PROFILE PSTN_BALANCE");
1188        return value;
1189}
1190
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
1214static char *skype_set_call(set_t *set, char *value)
1215{
1216        account_t *acc = set->data;
1217        struct im_connection *ic = acc->ic;
1218
1219        if (value) {
1220                skype_call(ic, value);
1221        } else
1222                skype_hangup(ic);
1223        return value;
1224}
1225
1226static void skype_add_buddy(struct im_connection *ic, char *who, char *group)
1227{
1228        char *nick, *ptr;
1229
1230        /* Unused parameter */
1231        group = group;
1232
1233        nick = g_strdup(who);
1234        ptr = strchr(nick, '@');
1235        if (ptr)
1236                *ptr = '\0';
1237        skype_printf(ic, "SET USER %s BUDDYSTATUS 2 Please authorize me\n",
1238                nick);
1239        g_free(nick);
1240}
1241
1242static void skype_remove_buddy(struct im_connection *ic, char *who, char *group)
1243{
1244        char *nick, *ptr;
1245
1246        /* Unused parameter */
1247        group = group;
1248
1249        nick = g_strdup(who);
1250        ptr = strchr(nick, '@');
1251        if (ptr)
1252                *ptr = '\0';
1253        skype_printf(ic, "SET USER %s BUDDYSTATUS 1\n", nick);
1254        g_free(nick);
1255}
1256
1257void skype_chat_msg(struct groupchat *gc, char *message, int flags)
1258{
1259        struct im_connection *ic = gc->ic;
1260
1261        /* Unused parameter */
1262        flags = flags;
1263
1264        skype_printf(ic, "CHATMESSAGE %s %s\n", gc->title, message);
1265}
1266
1267void skype_chat_leave(struct groupchat *gc)
1268{
1269        struct im_connection *ic = gc->ic;
1270        skype_printf(ic, "ALTER CHAT %s LEAVE\n", gc->title);
1271        gc->data = (void *)TRUE;
1272}
1273
1274void skype_chat_invite(struct groupchat *gc, char *who, char *message)
1275{
1276        struct im_connection *ic = gc->ic;
1277        char *ptr, *nick;
1278
1279        /* Unused parameter */
1280        who = who;
1281
1282        nick = g_strdup(message);
1283        ptr = strchr(nick, '@');
1284        if (ptr)
1285                *ptr = '\0';
1286        skype_printf(ic, "ALTER CHAT %s ADDMEMBERS %s\n", gc->title, nick);
1287        g_free(nick);
1288}
1289
1290void skype_chat_topic(struct groupchat *gc, char *message)
1291{
1292        struct im_connection *ic = gc->ic;
1293        struct skype_data *sd = ic->proto_data;
1294        skype_printf(ic, "ALTER CHAT %s SETTOPIC %s\n",
1295                gc->title, message);
1296        sd->topic_wait = 1;
1297}
1298
1299struct groupchat *skype_chat_with(struct im_connection *ic, char *who)
1300{
1301        struct skype_data *sd = ic->proto_data;
1302        char *ptr, *nick;
1303        nick = g_strdup(who);
1304        ptr = strchr(nick, '@');
1305        if (ptr)
1306                *ptr = '\0';
1307        skype_printf(ic, "CHAT CREATE %s\n", nick);
1308        sd->groupchat_with = g_strdup(nick);
1309        g_free(nick);
1310        /* We create a fake chat for now. We will replace it with a real one in
1311         * the real callback. */
1312        return imcb_chat_new(ic, "");
1313}
1314
1315static void skype_get_info(struct im_connection *ic, char *who)
1316{
1317        char *ptr, *nick;
1318        nick = g_strdup(who);
1319        ptr = strchr(nick, '@');
1320        if (ptr)
1321                *ptr = '\0';
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);
1337}
1338
1339static void skype_set_my_name(struct im_connection *ic, char *info)
1340{
1341        skype_set_display_name(set_find(&ic->acc->set, "display_name"), info);
1342}
1343
1344static void skype_init(account_t *acc)
1345{
1346        set_t *s;
1347
1348        s = set_add(&acc->set, "server", SKYPE_DEFAULT_SERVER, set_eval_account,
1349                acc);
1350        s->flags |= ACC_SET_OFFLINE_ONLY;
1351
1352        s = set_add(&acc->set, "port", SKYPE_DEFAULT_PORT, set_eval_int, acc);
1353        s->flags |= ACC_SET_OFFLINE_ONLY;
1354
1355        s = set_add(&acc->set, "display_name", NULL, skype_set_display_name,
1356                acc);
1357        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1358
1359        s = set_add(&acc->set, "call", NULL, skype_set_call, acc);
1360        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1361
1362        s = set_add(&acc->set, "balance", NULL, skype_set_balance, acc);
1363        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1364
1365        s = set_add(&acc->set, "skypeout_offline", "true", set_eval_bool, acc);
1366
1367        s = set_add(&acc->set, "skypeconsole", "false", set_eval_bool, acc);
1368        s->flags |= ACC_SET_OFFLINE_ONLY;
1369
1370        s = set_add(&acc->set, "skypeconsole_receive", "false", set_eval_bool,
1371                acc);
1372        s->flags |= ACC_SET_OFFLINE_ONLY;
1373
1374        s = set_add(&acc->set, "auto_join", "false", set_eval_bool, acc);
1375        s->flags |= ACC_SET_OFFLINE_ONLY;
1376
1377        s = set_add(&acc->set, "test_join", "false", set_eval_bool, acc);
1378        s->flags |= ACC_SET_OFFLINE_ONLY;
1379
1380        s = set_add(&acc->set, "show_moods", "false", set_eval_bool, acc);
1381
1382        s = set_add(&acc->set, "edit_prefix", "EDIT:",
1383                        NULL, acc);
1384}
1385
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
1416void init_plugin(void)
1417{
1418        struct prpl *ret = g_new0(struct prpl, 1);
1419
1420        ret->name = "skype";
1421        ret->login = skype_login;
1422        ret->init = skype_init;
1423        ret->logout = skype_logout;
1424        ret->buddy_msg = skype_buddy_msg;
1425        ret->get_info = skype_get_info;
1426        ret->set_my_name = skype_set_my_name;
1427        ret->away_states = skype_away_states;
1428        ret->set_away = skype_set_away;
1429        ret->add_buddy = skype_add_buddy;
1430        ret->remove_buddy = skype_remove_buddy;
1431        ret->chat_msg = skype_chat_msg;
1432        ret->chat_leave = skype_chat_leave;
1433        ret->chat_invite = skype_chat_invite;
1434        ret->chat_with = skype_chat_with;
1435        ret->handle_cmp = g_strcasecmp;
1436        ret->chat_topic = skype_chat_topic;
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
1441        register_protocol(ret);
1442}
Note: See TracBrowser for help on using the repository browser.