source: skype/skype.c @ 51dc72d

Last change on this file since 51dc72d was 51dc72d, checked in by Miklos Vajna <vmiklos@…>, at 2008-04-02T22:44:01Z

skype_set_call() allow calling anybody

  • here is the logic: we first try to map nicks to skype user names. on success, we use the username, on failure we use the value we got directly, later skype will return an error if there is no such nick or such other error occures.
  • Property mode set to 100644
File size: 32.8 KB
Line 
1/*
2 *  skype.c - Skype plugin for BitlBee
3 *
4 *  Copyright (c) 2007, 2008 by Miklos Vajna <vmiklos@frugalware.org>
5 *
6 *  Several ideas are used from the BitlBee Jabber plugin, which is
7 *
8 *  Copyright (c) 2006 by Wilmer van der Gaast <wilmer@gaast.net>
9 *
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License as published by
12 *  the Free Software Foundation; either version 2 of the License, or
13 *  (at your option) any later version.
14 *
15 *  This program is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 *  You should have received a copy of the GNU General Public License
21 *  along with this program; if not, write to the Free Software
22 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
23 *  USA.
24 */
25
26#define _XOPEN_SOURCE
27#include <stdio.h>
28#include <poll.h>
29#include <bitlbee.h>
30#include <bitlbee/ssl_client.h>
31#include <glib.h>
32
33#define SKYPE_DEFAULT_SERVER "localhost"
34#define SKYPE_DEFAULT_PORT "2727"
35
36/*
37 * Enumerations
38 */
39
40typedef enum
41{
42        SKYPE_CALL_RINGING = 1,
43        SKYPE_CALL_MISSED,
44        SKYPE_CALL_CANCELLED,
45        SKYPE_CALL_FINISHED,
46        SKYPE_CALL_REFUSED
47} skype_call_status;
48
49typedef enum
50{
51        SKYPE_FILETRANSFER_NEW = 1,
52        SKYPE_FILETRANSFER_FAILED
53} skype_filetransfer_status;
54
55/*
56 * Structures
57 */
58
59struct skype_data
60{
61        struct im_connection *ic;
62        char *username;
63        /* The effective file descriptor. We store it here so any function can
64         * write() to it. */
65        int fd;
66        /* File descriptor returned by bitlbee. we store it so we know when
67         * we're connected and when we aren't. */
68        int bfd;
69        /* ssl_getfd() uses this to get the file desciptor. */
70        void *ssl;
71        /* When we receive a new message id, we query the properties, finally
72         * the chatname. Store the properties here so that we can use
73         * imcb_buddy_msg() when we got the chatname. */
74        char *handle;
75        /* List, because of multiline messages. */
76        GList *body;
77        char *type;
78        /* This is necessary because we send a notification when we get the
79         * handle. So we store the state here and then we can send a
80         * notification about the handle is in a given status. */
81        skype_call_status call_status;
82        char *call_id;
83        char *call_duration;
84        /* If the call is outgoing or not */
85        int call_out;
86        /* Same for file transfers. */
87        skype_filetransfer_status filetransfer_status;
88        /* Using /j #nick we want to have a groupchat with two people. Usually
89         * not (default). */
90        char* groupchat_with;
91        /* The user who invited us to the chat. */
92        char* adder;
93        /* If we are waiting for a confirmation about we changed the topic. */
94        int topic_wait;
95        /* These are used by the info command. */
96        char *info_fullname;
97        char *info_phonehome;
98        char *info_phoneoffice;
99        char *info_phonemobile;
100        char *info_nrbuddies;
101        char *info_tz;
102        char *info_seen;
103        char *info_birthday;
104        char *info_sex;
105        char *info_language;
106        char *info_country;
107        char *info_province;
108        char *info_city;
109        char *info_homepage;
110        char *info_about;
111};
112
113struct skype_away_state
114{
115        char *code;
116        char *full_name;
117};
118
119struct skype_buddy_ask_data
120{
121        struct im_connection *ic;
122        /* This is also used for call IDs for simplicity */
123        char *handle;
124};
125
126/*
127 * Tables
128 */
129
130const struct skype_away_state skype_away_state_list[] =
131{
132        { "ONLINE",  "Online" },
133        { "SKYPEME",  "Skype Me" },
134        { "AWAY",   "Away" },
135        { "NA",    "Not available" },
136        { "DND",      "Do Not Disturb" },
137        { "INVISIBLE",      "Invisible" },
138        { "OFFLINE",      "Offline" },
139        { NULL, NULL}
140};
141
142/*
143 * Functions
144 */
145
146int skype_write( struct im_connection *ic, char *buf, int len )
147{
148        struct skype_data *sd = ic->proto_data;
149        struct pollfd pfd[1];
150
151        pfd[0].fd = sd->fd;
152        pfd[0].events = POLLOUT;
153
154        /* This poll is necessary or we'll get a SIGPIPE when we write() to
155         * sd->fd. */
156        poll(pfd, 1, 1000);
157        if(pfd[0].revents & POLLHUP)
158        {
159                imc_logout( ic, TRUE );
160                return FALSE;
161        }
162        ssl_write( sd->ssl, buf, len );
163
164        return TRUE;
165}
166
167static void skype_buddy_ask_yes( gpointer w, struct skype_buddy_ask_data *bla )
168{
169        char *buf = g_strdup_printf("SET USER %s ISAUTHORIZED TRUE", bla->handle);
170        skype_write( bla->ic, buf, strlen( buf ) );
171        g_free(buf);
172        g_free(bla->handle);
173        g_free(bla);
174}
175
176static void skype_buddy_ask_no( gpointer w, struct skype_buddy_ask_data *bla )
177{
178        char *buf = g_strdup_printf("SET USER %s ISAUTHORIZED FALSE", bla->handle);
179        skype_write( bla->ic, buf, strlen( buf ) );
180        g_free(buf);
181        g_free(bla->handle);
182        g_free(bla);
183}
184
185void skype_buddy_ask( struct im_connection *ic, char *handle, char *message)
186{
187        struct skype_buddy_ask_data *bla = g_new0( struct skype_buddy_ask_data, 1 );
188        char *buf;
189
190        bla->ic = ic;
191        bla->handle = g_strdup(handle);
192
193        buf = g_strdup_printf( "The user %s wants to add you to his/her buddy list, saying: '%s'.", handle, message);
194        imcb_ask( ic, buf, bla, skype_buddy_ask_yes, skype_buddy_ask_no );
195        g_free( buf );
196}
197
198static void skype_call_ask_yes( gpointer w, struct skype_buddy_ask_data *bla )
199{
200        char *buf = g_strdup_printf("SET CALL %s STATUS INPROGRESS", bla->handle);
201        skype_write( bla->ic, buf, strlen( buf ) );
202        g_free(buf);
203        g_free(bla->handle);
204        g_free(bla);
205}
206
207static void skype_call_ask_no( gpointer w, struct skype_buddy_ask_data *bla )
208{
209        char *buf = g_strdup_printf("SET CALL %s STATUS FINISHED", bla->handle);
210        skype_write( bla->ic, buf, strlen( buf ) );
211        g_free(buf);
212        g_free(bla->handle);
213        g_free(bla);
214}
215
216void skype_call_ask( struct im_connection *ic, char *call_id, char *message)
217{
218        struct skype_buddy_ask_data *bla = g_new0( struct skype_buddy_ask_data, 1 );
219
220        bla->ic = ic;
221        bla->handle = g_strdup(call_id);
222
223        imcb_ask( ic, message, bla, skype_call_ask_yes, skype_call_ask_no );
224}
225struct groupchat *skype_chat_by_name( struct im_connection *ic, char *name )
226{
227        struct groupchat *ret;
228
229        for( ret = ic->groupchats; ret; ret = ret->next )
230        {
231                if(strcmp(name, ret->title ) == 0 )
232                        break;
233        }
234
235        return ret;
236}
237
238static gboolean skype_read_callback( gpointer data, gint fd, b_input_condition cond )
239{
240        struct im_connection *ic = data;
241        struct skype_data *sd = ic->proto_data;
242        char buf[1024];
243        int st;
244        char **lines, **lineptr, *line, *ptr;
245
246        if( !sd || sd->fd == -1 )
247                return FALSE;
248        /* Read the whole data. */
249        st = ssl_read( sd->ssl, buf, sizeof( buf ) );
250        if( st > 0 )
251        {
252                buf[st] = '\0';
253                /* Then split it up to lines. */
254                lines = g_strsplit(buf, "\n", 0);
255                lineptr = lines;
256                while((line = *lineptr))
257                {
258                        if(!strlen(line))
259                                break;
260                        if(!strncmp(line, "USERS ", 6))
261                        {
262                                char **i;
263                                char **nicks;
264
265                                nicks = g_strsplit(line + 6, ", ", 0);
266                                i = nicks;
267                                while(*i)
268                                {
269                                        g_snprintf(buf, 1024, "GET USER %s ONLINESTATUS\n", *i);
270                                        skype_write( ic, buf, strlen( buf ) );
271                                        i++;
272                                }
273                                g_strfreev(nicks);
274                        }
275                        else if(!strncmp(line, "USER ", 5))
276                        {
277                                int flags = 0;
278                                char *status = strrchr(line, ' ');
279                                char *user = strchr(line, ' ');
280                                status++;
281                                ptr = strchr(++user, ' ');
282                                *ptr = '\0';
283                                ptr++;
284                                if(!strncmp(ptr, "ONLINESTATUS ", 13) &&
285                                                strcmp(user, sd->username) != 0
286                                                && strcmp(user, "echo123") != 0)
287                                {
288                                        ptr = g_strdup_printf("%s@skype.com", user);
289                                        imcb_add_buddy(ic, ptr, NULL);
290                                        if(strcmp(status, "OFFLINE") != 0)
291                                                flags |= OPT_LOGGED_IN;
292                                        if(strcmp(status, "ONLINE") != 0 && strcmp(status, "SKYPEME") != 0)
293                                                flags |= OPT_AWAY;
294                                        imcb_buddy_status(ic, ptr, flags, NULL, NULL);
295                                        g_free(ptr);
296                                }
297                                else if(!strncmp(ptr, "RECEIVEDAUTHREQUEST ", 20))
298                                {
299                                        char *message = ptr + 20;
300                                        if(strlen(message))
301                                                skype_buddy_ask(ic, user, message);
302                                }
303                                else if(!strncmp(ptr, "BUDDYSTATUS ", 12))
304                                {
305                                        char *st = ptr + 12;
306                                        if(!strcmp(st, "3"))
307                                        {
308                                                char *buf = g_strdup_printf("%s@skype.com", user);
309                                                imcb_add_buddy(ic, buf, NULL);
310                                                g_free(buf);
311                                        }
312                                }
313                                else if(!strncmp(ptr, "FULLNAME ", 9))
314                                        sd->info_fullname = g_strdup_printf("%s", ptr + 9);
315                                else if(!strncmp(ptr, "PHONE_HOME ", 11))
316                                        sd->info_phonehome = g_strdup_printf("%s", ptr + 11);
317                                else if(!strncmp(ptr, "PHONE_OFFICE ", 13))
318                                        sd->info_phoneoffice = g_strdup_printf("%s", ptr + 13);
319                                else if(!strncmp(ptr, "PHONE_MOBILE ", 13))
320                                        sd->info_phonemobile = g_strdup_printf("%s", ptr + 13);
321                                else if(!strncmp(ptr, "NROF_AUTHED_BUDDIES ", 20))
322                                        sd->info_nrbuddies = g_strdup_printf("%s", ptr + 20);
323                                else if(!strncmp(ptr, "TIMEZONE ", 9))
324                                        sd->info_tz = g_strdup_printf("%s", ptr + 9);
325                                else if(!strncmp(ptr, "LASTONLINETIMESTAMP ", 20))
326                                        sd->info_seen = g_strdup_printf("%s", ptr + 20);
327                                else if(!strncmp(ptr, "BIRTHDAY ", 9))
328                                        sd->info_birthday = g_strdup_printf("%s", ptr + 9);
329                                else if(!strncmp(ptr, "SEX ", 4))
330                                        sd->info_sex = g_strdup_printf("%s", ptr + 4);
331                                else if(!strncmp(ptr, "LANGUAGE ", 9))
332                                        sd->info_language = g_strdup_printf("%s", ptr + 9);
333                                else if(!strncmp(ptr, "COUNTRY ", 8))
334                                        sd->info_country = g_strdup_printf("%s", ptr + 8);
335                                else if(!strncmp(ptr, "PROVINCE ", 9))
336                                        sd->info_province = g_strdup_printf("%s", ptr + 9);
337                                else if(!strncmp(ptr, "CITY ", 5))
338                                        sd->info_city = g_strdup_printf("%s", ptr + 5);
339                                else if(!strncmp(ptr, "HOMEPAGE ", 9))
340                                        sd->info_homepage = g_strdup_printf("%s", ptr + 9);
341                                else if(!strncmp(ptr, "ABOUT ", 6))
342                                {
343                                        sd->info_about = g_strdup_printf("%s", ptr + 6);
344
345                                        GString *st = g_string_new("Contact Information\n");
346                                        g_string_append_printf(st, "Skype Name: %s\n", user);
347                                        if(sd->info_fullname)
348                                        {
349                                                if(strlen(sd->info_fullname))
350                                                        g_string_append_printf(st, "Full Name: %s\n", sd->info_fullname);
351                                                g_free(sd->info_fullname);
352                                        }
353                                        if(sd->info_phonehome)
354                                        {
355                                                if(strlen(sd->info_phonehome))
356                                                        g_string_append_printf(st, "Home Phone: %s\n", sd->info_phonehome);
357                                                g_free(sd->info_phonehome);
358                                        }
359                                        if(sd->info_phoneoffice)
360                                        {
361                                                if(strlen(sd->info_phoneoffice))
362                                                        g_string_append_printf(st, "Office Phone: %s\n", sd->info_phoneoffice);
363                                                g_free(sd->info_phoneoffice);
364                                        }
365                                        if(sd->info_phonemobile)
366                                        {
367                                                if(strlen(sd->info_phonemobile))
368                                                        g_string_append_printf(st, "Mobile Phone: %s\n", sd->info_phonemobile);
369                                                g_free(sd->info_phonemobile);
370                                        }
371                                        g_string_append_printf(st, "Personal Information\n");
372                                        if(sd->info_nrbuddies)
373                                        {
374                                                if(strlen(sd->info_nrbuddies))
375                                                        g_string_append_printf(st, "Contacts: %s\n", sd->info_nrbuddies);
376                                                g_free(sd->info_nrbuddies);
377                                        }
378                                        if(sd->info_tz)
379                                        {
380                                                if(strlen(sd->info_tz))
381                                                {
382                                                        char ib[256];
383                                                        time_t t = time(NULL);
384                                                        t += atoi(sd->info_tz)-(60*60*24);
385                                                        struct tm *gt = gmtime(&t);
386                                                        strftime(ib, 256, "%H:%M:%S", gt);
387                                                        g_string_append_printf(st, "Local Time: %s\n", ib);
388                                                }
389                                                g_free(sd->info_tz);
390                                        }
391                                        if(sd->info_seen)
392                                        {
393                                                if(strlen(sd->info_seen))
394                                                {
395                                                        char ib[256];
396                                                        time_t it = atoi(sd->info_seen);
397                                                        struct tm *tm = localtime(&it);
398                                                        strftime(ib, 256, ("%Y. %m. %d. %H:%M"), tm);
399                                                        g_string_append_printf(st, "Last Seen: %s\n", ib);
400                                                }
401                                                g_free(sd->info_seen);
402                                        }
403                                        if(sd->info_birthday)
404                                        {
405                                                if(strlen(sd->info_birthday) && strcmp(sd->info_birthday, "0"))
406                                                {
407                                                        char ib[256];
408                                                        struct tm tm;
409                                                        strptime(sd->info_birthday, "%Y%m%d", &tm);
410                                                        strftime(ib, 256, "%B %d, %Y", &tm);
411                                                        g_string_append_printf(st, "Birthday: %s\n", ib);
412
413                                                        strftime(ib, 256, "%Y", &tm);
414                                                        int year = atoi(ib);
415                                                        time_t t = time(NULL);
416                                                        struct tm *lt = localtime(&t);
417                                                        g_string_append_printf(st, "Age: %d\n", lt->tm_year+1900-year);
418                                                }
419                                                g_free(sd->info_birthday);
420                                        }
421                                        if(sd->info_sex)
422                                        {
423                                                if(strlen(sd->info_sex))
424                                                {
425                                                        char *iptr = sd->info_sex;
426                                                        while(*iptr++)
427                                                                *iptr = tolower(*iptr);
428                                                        g_string_append_printf(st, "Gender: %s\n", sd->info_sex);
429                                                }
430                                                g_free(sd->info_sex);
431                                        }
432                                        if(sd->info_language)
433                                        {
434                                                if(strlen(sd->info_language))
435                                                {
436                                                        char *iptr = strchr(sd->info_language, ' ');
437                                                        if(iptr)
438                                                                iptr++;
439                                                        else
440                                                                iptr = sd->info_language;
441                                                        g_string_append_printf(st, "Language: %s\n", iptr);
442                                                }
443                                                g_free(sd->info_language);
444                                        }
445                                        if(sd->info_country)
446                                        {
447                                                if(strlen(sd->info_country))
448                                                {
449                                                        char *iptr = strchr(sd->info_country, ' ');
450                                                        if(iptr)
451                                                                iptr++;
452                                                        else
453                                                                iptr = sd->info_country;
454                                                        g_string_append_printf(st, "Country: %s\n", iptr);
455                                                }
456                                                g_free(sd->info_country);
457                                        }
458                                        if(sd->info_province)
459                                        {
460                                                if(strlen(sd->info_province))
461                                                        g_string_append_printf(st, "Region: %s\n", sd->info_province);
462                                                g_free(sd->info_province);
463                                        }
464                                        if(sd->info_city)
465                                        {
466                                                if(strlen(sd->info_city))
467                                                        g_string_append_printf(st, "City: %s\n", sd->info_city);
468                                                g_free(sd->info_city);
469                                        }
470                                        if(sd->info_homepage)
471                                        {
472                                                if(strlen(sd->info_homepage))
473                                                        g_string_append_printf(st, "Homepage: %s\n", sd->info_homepage);
474                                                g_free(sd->info_homepage);
475                                        }
476                                        if(sd->info_about)
477                                        {
478                                                if(strlen(sd->info_about))
479                                                        g_string_append_printf(st, "%s\n", sd->info_about);
480                                                g_free(sd->info_about);
481                                        }
482                                        imcb_log(ic, "%s", st->str);
483                                        g_string_free(st, TRUE);
484                                }
485                        }
486                        else if(!strncmp(line, "CHATMESSAGE ", 12))
487                        {
488                                char *id = strchr(line, ' ');
489                                if(++id)
490                                {
491                                        char *info = strchr(id, ' ');
492                                        *info = '\0';
493                                        info++;
494                                        if(!strcmp(info, "STATUS RECEIVED"))
495                                        {
496                                                /* New message ID:
497                                                 * (1) Request its from field
498                                                 * (2) Request its body
499                                                 * (3) Request its type
500                                                 * (4) Query chatname
501                                                 */
502                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s FROM_HANDLE\n", id);
503                                                skype_write( ic, buf, strlen( buf ) );
504                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s BODY\n", id);
505                                                skype_write( ic, buf, strlen( buf ) );
506                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s TYPE\n", id);
507                                                skype_write( ic, buf, strlen( buf ) );
508                                                g_snprintf(buf, 1024, "GET CHATMESSAGE %s CHATNAME\n", id);
509                                                skype_write( ic, buf, strlen( buf ) );
510                                        }
511                                        else if(!strncmp(info, "FROM_HANDLE ", 12))
512                                        {
513                                                info += 12;
514                                                /* New from field value. Store
515                                                 * it, then we can later use it
516                                                 * when we got the message's
517                                                 * body. */
518                                                g_free(sd->handle);
519                                                sd->handle = g_strdup_printf("%s@skype.com", info);
520                                        }
521                                        else if(!strncmp(info, "EDITED_BY ", 10))
522                                        {
523                                                info += 10;
524                                                /* This is the same as
525                                                 * FROM_HANDLE, except that we
526                                                 * never request these lines
527                                                 * from Skype, we just get
528                                                 * them. */
529                                                g_free(sd->handle);
530                                                sd->handle = g_strdup_printf("%s@skype.com", info);
531                                        }
532                                        else if(!strncmp(info, "BODY ", 5))
533                                        {
534                                                info += 5;
535                                                sd->body = g_list_append(sd->body, g_strdup(info));
536                                        }
537                                        else if(!strncmp(info, "TYPE ", 5))
538                                        {
539                                                info += 5;
540                                                g_free(sd->type);
541                                                sd->type = g_strdup(info);
542                                        }
543                                        else if(!strncmp(info, "CHATNAME ", 9))
544                                        {
545                                                info += 9;
546                                                if(sd->handle && sd->body && sd->type)
547                                                {
548                                                        struct groupchat *gc = skype_chat_by_name(ic, info);
549                                                        int i;
550                                                        for(i=0;i<g_list_length(sd->body);i++)
551                                                        {
552                                                                char *body = g_list_nth_data(sd->body, i);
553                                                                if(!strcmp(sd->type, "SAID") || !strcmp(sd->type, "EMOTED"))
554                                                                {
555                                                                        char *st;
556                                                                        if(!strcmp(sd->type, "SAID"))
557                                                                                st = g_strdup(body);
558                                                                        else
559                                                                        {
560                                                                                st = g_strdup_printf("/me %s", body);
561                                                                        }
562                                                                        if(!gc)
563                                                                                /* Private message */
564                                                                                imcb_buddy_msg(ic, sd->handle, st, 0, 0);
565                                                                        else
566                                                                                /* Groupchat message */
567                                                                                imcb_chat_msg(gc, sd->handle, st, 0, 0);
568                                                                        g_free(st);
569                                                                }
570                                                                else if(!strcmp(sd->type, "SETTOPIC"))
571                                                                {
572                                                                        if(gc)
573                                                                                imcb_chat_topic(gc, sd->handle, body, 0);
574                                                                }
575                                                                else if(!strcmp(sd->type, "LEFT"))
576                                                                {
577                                                                        if(gc)
578                                                                                imcb_chat_remove_buddy(gc, sd->handle, NULL);
579                                                                }
580                                                        }
581                                                        g_list_free(sd->body);
582                                                        sd->body = NULL;
583                                                }
584                                        }
585                                }
586                        }
587                        else if(!strncmp(line, "CALL ", 5))
588                        {
589                                char *id = strchr(line, ' ');
590                                if(++id)
591                                {
592                                        char *info = strchr(id, ' ');
593                                        *info = '\0';
594                                        info++;
595                                        if(!strcmp(info, "STATUS RINGING"))
596                                        {
597                                                if(sd->call_id)
598                                                        g_free(sd->call_id);
599                                                sd->call_id = g_strdup(id);
600                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
601                                                skype_write( ic, buf, strlen( buf ) );
602                                                sd->call_status = SKYPE_CALL_RINGING;
603                                        }
604                                        else if(!strcmp(info, "STATUS MISSED"))
605                                        {
606                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
607                                                skype_write( ic, buf, strlen( buf ) );
608                                                sd->call_status = SKYPE_CALL_MISSED;
609                                        }
610                                        else if(!strcmp(info, "STATUS CANCELLED"))
611                                        {
612                                                        g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
613                                                        skype_write( ic, buf, strlen( buf ) );
614                                                        sd->call_status = SKYPE_CALL_CANCELLED;
615                                        }
616                                        else if(!strcmp(info, "STATUS FINISHED"))
617                                        {
618                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
619                                                skype_write( ic, buf, strlen( buf ) );
620                                                sd->call_status = SKYPE_CALL_FINISHED;
621                                        }
622                                        else if(!strcmp(info, "STATUS REFUSED"))
623                                        {
624                                                g_snprintf(buf, 1024, "GET CALL %s PARTNER_HANDLE\n", id);
625                                                skype_write( ic, buf, strlen( buf ) );
626                                                sd->call_status = SKYPE_CALL_REFUSED;
627                                        }
628                                        else if(!strcmp(info, "STATUS UNPLACED"))
629                                        {
630                                                if(sd->call_id)
631                                                        g_free(sd->call_id);
632                                                /* Save the ID for later usage (Cancel/Finish). */
633                                                sd->call_id = g_strdup(id);
634                                                sd->call_out = TRUE;
635                                        }
636                                        else if(!strncmp(info, "DURATION ", 9))
637                                        {
638                                                if(sd->call_duration)
639                                                        g_free(sd->call_duration);
640                                                sd->call_duration = g_strdup(info+9);
641                                        }
642                                        else if(!strncmp(info, "PARTNER_HANDLE ", 15))
643                                        {
644                                                info += 15;
645                                                if(sd->call_status) {
646                                                        switch(sd->call_status)
647                                                        {
648                                                                case SKYPE_CALL_RINGING:
649                                                                        if(sd->call_out)
650                                                                                imcb_log(ic, "You are currently ringing the user %s.", info);
651                                                                        else
652                                                                        {
653                                                                                g_snprintf(buf, 1024, "The user %s is currently ringing you.", info);
654                                                                                skype_call_ask(ic, sd->call_id, buf);
655                                                                        }
656                                                                        break;
657                                                                case SKYPE_CALL_MISSED:
658                                                                        imcb_log(ic, "You have missed a call from user %s.", info);
659                                                                        break;
660                                                                case SKYPE_CALL_CANCELLED:
661                                                                        imcb_log(ic, "You cancelled the call to the user %s.", info);
662                                                                        sd->call_status = 0;
663                                                                        sd->call_out = FALSE;
664                                                                        break;
665                                                                case SKYPE_CALL_REFUSED:
666                                                                        if(sd->call_out)
667                                                                                imcb_log(ic, "The user %s refused the call.", info);
668                                                                        else
669                                                                                imcb_log(ic, "You refused the call from user %s.", info);
670                                                                        sd->call_out = FALSE;
671                                                                        break;
672                                                                case SKYPE_CALL_FINISHED:
673                                                                        if(sd->call_duration)
674                                                                                imcb_log(ic, "You finished the call to the user %s (duration: %s seconds).", info, sd->call_duration);
675                                                                        else
676                                                                                imcb_log(ic, "You finished the call to the user %s.", info);
677                                                                        sd->call_out = FALSE;
678                                                                        break;
679                                                                default:
680                                                                        /* Don't be noisy, ignore other statuses for now. */
681                                                                        break;
682                                                        }
683                                                        sd->call_status = 0;
684                                                }
685                                        }
686                                }
687                        }
688                        else if(!strncmp(line, "FILETRANSFER ", 13))
689                        {
690                                char *id = strchr(line, ' ');
691                                if(++id)
692                                {
693                                        char *info = strchr(id, ' ');
694                                        *info = '\0';
695                                        info++;
696                                        if(!strcmp(info, "STATUS NEW"))
697                                        {
698                                                g_snprintf(buf, 1024, "GET FILETRANSFER %s PARTNER_HANDLE\n", id);
699                                                skype_write( ic, buf, strlen( buf ) );
700                                                sd->filetransfer_status = SKYPE_FILETRANSFER_NEW;
701                                        }
702                                        else if(!strcmp(info, "STATUS FAILED"))
703                                        {
704                                                g_snprintf(buf, 1024, "GET FILETRANSFER %s PARTNER_HANDLE\n", id);
705                                                skype_write( ic, buf, strlen( buf ) );
706                                                sd->filetransfer_status = SKYPE_FILETRANSFER_FAILED;
707                                        }
708                                        else if(!strncmp(info, "PARTNER_HANDLE ", 15))
709                                        {
710                                                info += 15;
711                                                if(sd->filetransfer_status) {
712                                                        switch(sd->filetransfer_status)
713                                                        {
714                                                                case SKYPE_FILETRANSFER_NEW:
715                                                                        imcb_log(ic, "The user %s offered a new file for you.", info);
716                                                                        break;
717                                                                case SKYPE_FILETRANSFER_FAILED:
718                                                                        imcb_log(ic, "Failed to transfer file from user %s.", info);
719                                                                        break;
720                                                        }
721                                                        sd->filetransfer_status = 0;
722                                                }
723                                        }
724                                }
725                        }
726                        else if(!strncmp(line, "CHAT ", 5))
727                        {
728                                char *id = strchr(line, ' ');
729                                if(++id)
730                                {
731                                        char *info = strchr(id, ' ');
732                                        if(info)
733                                                *info = '\0';
734                                        info++;
735                                        /* Remove fake chat if we created one in skype_chat_with() */
736                                        struct groupchat *gc = skype_chat_by_name(ic, "");
737                                        if(gc)
738                                                imcb_chat_free(gc);
739                                        if(!strcmp(info, "STATUS MULTI_SUBSCRIBED"))
740                                        {
741                                                imcb_chat_new( ic, id );
742                                                g_snprintf(buf, 1024, "GET CHAT %s ADDER\n", id);
743                                                skype_write(ic, buf, strlen(buf));
744                                                g_snprintf(buf, 1024, "GET CHAT %s TOPIC\n", id);
745                                                skype_write(ic, buf, strlen(buf));
746                                        }
747                                        else if(!strcmp(info, "STATUS DIALOG") && sd->groupchat_with)
748                                        {
749                                                gc = imcb_chat_new( ic, id );
750                                                /* According to the docs this
751                                                 * is necessary. However it
752                                                 * does not seem the situation
753                                                 * and it would open an extra
754                                                 * window on our client, so
755                                                 * just leave it out. */
756                                                /*g_snprintf(buf, 1024, "OPEN CHAT %s\n", id);
757                                                skype_write(ic, buf, strlen(buf));*/
758                                                g_snprintf(buf, 1024, "%s@skype.com", sd->groupchat_with);
759                                                imcb_chat_add_buddy(gc, buf);
760                                                imcb_chat_add_buddy(gc, sd->username);
761                                                g_free(sd->groupchat_with);
762                                                sd->groupchat_with = NULL;
763                                                g_snprintf(buf, 1024, "GET CHAT %s ADDER\n", id);
764                                                skype_write(ic, buf, strlen(buf));
765                                                g_snprintf(buf, 1024, "GET CHAT %s TOPIC\n", id);
766                                                skype_write(ic, buf, strlen(buf));
767                                        }
768                                        else if(!strcmp(info, "STATUS UNSUBSCRIBED"))
769                                        {
770                                                gc = skype_chat_by_name(ic, id);
771                                                if(gc)
772                                                        gc->data = (void*)FALSE;
773                                        }
774                                        else if(!strncmp(info, "ADDER ", 6))
775                                        {
776                                                info += 6;
777                                                g_free(sd->adder);
778                                                sd->adder = g_strdup_printf("%s@skype.com", info);
779                                        }
780                                        else if(!strncmp(info, "TOPIC ", 6))
781                                        {
782                                                info += 6;
783                                                gc = skype_chat_by_name(ic, id);
784                                                if(gc && (sd->adder || sd->topic_wait))
785                                                {
786                                                        if(sd->topic_wait)
787                                                        {
788                                                                sd->adder = g_strdup(sd->username);
789                                                                sd->topic_wait = 0;
790                                                        }
791                                                        imcb_chat_topic(gc, sd->adder, info, 0);
792                                                        g_free(sd->adder);
793                                                        sd->adder = NULL;
794                                                }
795                                        }
796                                        else if(!strncmp(info, "ACTIVEMEMBERS ", 14))
797                                        {
798                                                info += 14;
799                                                gc = skype_chat_by_name(ic, id);
800                                                /* Hack! We set ->data to TRUE
801                                                 * while we're on the channel
802                                                 * so that we won't rejoin
803                                                 * after a /part. */
804                                                if(gc && !gc->data)
805                                                {
806                                                        char **members = g_strsplit(info, " ", 0);
807                                                        int i;
808                                                        for(i=0;members[i];i++)
809                                                        {
810                                                                if(!strcmp(members[i], sd->username))
811                                                                        continue;
812                                                                g_snprintf(buf, 1024, "%s@skype.com", members[i]);
813                                                                if(!g_list_find_custom(gc->in_room, buf, (GCompareFunc)strcmp))
814                                                                        imcb_chat_add_buddy(gc, buf);
815                                                        }
816                                                        imcb_chat_add_buddy(gc, sd->username);
817                                                        g_strfreev(members);
818                                                }
819                                        }
820                                }
821                        }
822                        else if(!strncmp(line, "PASSWORD ", 9))
823                        {
824                                if(!strncmp(line+9, "OK", 2))
825                                        imcb_connected(ic);
826                                else
827                                {
828                                        imcb_error(ic, "Authentication Failed");
829                                        imc_logout( ic, TRUE );
830                                }
831                        }
832                        lineptr++;
833                }
834                g_strfreev(lines);
835        }
836        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
837        {
838                closesocket( sd->fd );
839                sd->fd = -1;
840
841                imcb_error( ic, "Error while reading from server" );
842                imc_logout( ic, TRUE );
843                return FALSE;
844        }
845        return TRUE;
846}
847
848gboolean skype_start_stream( struct im_connection *ic )
849{
850        struct skype_data *sd = ic->proto_data;
851        char *buf;
852        int st;
853
854        if(!sd)
855                return FALSE;
856
857        if( sd->bfd <= 0 )
858                sd->bfd = b_input_add( sd->fd, GAIM_INPUT_READ, skype_read_callback, ic );
859
860        /* Log in */
861        buf = g_strdup_printf("USERNAME %s\n", ic->acc->user);
862        st = skype_write( ic, buf, strlen( buf ) );
863        g_free(buf);
864        buf = g_strdup_printf("PASSWORD %s\n", ic->acc->pass);
865        st = skype_write( ic, buf, strlen( buf ) );
866        g_free(buf);
867
868        /* This will download all buddies. */
869        buf = g_strdup_printf("SEARCH FRIENDS\n");
870        st = skype_write( ic, buf, strlen( buf ) );
871        g_free(buf);
872        buf = g_strdup_printf("SET USERSTATUS ONLINE\n");
873        skype_write( ic, buf, strlen( buf ) );
874        g_free(buf);
875        return st;
876}
877
878gboolean skype_connected( gpointer data, void *source, b_input_condition cond )
879{
880        struct im_connection *ic = data;
881        struct skype_data *sd = ic->proto_data;
882        if(!source)
883        {
884                sd->ssl = NULL;
885                imcb_error( ic, "Could not connect to server" );
886                imc_logout( ic, TRUE );
887                return FALSE;
888        }
889        imcb_log( ic, "Connected to server, logging in" );
890        return skype_start_stream(ic);
891}
892
893static void skype_login( account_t *acc )
894{
895        struct im_connection *ic = imcb_new( acc );
896        struct skype_data *sd = g_new0( struct skype_data, 1 );
897
898        ic->proto_data = sd;
899
900        imcb_log( ic, "Connecting" );
901        sd->ssl = ssl_connect(set_getstr( &acc->set, "server" ), set_getint( &acc->set, "port" ), skype_connected, ic );
902        sd->fd = sd->ssl ? ssl_getfd( sd->ssl ) : -1;
903        sd->username = g_strdup( acc->user );
904
905        sd->ic = ic;
906}
907
908static void skype_logout( struct im_connection *ic )
909{
910        struct skype_data *sd = ic->proto_data;
911        char *buf;
912
913        buf = g_strdup_printf("SET USERSTATUS OFFLINE\n");
914        skype_write( ic, buf, strlen( buf ) );
915        g_free(buf);
916
917        g_free(sd->username);
918        g_free(sd->handle);
919        g_free(sd);
920        ic->proto_data = NULL;
921}
922
923static int skype_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )
924{
925        char *buf, *ptr, *nick;
926        int st;
927
928        nick = g_strdup(who);
929        ptr = strchr(nick, '@');
930        if(ptr)
931                *ptr = '\0';
932
933        buf = g_strdup_printf("MESSAGE %s %s\n", nick, message);
934        g_free(nick);
935        st = skype_write( ic, buf, strlen( buf ) );
936        g_free(buf);
937
938        return st;
939}
940
941const struct skype_away_state *skype_away_state_by_name( char *name )
942{
943        int i;
944
945        for( i = 0; skype_away_state_list[i].full_name; i ++ )
946                if( g_strcasecmp( skype_away_state_list[i].full_name, name ) == 0 )
947                        return( skype_away_state_list + i );
948
949        return NULL;
950}
951
952static void skype_set_away( struct im_connection *ic, char *state_txt, char *message )
953{
954        const struct skype_away_state *state;
955        char *buf;
956
957        if( strcmp( state_txt, GAIM_AWAY_CUSTOM ) == 0 )
958                state = skype_away_state_by_name( "Away" );
959        else
960                state = skype_away_state_by_name( state_txt );
961        buf = g_strdup_printf("SET USERSTATUS %s\n", state->code);
962        skype_write( ic, buf, strlen( buf ) );
963        g_free(buf);
964}
965
966static GList *skype_away_states( struct im_connection *ic )
967{
968        static GList *l = NULL;
969        int i;
970       
971        if( l == NULL )
972                for( i = 0; skype_away_state_list[i].full_name; i ++ )
973                        l = g_list_append( l, (void*) skype_away_state_list[i].full_name );
974       
975        return l;
976}
977
978static char *skype_set_display_name( set_t *set, char *value )
979{
980        account_t *acc = set->data;
981        struct im_connection *ic = acc->ic;
982        char *buf;
983
984        buf = g_strdup_printf("SET PROFILE FULLNAME %s", value);
985        skype_write( ic, buf, strlen( buf ) );
986        g_free(buf);
987        return(value);
988}
989
990static char *skype_set_call( set_t *set, char *value )
991{
992        account_t *acc = set->data;
993        struct im_connection *ic = acc->ic;
994        struct skype_data *sd = ic->proto_data;
995        char *nick, *ptr, *buf;
996
997        if(value)
998        {
999                user_t *u = user_find(acc->irc, value);
1000                /* We are starting a call */
1001                if(!u)
1002                        nick = g_strdup(value);
1003                else
1004                        nick = g_strdup(u->handle);
1005                ptr = strchr(nick, '@');
1006                if(ptr)
1007                        *ptr = '\0';
1008
1009                buf = g_strdup_printf("CALL %s", nick);
1010                skype_write( ic, buf, strlen( buf ) );
1011                g_free(buf);
1012                g_free(nick);
1013        }
1014        else
1015        {
1016                /* We are ending a call */
1017                if(sd->call_id)
1018                {
1019                        buf = g_strdup_printf("SET CALL %s STATUS FINISHED", sd->call_id);
1020                        skype_write( ic, buf, strlen( buf ) );
1021                        g_free(buf);
1022                        g_free(sd->call_id);
1023                        sd->call_id = NULL;
1024                }
1025                else
1026                {
1027                        imcb_error(ic, "There are no active calls currently.");
1028                }
1029        }
1030        return(value);
1031}
1032
1033static void skype_add_buddy( struct im_connection *ic, char *who, char *group )
1034{
1035        char *buf, *nick, *ptr;
1036
1037        nick = g_strdup(who);
1038        ptr = strchr(nick, '@');
1039        if(ptr)
1040                *ptr = '\0';
1041        buf = g_strdup_printf("SET USER %s BUDDYSTATUS 2 Please authorize me\n", nick);
1042        skype_write( ic, buf, strlen( buf ) );
1043        g_free(nick);
1044}
1045
1046static void skype_remove_buddy( struct im_connection *ic, char *who, char *group )
1047{
1048        char *buf, *nick, *ptr;
1049
1050        nick = g_strdup(who);
1051        ptr = strchr(nick, '@');
1052        if(ptr)
1053                *ptr = '\0';
1054        buf = g_strdup_printf("SET USER %s BUDDYSTATUS 1\n", nick);
1055        skype_write( ic, buf, strlen( buf ) );
1056        g_free(nick);
1057}
1058
1059void skype_chat_msg( struct groupchat *gc, char *message, int flags )
1060{
1061        struct im_connection *ic = gc->ic;
1062        char *buf;
1063        buf = g_strdup_printf("CHATMESSAGE %s %s\n", gc->title, message);
1064        skype_write( ic, buf, strlen( buf ) );
1065        g_free(buf);
1066}
1067
1068void skype_chat_leave( struct groupchat *gc )
1069{
1070        struct im_connection *ic = gc->ic;
1071        char *buf;
1072        buf = g_strdup_printf("ALTER CHAT %s LEAVE\n", gc->title);
1073        skype_write( ic, buf, strlen( buf ) );
1074        g_free(buf);
1075        gc->data = (void*)TRUE;
1076}
1077
1078void skype_chat_invite(struct groupchat *gc, char *who, char *message)
1079{
1080        struct im_connection *ic = gc->ic;
1081        char *buf, *ptr, *nick;
1082        nick = g_strdup(message);
1083        ptr = strchr(nick, '@');
1084        if(ptr)
1085                *ptr = '\0';
1086        buf = g_strdup_printf("ALTER CHAT %s ADDMEMBERS %s\n", gc->title, nick);
1087        skype_write( ic, buf, strlen( buf ) );
1088        g_free(buf);
1089        g_free(nick);
1090}
1091
1092void skype_chat_topic(struct groupchat *gc, char *message)
1093{
1094        struct im_connection *ic = gc->ic;
1095        struct skype_data *sd = ic->proto_data;
1096        char *buf;
1097        buf = g_strdup_printf("ALTER CHAT %s SETTOPIC %s\n", gc->title, message);
1098        skype_write( ic, buf, strlen( buf ) );
1099        g_free(buf);
1100        sd->topic_wait = 1;
1101}
1102
1103struct groupchat *skype_chat_with(struct im_connection *ic, char *who)
1104{
1105        struct skype_data *sd = ic->proto_data;
1106        char *ptr, *nick, *buf;
1107        nick = g_strdup(who);
1108        ptr = strchr(nick, '@');
1109        if(ptr)
1110                *ptr = '\0';
1111        buf = g_strdup_printf("CHAT CREATE %s\n", nick);
1112        skype_write(ic, buf, strlen(buf));
1113        g_free(buf);
1114        sd->groupchat_with = g_strdup(nick);
1115        g_free(nick);
1116        /* We create a fake chat for now. We will replace it with a real one in
1117         * the real callback. */
1118        return(imcb_chat_new( ic, "" ));
1119}
1120
1121static void skype_get_info(struct im_connection *ic, char *who)
1122{
1123        char *ptr, *nick, *buf;
1124        nick = g_strdup(who);
1125        ptr = strchr(nick, '@');
1126        if(ptr)
1127                *ptr = '\0';
1128        buf = g_strdup_printf("GET USER %s FULLNAME\n", nick);
1129        skype_write(ic, buf, strlen(buf));
1130        g_free(buf);
1131        buf = g_strdup_printf("GET USER %s PHONE_HOME\n", nick);
1132        skype_write(ic, buf, strlen(buf));
1133        g_free(buf);
1134        buf = g_strdup_printf("GET USER %s PHONE_OFFICE\n", nick);
1135        skype_write(ic, buf, strlen(buf));
1136        g_free(buf);
1137        buf = g_strdup_printf("GET USER %s PHONE_MOBILE\n", nick);
1138        skype_write(ic, buf, strlen(buf));
1139        g_free(buf);
1140        buf = g_strdup_printf("GET USER %s NROF_AUTHED_BUDDIES\n", nick);
1141        skype_write(ic, buf, strlen(buf));
1142        g_free(buf);
1143        buf = g_strdup_printf("GET USER %s TIMEZONE\n", nick);
1144        skype_write(ic, buf, strlen(buf));
1145        g_free(buf);
1146        buf = g_strdup_printf("GET USER %s LASTONLINETIMESTAMP\n", nick);
1147        skype_write(ic, buf, strlen(buf));
1148        g_free(buf);
1149        buf = g_strdup_printf("GET USER %s BIRTHDAY\n", nick);
1150        skype_write(ic, buf, strlen(buf));
1151        g_free(buf);
1152        buf = g_strdup_printf("GET USER %s SEX\n", nick);
1153        skype_write(ic, buf, strlen(buf));
1154        g_free(buf);
1155        buf = g_strdup_printf("GET USER %s LANGUAGE\n", nick);
1156        skype_write(ic, buf, strlen(buf));
1157        g_free(buf);
1158        buf = g_strdup_printf("GET USER %s COUNTRY\n", nick);
1159        skype_write(ic, buf, strlen(buf));
1160        g_free(buf);
1161        buf = g_strdup_printf("GET USER %s PROVINCE\n", nick);
1162        skype_write(ic, buf, strlen(buf));
1163        g_free(buf);
1164        buf = g_strdup_printf("GET USER %s CITY\n", nick);
1165        skype_write(ic, buf, strlen(buf));
1166        g_free(buf);
1167        buf = g_strdup_printf("GET USER %s HOMEPAGE\n", nick);
1168        skype_write(ic, buf, strlen(buf));
1169        g_free(buf);
1170        buf = g_strdup_printf("GET USER %s ABOUT\n", nick);
1171        skype_write(ic, buf, strlen(buf));
1172        g_free(buf);
1173}
1174
1175static void skype_set_my_name( struct im_connection *ic, char *info )
1176{
1177        skype_set_display_name( set_find( &ic->acc->set, "display_name" ), info );
1178}
1179
1180static void skype_init( account_t *acc )
1181{
1182        set_t *s;
1183
1184        s = set_add( &acc->set, "server", SKYPE_DEFAULT_SERVER, set_eval_account, acc );
1185        s->flags |= ACC_SET_OFFLINE_ONLY;
1186
1187        s = set_add( &acc->set, "port", SKYPE_DEFAULT_PORT, set_eval_int, acc );
1188        s->flags |= ACC_SET_OFFLINE_ONLY;
1189
1190        s = set_add( &acc->set, "display_name", NULL, skype_set_display_name, acc );
1191        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1192
1193        s = set_add( &acc->set, "call", NULL, skype_set_call, acc );
1194        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
1195}
1196
1197void init_plugin(void)
1198{
1199        struct prpl *ret = g_new0( struct prpl, 1 );
1200
1201        ret->name = "skype";
1202        ret->login = skype_login;
1203        ret->init = skype_init;
1204        ret->logout = skype_logout;
1205        ret->buddy_msg = skype_buddy_msg;
1206        ret->get_info = skype_get_info;
1207        ret->set_my_name = skype_set_my_name;
1208        ret->away_states = skype_away_states;
1209        ret->set_away = skype_set_away;
1210        ret->add_buddy = skype_add_buddy;
1211        ret->remove_buddy = skype_remove_buddy;
1212        ret->chat_msg = skype_chat_msg;
1213        ret->chat_leave = skype_chat_leave;
1214        ret->chat_invite = skype_chat_invite;
1215        ret->chat_with = skype_chat_with;
1216        ret->handle_cmp = g_strcasecmp;
1217        ret->chat_topic = skype_chat_topic;
1218        register_protocol( ret );
1219}
1220
1221/* vim: set ts=2 sw=2 noet: */
Note: See TracBrowser for help on using the repository browser.