/* * Skype plugin for libpurple/Pidgin/Adium * Written by: Eion Robb * * This plugin uses the Skype API to show your contacts in libpurple, and send/receive * chat messages. * It requires the Skype program to be running. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Skype API Terms of Use: * The following statement must be displayed in the documentation of this appliction: * This plugin "uses Skype Software" to display contacts, and chat to Skype users from within Pidgin * "This product uses the Skype API but is not endorsed, certified or otherwise approved in any way by Skype" * * The use of this plugin requries your acceptance of the Skype EULA (http://www.skype.com/intl/en/company/legal/eula/index.html) * * Skype is the trademark of Skype Limited */ #define PURPLE_PLUGIN #define DBUS_API_SUBJECT_TO_CHANGE #define _GNU_SOURCE #define GETTEXT_PACKAGE "skype4pidgin" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #if __GNUC__ # include #endif #include //#include #undef ENABLE_NLS #ifdef ENABLE_NLS # ifdef _WIN32 # include # endif # include #else # define _(a) a #endif #ifndef G_GNUC_NULL_TERMINATED # if __GNUC__ >= 4 # define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) # else # define G_GNUC_NULL_TERMINATED # endif /* __GNUC__ >= 4 */ #endif /* G_GNUC_NULL_TERMINATED */ #include #include #include #include #include #include #include #include #include #ifndef INSTANTBIRD #include gboolean skype_media_initiate(PurpleAccount *account, const char *who, PurpleMediaSessionType type); PurpleMediaCaps skype_get_media_caps(PurpleAccount *account, const char *who); static void skype_handle_incoming_call(PurpleConnection *gc, char *callnumber_string); static void skype_handle_call_got_ended(char *callnumber_string); static void skype_send_call_end(char *callnumber_string); static GHashTable *call_media_hash = NULL; #endif typedef struct _SkypeBuddy { PurpleBuddy *buddy; //this bit used in Get Info gchar *handle; gchar *fullname; gchar *mood; struct tm *birthday; gchar *gender; gchar *language; gchar *country; gboolean is_video_capable; gboolean is_authorized; gboolean is_blocked; time_t last_online; gdouble timezone_offset; guint number_of_buddies; gchar *about; //this bit not used yet gchar *province; gchar *city; gchar *phone_home; gchar *phone_office; gchar *phone_mobile; gchar *homepage; gboolean has_call_equipment; gboolean is_voicemail_capable; gboolean is_callforward_active; gboolean can_leave_voicemail; //libpurple buddy stuff guint typing_stream; //whether we've populated all fields from skype gboolean is_skypebuddy_complete; } SkypeBuddy; typedef struct _SkypeChat { PurpleAccount *account; PurpleConversation *conv; gint prpl_chat_id; gchar *name; gchar **members; gchar *partner_handle; PurpleConversationType type; gchar *topic; gchar *friendlyname; int type_request_count; //count the number of times we request the chat type } SkypeChat; static GHashTable *chat_link_table = NULL; static GStaticMutex chat_link_mutex = G_STATIC_MUTEX_INIT; //This is used for incomming SMS status messages to be associated with a particular phone number static GHashTable *sms_convo_link_table = NULL; static guint protocol_version = 7; #define INCLUDED_LIBSKYPE_C #include "debug.c" #include "skype_messaging.c" static void plugin_init(PurplePlugin *plugin); gboolean plugin_load(PurplePlugin *plugin); gboolean plugin_unload(PurplePlugin *plugin); GList *skype_status_types(PurpleAccount *acct); void skype_login(PurpleAccount *acct); void skype_close(PurpleConnection *gc); int skype_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags); int skype_send_sms(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags); static void hide_skype(); void skype_get_info(PurpleConnection *gc, const gchar *username); gchar *skype_get_user_info(const gchar *username, const gchar *property); void skype_set_status(PurpleAccount *account, PurpleStatus *status); void skype_set_idle(PurpleConnection *gc, int time); void skype_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); void skype_add_buddy_with_invite(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message); void skype_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); void skype_add_deny(PurpleConnection *gc, const char *who); void skype_rem_deny(PurpleConnection *gc, const char *who); void skype_add_permit(PurpleConnection *gc, const char *who); void skype_rem_permit(PurpleConnection *gc, const char *who); void skype_keepalive(PurpleConnection *gc); gboolean skype_set_buddies(PurpleAccount *acct); void skype_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img); GList *skype_actions(PurplePlugin *plugin, gpointer context); void skype_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *userinfo, gboolean full); char *skype_status_text(PurpleBuddy *buddy); const char *skype_list_icon(PurpleAccount *account, PurpleBuddy *buddy); const char *skype_normalize(const PurpleAccount *acct, const char *who); void skype_update_buddy_alias(PurpleBuddy *buddy); gboolean skype_update_buddy_status(PurpleBuddy *buddy); void skype_get_account_alias(PurpleAccount *acct); const char *skype_get_account_username(PurpleAccount *acct); static PurpleAccount *skype_get_account(PurpleAccount *newaccount); void skype_update_buddy_icon(PurpleBuddy *buddy); static GList *skype_node_menu(PurpleBlistNode *node); static void skype_silence(PurplePlugin *plugin, gpointer data); void skype_slist_friend_check(gpointer buddy_pointer, gpointer friends_pointer); int skype_slist_friend_search(gconstpointer buddy_pointer, gconstpointer buddyname_pointer); static gboolean skype_check_missedmessages(PurpleAccount *account); static int skype_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags); static void skype_chat_leave(PurpleConnection *gc, int id); static void skype_chat_invite(PurpleConnection *gc, int id, const char *msg, const char *who); static void skype_initiate_chat(PurpleBlistNode *node, gpointer data); static void skype_set_chat_topic(PurpleConnection *gc, int id, const char *topic); gchar *skype_cb_real_name(PurpleConnection *gc, int id, const char *who); GList *skype_join_chat_info(PurpleConnection *gc); GHashTable *skype_join_chat_info_defaults(PurpleConnection *gc, const char *chat_name); void skype_alias_buddy(PurpleConnection *gc, const char *who, const char *alias); gboolean skype_offline_msg(const PurpleBuddy *buddy); //void skype_slist_remove_messages(gpointer buddy_pointer, gpointer unused); static void skype_plugin_update_check(void); void skype_plugin_update_callback(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message); gchar *timestamp_to_datetime(time_t timestamp); void skype_show_search_users(PurplePluginAction *action); static void skype_search_users(PurpleConnection *gc, const gchar *searchterm); void skype_searchresults_add_buddy(PurpleConnection *gc, GList *row, void *user_data); gchar *skype_strdup_withhtml(const gchar *src); void skype_join_chat(PurpleConnection *, GHashTable *components); gchar *skype_get_chat_name(GHashTable *components); static void skype_display_skype_credit(PurplePluginAction *action); unsigned int skype_send_typing(PurpleConnection *, const char *name, PurpleTypingState state); static PurpleCmdRet skype_cmd_leave(PurpleConversation *, const gchar *, gchar **, gchar **, void *); static PurpleCmdRet skype_cmd_topic(PurpleConversation *, const gchar *, gchar **, gchar **, void *); static PurpleCmdRet skype_cmd_kick(PurpleConversation *, const gchar *, gchar **, gchar **, void *); static PurpleCmdRet skype_cmd_kickban(PurpleConversation *, const gchar *, gchar **, gchar **, void *); int skype_send_raw(PurpleConnection *, const char *, int); void skype_group_buddy(PurpleConnection *, const char *who, const char *old_group, const char *new_group); void skype_rename_group(PurpleConnection *, const char *old_name, PurpleGroup *group, GList *moved_buddies); void skype_remove_group(PurpleConnection *, PurpleGroup *); int skype_find_group_with_name(const char *group_name_in); static gboolean skype_uri_handler(const char *proto, const char *cmd, GHashTable *params); void skype_buddy_free(PurpleBuddy *buddy); const char *skype_list_emblem(PurpleBuddy *buddy); SkypeBuddy *skype_buddy_new(PurpleBuddy *buddy); static void skype_request_auth_from_blist(PurpleBlistNode *node, gpointer data); static void skype_open_sms_im(PurpleBlistNode *node, gpointer data); void skype_got_buddy_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message); gboolean skype_check_keepalive(PurpleConnection *gc); void skype_open_verify_mobile_number_step2(gpointer ignore, gchar *mobile_number); static void skype_open_verify_mobile_number(PurplePlugin *plugin, gpointer data); void skype_open_verify_mobile_number_step3(gpointer mobile_number, gchar *verification_code); gchar *skype_set_next_sms_number_for_conversation(PurpleConversation *conv, const gchar *who); gboolean skype_login_cb(gpointer acct); void skype_put_buddies_in_groups(void); gboolean groups_table_find_group(gpointer key, gpointer value, gpointer user_data); PurpleChat *skype_find_blist_chat(PurpleAccount *account, const char *name); gboolean skype_login_retry(PurpleAccount *acct); gboolean skype_login_part2(PurpleAccount *acct); typedef void (*_account_char_fn)(PurpleAccount *, const char *); void skype_set_public_alias(PurpleConnection *gc, const char *alias, _account_char_fn success_cb, _account_char_fn failure_cb); void skype_get_public_alias(PurpleConnection *gc, _account_char_fn success_cb, _account_char_fn failure_cb); #ifndef SKYPENET #ifdef _WIN32 static void skype_open_skype_options(void); #endif void skype_call_number(gpointer ignore, gchar *number); void skype_call_number_request(PurplePlugin *plugin, gpointer data); void skype_program_update_callback(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message); static void skype_program_update_check(void); static void skype_send_file_from_blist(PurpleBlistNode *node, gpointer data); static void skype_call_user_from_blist(PurpleBlistNode *node, gpointer data); static void skype_end_call_from_blist(PurpleBlistNode *node, gpointer data); #endif PurplePluginProtocolInfo prpl_info = { /* options */ #ifndef SKYPENET #if _WIN32 || __APPLE__ || SKYPE_DBUS || !USE_XVFB_SERVER OPT_PROTO_NO_PASSWORD| #endif #endif #if PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION >= 8 OPT_PROTO_INVITE_MESSAGE| #endif OPT_PROTO_REGISTER_NOSCREENNAME|OPT_PROTO_CHAT_TOPIC|OPT_PROTO_SLASH_COMMANDS_NATIVE, NULL, /* user_splits */ NULL, /* protocol_options */ {"png,gif,jpeg", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */ skype_list_icon, /* list_icon */ skype_list_emblem, /* list_emblem */ skype_status_text, /* status_text */ skype_tooltip_text, /* tooltip_text */ skype_status_types, /* status_types */ skype_node_menu, /* blist_node_menu */ skype_join_chat_info,/* chat_info */ skype_join_chat_info_defaults,/* chat_info_defaults */ skype_login, /* login */ skype_close, /* close */ skype_send_im, /* send_im */ NULL, /* set_info */ skype_send_typing, /* send_typing */ skype_get_info, /* get_info */ skype_set_status, /* set_status */ skype_set_idle, /* set_idle */ NULL, /* change_passwd */ #if PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION >= 8 skype_add_buddy_with_invite, /* add_buddy */ #else skype_add_buddy, /* add_buddy */ #endif NULL, /* add_buddies */ skype_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ skype_add_permit, /* add_permit */ skype_add_deny, /* add_deny */ skype_rem_permit, /* rem_permit */ skype_rem_deny, /* rem_deny */ NULL, /* set_permit_deny */ skype_join_chat, /* join_chat */ NULL, /* reject chat invite */ skype_get_chat_name, /* get_chat_name */ skype_chat_invite, /* chat_invite */ /*skype_chat_leave*/NULL, /* chat_leave */ NULL, /* chat_whisper */ skype_chat_send, /* chat_send */ skype_keepalive, /* keepalive */ NULL, /* register_user */ NULL, /* get_cb_info */ NULL, /* get_cb_away */ skype_alias_buddy, /* alias_buddy */ skype_group_buddy, /* group_buddy */ skype_rename_group, /* rename_group */ skype_buddy_free, /* buddy_free */ NULL, /* convo_closed */ skype_normalize, /* normalize */ skype_set_buddy_icon,/* set_buddy_icon */ skype_remove_group, /* remove_group */ /*skype_cb_real_name*/NULL, /* get_cb_real_name */ skype_set_chat_topic,/* set_chat_topic */ NULL, /* find_blist_chat */ NULL, /* roomlist_get_list */ NULL, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ NULL, /* send_file */ NULL, /* new_xfer */ skype_offline_msg, /* offline_message */ NULL, /* whiteboard_prpl_ops */ skype_send_raw, /* send_raw */ NULL, /* roomlist_room_serialize */ NULL, /* unregister_user */ NULL, /* send_attention */ NULL, /* attention_types */ #if PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION == 1 (gpointer) #endif sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ #ifndef INSTANTBIRD skype_media_initiate,/* initiate_media */ skype_get_media_caps,/* can_do_media */ #else NULL, /* initiate_media */ NULL, /* can_do_media */ #endif NULL, /* get_moods */ skype_set_public_alias, /* set_public_alias */ skype_get_public_alias /* get_public_alias */ #if PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION >= 8 , skype_add_buddy_with_invite, /* add_buddy_with_invite */ NULL /* add_buddies_with_invite */ #endif }; static PurplePluginInfo info = { PURPLE_PLUGIN_MAGIC, /* PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, */ 2, 1, PURPLE_PLUGIN_PROTOCOL, /* type */ NULL, /* ui_requirement */ 0, /* flags */ NULL, /* dependencies */ PURPLE_PRIORITY_DEFAULT, /* priority */ #ifdef SKYPENET "prpl-bigbrownchunx-skypenet", "Skype (Network)", #else #ifdef SKYPE_DBUS "prpl-bigbrownchunx-skype-dbus", /* id */ "Skype (D-Bus)", #else "prpl-bigbrownchunx-skype", /* id */ "Skype", /* name */ #endif #endif "2.0", /* version */ "Allows using Skype IM functions from within Pidgin", /* summary */ "Allows using Skype IM functions from within Pidgin", /* description */ "Eion Robb ", /* author */ "http://eion.robbmob.com/", /* homepage */ plugin_load, /* load */ plugin_unload, /* unload */ NULL, /* destroy */ NULL, /* ui_info */ &prpl_info, /* extra_info */ NULL, /* prefs_info */ skype_actions, /* actions */ NULL, /* padding */ NULL, NULL, NULL }; static PurplePlugin *this_plugin; static void plugin_init(PurplePlugin *plugin) { PurpleAccountOption *option; #if ( _WIN32 || __APPLE__ ) && ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); #endif #if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32 if (glib_check_version(2, 32, 0)) if (!g_thread_supported ()) g_thread_init (NULL); #endif this_plugin = plugin; /* plugin's path at this_plugin->path */ #ifdef SKYPENET option = purple_account_option_string_new(_("Server"), "host", "skype.robbmob.com"); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_int_new(_("Port"), "port", 5000); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); #endif option = purple_account_option_bool_new(_("Show SkypeOut contacts as 'Online'"), "skypeout_online", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Make Skype online/offline when going online/offline"), "skype_sync", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Update Skype status message"), "update_status", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Display video capability status"), "video_capable", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); //option = purple_account_option_bool_new(_("Automatically check for updates"), "check_for_updates", TRUE); //prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Automatically reject all authorization requests"), "reject_all_auths", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); #ifndef SKYPENET option = purple_account_option_bool_new(_("Auto-start Skype if not running"), "skype_autostart", TRUE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_string_new(_("Skype Path"), "skype_path", ""); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); #endif //leave purple_cmd_register("leave", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plugin->info->id, skype_cmd_leave, _("leave [channel]: Leave the chat"), NULL); //topic purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, plugin->info->id, skype_cmd_topic, _("topic [<new topic>]: View or change the topic"), NULL); //call, as in call person //kick purple_cmd_register("kick", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, plugin->info->id, skype_cmd_kick, _("kick <user> [room]: Kick a user from the room."), NULL); //kickban purple_cmd_register("kickban", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, plugin->info->id, skype_cmd_kickban, _("kick <user> [room]: Kick a user from the room."), NULL); purple_signal_connect(purple_get_core(), "uri-handler", plugin, PURPLE_CALLBACK(skype_uri_handler), NULL); } /* copied from oscar.c to be libpurple 2.1 compatible */ static PurpleAccount * find_acct(const char *prpl, const char *acct_id) { PurpleAccount *acct = NULL; /* If we have a specific acct, use it */ if (acct_id) { acct = purple_accounts_find(acct_id, prpl); if (acct && !purple_account_is_connected(acct)) acct = NULL; } else { /* Otherwise find an active account for the protocol */ GList *l = purple_accounts_get_all(); while (l) { if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) && purple_account_is_connected(l->data)) { acct = l->data; break; } l = l->next; } } return acct; } static PurpleCmdRet skype_cmd_leave(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) { PurpleConnection *gc = NULL; int id = -1; gc = purple_conversation_get_gc(conv); id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); if (gc == NULL || id == -1) return PURPLE_CMD_RET_FAILED; skype_chat_leave(gc, id); return PURPLE_CMD_RET_OK; } static PurpleCmdRet skype_cmd_topic(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) { PurpleConnection *gc = NULL; int id = -1; gc = purple_conversation_get_gc(conv); id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); if (gc == NULL || id == -1) return PURPLE_CMD_RET_FAILED; skype_set_chat_topic(gc, id, args ? args[0] : NULL); return PURPLE_CMD_RET_OK; } static PurpleCmdRet skype_cmd_kick(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) { gchar *temp; gchar *chat_id; chat_id = (gchar *)g_hash_table_lookup(conv->data, "chat_id"); temp = skype_send_message("ALTER CHAT %s KICK %s", chat_id, args[0]); if (!temp || !strlen(temp)) { g_free(temp); return PURPLE_CMD_RET_FAILED; } return PURPLE_CMD_RET_OK; } static PurpleCmdRet skype_cmd_kickban(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) { gchar *temp; gchar *chat_id; chat_id = (gchar *)g_hash_table_lookup(conv->data, "chat_id"); temp = skype_send_message("ALTER CHAT %s KICKBAN %s", chat_id, args[0]); if (!temp || !strlen(temp)) { g_free(temp); return PURPLE_CMD_RET_FAILED; } return PURPLE_CMD_RET_OK; } PURPLE_INIT_PLUGIN(skype, plugin_init, info); static PurpleAccount * skype_get_account(PurpleAccount *newaccount) { static PurpleAccount* account = NULL; if (newaccount != NULL) account = newaccount; return account; } #if 0 void skype_ft_debug(PurpleXfer *xfer) {printf("%s\n", xfer->account->protocol_id);} void skype_ft_new_xfer(PurpleXfer *xfer) {printf("skype_ft_new_xfer\n");skype_ft_debug(xfer);} void skype_ft_destroy(PurpleXfer *xfer) {printf("skype_ft_destroy\n");skype_ft_debug(xfer);} void skype_ft_add_xfer(PurpleXfer *xfer) {printf("skype_ft_add_xfer\n");skype_ft_debug(xfer);} void skype_ft_update_progress(PurpleXfer *xfer, double percent) {printf("skype_ft_update_progress\n");skype_ft_debug(xfer);} void skype_ft_cancel_local(PurpleXfer *xfer) {printf("skype_ft_cancel_local\n");skype_ft_debug(xfer);} void skype_ft_cancel_remote(PurpleXfer *xfer) {printf("skype_ft_cancel_remote\n");skype_ft_debug(xfer);} gboolean plugin_load(PurplePlugin *plugin) { PurpleXferUiOps *ops = purple_xfers_get_ui_ops(); ops->new_xfer = skype_ft_new_xfer; ops->destroy = skype_ft_destroy; ops->add_xfer = skype_ft_add_xfer; ops->update_progress = skype_ft_update_progress; ops->cancel_local = skype_ft_cancel_local; ops->cancel_remote = skype_ft_cancel_remote; return TRUE; } #else gboolean plugin_load(PurplePlugin *plugin) { return TRUE; } #endif gboolean plugin_unload(PurplePlugin *plugin) { return TRUE; } static GList * skype_node_menu(PurpleBlistNode *node) { GList *m = NULL; PurpleMenuAction *act; PurpleBuddy *buddy; SkypeBuddy *sbuddy; gchar *temp; if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { buddy = (PurpleBuddy *)node; sbuddy = (SkypeBuddy *)buddy->proto_data; #ifndef SKYPENET #ifndef __APPLE__ act = purple_menu_action_new(_("_Send File"), PURPLE_CALLBACK(skype_send_file_from_blist), NULL, NULL); m = g_list_append(m, act); #endif #ifndef INSTANTBIRD if (!purple_media_manager_get()) { gint call_id = 0, i, j; temp = skype_send_message("SEARCH ACTIVECALLS"); if (temp && *temp && temp[5] && temp[6]) { gchar **ids = g_strsplit(&temp[6], ", ", 0); gchar **buddy_calls = NULL; g_free(temp); temp = skype_send_message("SEARCH CALLS %s", buddy->name); if (temp && *temp && temp[5] && temp[6]) { buddy_calls = g_strsplit(&temp[6], ", ", 0); for (i = 0; ids[i]; i++) { for (j = 0; buddy_calls[j]; j++) { if (g_str_equal(ids[i], buddy_calls[j])) { call_id = atoi(ids[i]); break; } } if (call_id) break; } } g_free(temp); g_strfreev(buddy_calls); g_strfreev(ids); } if (call_id) { act = purple_menu_action_new(_("End Call"), PURPLE_CALLBACK(skype_end_call_from_blist), GINT_TO_POINTER(call_id), NULL); m = g_list_append(m, act); } else { act = purple_menu_action_new(_("Call..."), PURPLE_CALLBACK(skype_call_user_from_blist), NULL, NULL); m = g_list_append(m, act); } } #endif #endif act = purple_menu_action_new(_("Initiate _Chat"), PURPLE_CALLBACK(skype_initiate_chat), NULL, NULL); m = g_list_append(m, act); if (buddy->name[0] == '+' || (sbuddy && sbuddy->phone_mobile)) { act = purple_menu_action_new(_("Send SMS"), PURPLE_CALLBACK(skype_open_sms_im), NULL, NULL); m = g_list_append(m, act); } if (!PURPLE_BUDDY_IS_ONLINE(buddy)) { temp = skype_send_message("GET USER %s BUDDYSTATUS", buddy->name); purple_debug_info("skype", "Offline buddy's status is %c\n", temp[18+strlen(buddy->name)]); if (temp[18+strlen(buddy->name)] == '2') { act = purple_menu_action_new(_("Re-request authorization"), PURPLE_CALLBACK(skype_request_auth_from_blist), NULL, NULL); m = g_list_append(m, act); } g_free(temp); } } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { act = purple_menu_action_new(_("Initiate _Chat"), PURPLE_CALLBACK(skype_initiate_chat), NULL, NULL); m = g_list_append(m, act); } return m; } #ifndef SKYPENET static void skype_send_file_from_blist(PurpleBlistNode *node, gpointer data) { PurpleBuddy *buddy; if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { buddy = (PurpleBuddy *) node; if (PURPLE_BUDDY_IS_ONLINE(buddy)) { skype_send_message_nowait("OPEN FILETRANSFER %s", buddy->name); } } } static void skype_call_user_from_blist(PurpleBlistNode *node, gpointer data) { PurpleBuddy *buddy; if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { buddy = (PurpleBuddy *) node; skype_send_message_nowait("CALL %s", buddy->name); } } static void skype_end_call_from_blist(PurpleBlistNode *node, gpointer data) { gint call_id; if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { call_id = GPOINTER_TO_INT(data); skype_send_message_nowait("ALTER CALL %d HANGUP", call_id); } } #endif const char * skype_normalize(const PurpleAccount *acct, const char *who) { static gchar *last_normalize = NULL; g_free(last_normalize); last_normalize = g_utf8_strdown(who, -1); return last_normalize; } GList * skype_actions(PurplePlugin *plugin, gpointer context) { GList *m = NULL; PurpleMenuAction *act; #ifndef SKYPENET act = purple_menu_action_new(_("Hide Skype"), PURPLE_CALLBACK(skype_silence), NULL, NULL); m = g_list_append(m, act); act = purple_menu_action_new(_("Check for Skype updates..."), PURPLE_CALLBACK(skype_program_update_check), NULL, NULL); m = g_list_append(m, act); #endif if (this_plugin != NULL && this_plugin->path != NULL) { act = purple_menu_action_new(_("Check for plugin updates..."), PURPLE_CALLBACK(skype_plugin_update_check), NULL, NULL); m = g_list_append(m, act); } act = purple_menu_action_new(_("Search for buddies..."), PURPLE_CALLBACK(skype_show_search_users), NULL, NULL); m = g_list_append(m, act); act = purple_menu_action_new(_("Check Skype balance..."), PURPLE_CALLBACK(skype_display_skype_credit), NULL, NULL); m = g_list_append(m, act); #ifndef SKYPENET act = purple_menu_action_new(_("Call..."), PURPLE_CALLBACK(skype_call_number_request), NULL, NULL); m = g_list_append(m, act); #ifdef _WIN32 act = purple_menu_action_new(_("Open Skype Options..."), PURPLE_CALLBACK(skype_open_skype_options), NULL, NULL); m = g_list_append(m, act); #endif #endif act = purple_menu_action_new(_("Verify mobile number..."), PURPLE_CALLBACK(skype_open_verify_mobile_number), NULL, NULL); m = g_list_append(m, act); return m; } #if !defined SKYPENET && defined _WIN32 static void skype_open_skype_options(void) { skype_send_message_nowait("OPEN OPTIONS"); } #endif static void skype_silence(PurplePlugin *plugin, gpointer data) { skype_send_message_nowait("SET SILENT_MODE ON"); skype_send_message_nowait("MINIMIZE"); hide_skype(); } static void skype_open_verify_mobile_number(PurplePlugin *plugin, gpointer data) { purple_request_input(plugin, _("Verify mobile number..."), _("Enter the mobile phone number to send verification SMS to"), NULL, NULL, FALSE, FALSE, NULL, _("OK"), G_CALLBACK(skype_open_verify_mobile_number_step2), _("_Cancel"), NULL, NULL, NULL, NULL, plugin); } void skype_open_verify_mobile_number_step2(gpointer plugin, gchar *mobile_number) { skype_send_message_nowait("CREATE SMS CONFIRMATION_CODE_REQUEST %s", mobile_number); purple_request_input(plugin, _("Verify mobile number..."), _("Enter the verification code which was sent to your mobile"), NULL, NULL, FALSE, FALSE, NULL, _("OK"), G_CALLBACK(skype_open_verify_mobile_number_step3), _("_Cancel"), NULL, NULL, NULL, NULL, mobile_number); } void skype_open_verify_mobile_number_step3(gpointer mobile_number, gchar *verification_code) { skype_send_message_nowait("CREATE SMS CONFIRMATION_CODE_SUBMIT %s", mobile_number); } static void skype_plugin_update_check(void) { gchar *basename; struct stat *filestat = g_new(struct stat, 1); //this_plugin is the PidginPlugin if (this_plugin == NULL || this_plugin->path == NULL || filestat == NULL || g_stat(this_plugin->path, filestat) == -1) { purple_notify_warning(this_plugin, "Warning", "Could not check for updates", NULL); } else { basename = g_path_get_basename(this_plugin->path); purple_util_fetch_url(g_strconcat("http://eion.robbmob.com/version?version=", basename, NULL), TRUE, NULL, FALSE, skype_plugin_update_callback, (gpointer)filestat); } } void skype_plugin_update_callback(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { time_t mtime = ((struct stat *) user_data)->st_mtime; time_t servertime = atoi(url_text); skype_debug_info("skype", "Server filemtime: %d, Local filemtime: %d\n", servertime, mtime); if (servertime > mtime) { purple_notify_info(this_plugin, _("New Version Available"), _("There is a newer version of the Skype plugin available for download."), g_strconcat(_("Your version"),": ",timestamp_to_datetime(mtime),"\n",_("Latest version"),": ",timestamp_to_datetime(servertime),"\nLatest version available from: ", this_plugin->info->homepage, NULL)); } else { purple_notify_info(this_plugin, _("No updates found"), _("No updates found"), _("You have the latest version of the Skype plugin")); } } #ifndef SKYPENET static void skype_program_update_check(void) { /* Windows: http://ui.skype.com/ui/0/3.5.0.239/en/getnewestversion Linux: http://ui.skype.com/ui/2/2.0.0.13/en/getnewestversion Mac: http://ui.skype.com/ui/3/2.6.0.151/en/getnewestversion User-Agent: Skype */ gchar *version; gchar *temp; gchar version_url[60]; int platform_number; #ifdef _WIN32 platform_number = 0; #else # ifdef __APPLE__ platform_number = 3; # else platform_number = 2; # endif #endif temp = skype_send_message("GET SKYPEVERSION"); version = g_strdup(&temp[13]); g_free(temp); sprintf(version_url, "http://ui.skype.com/ui/%d/%s/en/getnewestversion", platform_number, version); purple_util_fetch_url(version_url, TRUE, "Skype", TRUE, skype_program_update_callback, (gpointer)version); } void skype_program_update_callback(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { gchar *version = (gchar *)user_data; int v1, v2, v3, v4; int s1, s2, s3, s4; gboolean newer_version = FALSE; sscanf(version, "%d.%d.%d.%d", &v1, &v2, &v3, &v4); sscanf(url_text, "%d.%d.%d.%d", &s1, &s2, &s3, &s4); if (s1 > v1) newer_version = TRUE; else if (s1 == v1 && s2 > v2) newer_version = TRUE; else if (s1 == v1 && s2 == v2 && s3 > v3) newer_version = TRUE; else if (s1 == v1 && s2 == v2 && s3 == v3 && s4 > v4) newer_version = TRUE; if (newer_version) { purple_notify_info(this_plugin, _("New Version Available"), _("There is a newer version of Skype available for download"), g_strconcat(_("Your version"),": ", version, "\n",_("Latest version"),": ", url_text, "\n\nhttp://www.skype.com/go/download", NULL)); } else { purple_notify_info(this_plugin, _("No updates found"), _("No updates found"), _("You have the latest version of Skype")); } } void skype_call_number_request(PurplePlugin *plugin, gpointer data) { //http://developer.pidgin.im/doxygen/dev/html/request_8h.html#80ea2f9ad3a45471e05f09a2b5abcd75 purple_request_input(plugin, _("Call..."), _("Enter the phone number or Skype buddy name to call"), NULL, NULL, FALSE, FALSE, NULL, _("Call..."), G_CALLBACK(skype_call_number), _("_Cancel"), NULL, NULL, NULL, NULL, NULL); } void skype_call_number(gpointer ignore, gchar *number) { skype_send_message_nowait("CALL %s", number); } #endif GList * skype_status_types(PurpleAccount *acct) { GList *types; PurpleStatusType *status; skype_debug_info("skype", "returning status types\n"); types = NULL; /* Statuses are almost all the same. Define a macro to reduce code repetition. */ #define _SKYPE_ADD_NEW_STATUS(prim,id,name) status = \ purple_status_type_new_with_attrs( \ prim, /* PurpleStatusPrimitive */ \ id, /* id */ \ _(name),/* name */ \ TRUE, /* savable */ \ TRUE, /* user_settable */ \ FALSE, /* not independent */ \ \ /* Attributes - each status can have a message. */ \ "message", \ _("Message"), \ purple_value_new(PURPLE_TYPE_STRING), \ NULL); \ \ \ types = g_list_append(types, status) _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE, "ONLINE", "Online"); _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE, "SKYPEME", "SkypeMe"); _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_AWAY, "AWAY", "Away"); _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_EXTENDED_AWAY, "NA", "Not Available"); _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_UNAVAILABLE, "DND", "Do Not Disturb"); _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_INVISIBLE, "INVISIBLE", "Invisible"); //_SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_OFFLINE, "Offline"); //User status could be 'logged out' - make not settable status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "LOGGEDOUT", _("Logged out"), FALSE, FALSE, FALSE); types = g_list_append(types, status); //User could be a SkypeOut contact if (purple_account_get_bool(acct, "skypeout_online", TRUE)) { status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, "SKYPEOUT", _("SkypeOut"), FALSE, FALSE, FALSE); } else { status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "SKYPEOUT", _("SkypeOut"), FALSE, FALSE, FALSE); } types = g_list_append(types, status); //Offline people shouldn't have status messages status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "OFFLINE", _("Offline"), TRUE, TRUE, FALSE); types = g_list_append(types, status); return types; } void skype_get_all_buddy_info(PurpleBuddy *buddy) { skype_send_message_nowait("GET USER %s MOOD_TEXT", buddy->name); skype_send_message_nowait("GET USER %s ABOUT", buddy->name); skype_send_message_nowait("GET USER %s SEX", buddy->name); skype_send_message_nowait("GET USER %s BIRTHDAY", buddy->name); skype_send_message_nowait("GET USER %s LANGUAGE", buddy->name); skype_send_message_nowait("GET USER %s COUNTRY", buddy->name); skype_send_message_nowait("GET USER %s TIMEZONE", buddy->name); skype_send_message_nowait("GET USER %s PROVINCE", buddy->name); skype_send_message_nowait("GET USER %s CITY", buddy->name); skype_send_message_nowait("GET USER %s PHONE_MOBILE", buddy->name); skype_send_message_nowait("GET USER %s PHONE_OFFICE", buddy->name); skype_send_message_nowait("GET USER %s PHONE_HOME", buddy->name); skype_send_message_nowait("GET USER %s HOMEPAGE", buddy->name); skype_send_message_nowait("GET USER %s IS_VIDEO_CAPABLE", buddy->name); skype_send_message_nowait("GET USER %s PHONE_MOBILE", buddy->name); skype_send_message_nowait("GET USER %s HASCALLEQUIPMENT", buddy->name); skype_send_message_nowait("GET USER %s IS_VOICEMAIL_CAPABLE", buddy->name); skype_send_message_nowait("GET USER %s CAN_LEAVE_VM", buddy->name); skype_send_message_nowait("GET USER %s ISAUTHORIZED", buddy->name); skype_send_message_nowait("GET USER %s ISBLOCKED", buddy->name); skype_send_message_nowait("GET USER %s LASTONLINETIMESTAMP", buddy->name); skype_send_message_nowait("GET USER %s NROF_AUTHED_BUDDIES", buddy->name); } gboolean skype_set_buddies(PurpleAccount *acct) { char *friends_text; gchar **mood_buddyname; char **friends; char **full_friends_list; GSList *existing_friends; PurpleBuddy *buddy; SkypeBuddy *sbuddy; PurpleGroup *skype_group = NULL; PurpleGroup *skypeout_group = NULL; int i; friends_text = skype_send_message("GET AUTH_CONTACTS_PROFILES"); if (!friends_text || friends_text[0] == '\0') { g_free(friends_text); } else { full_friends_list = g_strsplit((strchr(friends_text, ' ')+1), ";", 0); g_free(friends_text); if (full_friends_list && full_friends_list[0]) { //in the format of: username;full name;phone;office phone;mobile phone; // online status;friendly name;voicemail;mood // (comma-seperated lines, usernames can have comma's) for (i=0; full_friends_list[i]; i+=8) { skype_debug_info("skype","Search buddy %s\n", full_friends_list[i]); buddy = purple_find_buddy(acct, full_friends_list[i]); if (buddy != NULL) { //the buddy was already in the list sbuddy = g_new0(SkypeBuddy, 1); sbuddy->handle = g_strdup(buddy->name); sbuddy->buddy = buddy; buddy->proto_data = sbuddy; skype_debug_info("skype","Buddy already in list: %s (%s)\n", buddy->name, full_friends_list[i]); } else { skype_debug_info("skype","Buddy not in list %s\n", full_friends_list[i]); buddy = purple_buddy_new(acct, full_friends_list[i], NULL); sbuddy = g_new0(SkypeBuddy, 1); sbuddy->handle = g_strdup(buddy->name); sbuddy->buddy = buddy; buddy->proto_data = sbuddy; //find out what group this buddy is in //for now, dump them into a default group, until skype tells us where they belong if (full_friends_list[i][0] == '+') { #ifndef __APPLE__ if (skypeout_group == NULL) { skypeout_group = purple_find_group("SkypeOut"); if (skypeout_group == NULL) { skypeout_group = purple_group_new("SkypeOut"); if (skypeout_group == NULL) skype_debug_error("skype", "SkypeOut group is NULL!\n"); purple_blist_add_group(skypeout_group, NULL); } } #endif purple_blist_add_buddy(buddy, NULL, skypeout_group, NULL); } else { #ifndef __APPLE__ if (skype_group == NULL) { skype_group = purple_find_group("Skype"); if (skype_group == NULL) { skype_group = purple_group_new("Skype"); if (skype_group == NULL) skype_debug_error("skype", "Skype group is NULL!\n"); purple_blist_add_group(skype_group, NULL); } } #endif purple_blist_add_buddy(buddy, NULL, skype_group, NULL); } } //in the format of: username;full name;phone;office phone;mobile phone; // online status;friendly name;voicemail;mood sbuddy->fullname = g_strdup(full_friends_list[i+1]); if (!purple_buddy_get_server_alias(buddy)) purple_blist_server_alias_buddy(buddy, sbuddy->fullname); sbuddy->phone_home = g_strdup(full_friends_list[i+2]); sbuddy->phone_office = g_strdup(full_friends_list[i+3]); sbuddy->phone_mobile = g_strdup(full_friends_list[i+4]); if (full_friends_list[i+6] && *full_friends_list[i+6]) purple_blist_server_alias_buddy(buddy, full_friends_list[i+6]); sbuddy->is_voicemail_capable = g_str_equal(full_friends_list[i+7], "TRUE")?TRUE:FALSE; if (full_friends_list[i+8] == NULL) { sbuddy->mood = g_strdup(""); } else { mood_buddyname = g_strsplit(full_friends_list[i+8], ",", 2); if (mood_buddyname[0] == NULL) { sbuddy->mood = g_strdup(""); full_friends_list[i+8] = NULL; } else { sbuddy->mood = g_strdup(mood_buddyname[0]); purple_util_chrreplace(sbuddy->mood, '\n', ' '); if (mood_buddyname[1] && mood_buddyname[1][0] != '\0' && full_friends_list[i+8]) { g_free(full_friends_list[i+8]); full_friends_list[i+8] = g_strdup(mood_buddyname[1]); } else { full_friends_list[i+8] = NULL; } } g_strfreev(mood_buddyname); } //Do this one last to update buddy list if (g_str_equal(full_friends_list[i+5], "SKYPEOUT") && (!purple_account_get_bool(acct, "skypeout_online", TRUE))) { purple_prpl_got_user_status(acct, buddy->name, "OFFLINE", NULL); } else { purple_prpl_got_user_status(acct, buddy->name, full_friends_list[i+5], NULL); } skype_send_message_nowait("GET USER %s IS_VIDEO_CAPABLE", buddy->name); } g_strfreev(full_friends_list); skype_put_buddies_in_groups(); return FALSE; } g_strfreev(full_friends_list); } friends_text = skype_send_message("SEARCH FRIENDS"); if (strlen(friends_text) == 0) { g_free(friends_text); return FALSE; } //skip first word (should be USERS) and seperate by comma-space friends = g_strsplit((strchr(friends_text, ' ')+1), ", ", 0); g_free(friends_text); if (friends == NULL || friends[0] == NULL) { return FALSE; } //Remove from libpurple buddy list if not in skype friends list existing_friends = purple_find_buddies(acct, NULL); g_slist_foreach(existing_friends, (GFunc)skype_slist_friend_check, friends); //grab the list of buddy's again since they could have changed existing_friends = purple_find_buddies(acct, NULL); for (i=0; friends[i]; i++) { //If already in list, dont recreate, reuse skype_debug_info("skype", "Searching for friend %s\n", friends[i]); buddy = purple_find_buddy(acct, friends[i]); if (buddy != NULL) { //the buddy was already in the list buddy->proto_data = skype_buddy_new(buddy); skype_debug_info("skype","Buddy already in list: %s (%s)\n", buddy->name, friends[i]); } else { skype_debug_info("skype","Buddy not in list %s\n", friends[i]); buddy = purple_buddy_new(acct, friends[i], NULL); buddy->proto_data = skype_buddy_new(buddy); //find out what group this buddy is in //for now, dump them into a default group, until skype tells us where they belong if (friends[i][0] == '+') { #ifndef __APPLE__ if (skypeout_group == NULL) { skypeout_group = purple_find_group("SkypeOut"); if (skypeout_group == NULL) { skypeout_group = purple_group_new("SkypeOut"); purple_blist_add_group(skypeout_group, NULL); } } #endif purple_blist_add_buddy(buddy, NULL, skypeout_group, NULL); } else { #ifndef __APPLE__ if (skype_group == NULL) { skype_group = purple_find_group("Skype"); if (skype_group == NULL) { skype_group = purple_group_new("Skype"); purple_blist_add_group(skype_group, NULL); } } #endif purple_blist_add_buddy(buddy, NULL, skype_group, NULL); } } skype_update_buddy_status(buddy); skype_update_buddy_alias(buddy); purple_prpl_got_user_idle(acct, buddy->name, FALSE, 0); //skype_update_buddy_icon(buddy); } //special case, if we're on our own buddy list if ((buddy = purple_find_buddy(acct, skype_get_account_username(acct)))) { skype_update_buddy_status(buddy); skype_update_buddy_alias(buddy); purple_prpl_got_user_idle(acct, buddy->name, FALSE, 0); //skype_update_buddy_icon(buddy); } skype_debug_info("skype", "Friends Count: %d\n", i); g_strfreev(friends); skype_put_buddies_in_groups(); return FALSE; } void skype_slist_friend_check(gpointer buddy_pointer, gpointer friends_pointer) { int i; PurpleBuddy *buddy = (PurpleBuddy *)buddy_pointer; char **friends = (char **)friends_pointer; if (g_str_equal(buddy->name, skype_get_account_username(buddy->account))) { //we must have put ourselves on our own list in pidgin, ignore return; } for(i=0; friends[i]; i++) { if (strlen(friends[i]) == 0) continue; if (g_str_equal(buddy->name, friends[i])) return; } skype_debug_info("skype", "removing buddy %d with name %s\n", buddy, buddy->name); purple_blist_remove_buddy(buddy); } int skype_slist_friend_search(gconstpointer buddy_pointer, gconstpointer buddyname_pointer) { PurpleBuddy *buddy; gchar *buddyname; if (buddy_pointer == NULL) return -1; if (buddyname_pointer == NULL) return 1; buddy = (PurpleBuddy *)buddy_pointer; buddyname = (gchar *)buddyname_pointer; if (buddy->name == NULL) return -1; return strcmp(buddy->name, buddyname); } //create the memmem() function for platforms that dont support it #if __APPLE__ || _WIN32 #ifndef _LIBC # define __builtin_expect(expr, val) (expr) #endif #undef memmem void *memmem (const void *, size_t, const void *, size_t); /* Return the first occurrence of NEEDLE in HAYSTACK. */ void * memmem (haystack, haystack_len, needle, needle_len) const void *haystack; size_t haystack_len; const void *needle; size_t needle_len; { const char *begin; const char *const last_possible = (const char *) haystack + haystack_len - needle_len; if (needle_len == 0) /* The first occurrence of the empty string is deemed to occur at the beginning of the string. */ return (void *) haystack; /* Sanity check, otherwise the loop might search through the whole memory. */ if (__builtin_expect (haystack_len < needle_len, 0)) return NULL; for (begin = (const char *) haystack; begin <= last_possible; ++begin) if (begin[0] == ((const char *) needle)[0] && !memcmp ((const void *) &begin[1], (const void *) ((const char *) needle + 1), needle_len - 1)) return (void *) begin; return NULL; } #endif void skype_got_buddy_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { PurpleBuddy *buddy = user_data; PurpleAccount *acct = buddy->account; if (error_message || !len || !url_text) return; purple_buddy_icons_set_for_user(acct, buddy->name, g_memdup(url_text,len), len, NULL); } void skype_update_buddy_icon(PurpleBuddy *buddy) { PurpleAccount *acct; gchar *filename = NULL; gchar *new_filename = NULL; gchar *image_data = NULL; gsize image_data_len = 0; gchar *ret; int fh; GError *error; /* -1 unknown, 0 none, 1 skype api, 2 dbb file, 3 url */ static gint api_supports_avatar = #ifndef SKYPENET # ifdef _WIN32 1; # else # ifdef __APPLE__ -1; //OSX only supports GET AVATAR in beta # else 2; # endif # endif #else 3; #endif if (api_supports_avatar == 0) { return; } skype_debug_info("skype", "Updating buddy icon for %s (%d)\n", buddy->name, api_supports_avatar); acct = purple_buddy_get_account(buddy); #ifndef INSTANTBIRD if (api_supports_avatar == 1 || api_supports_avatar == -1) { fh = g_file_open_tmp("skypeXXXXXX", &filename, &error); close(fh); if (filename != NULL) { new_filename = g_strconcat(filename, ".jpg", NULL); g_rename(filename, new_filename); ret = skype_send_message("GET USER %s AVATAR 1 %s", buddy->name, new_filename); if (strlen(ret) == 0) { skype_debug_warning("skype", "Error: Protocol doesn't suppot AVATAR\n"); } else { if (g_file_get_contents(new_filename, &image_data, &image_data_len, NULL)) { api_supports_avatar = 1; purple_buddy_icons_set_for_user(acct, buddy->name, image_data, image_data_len, NULL); } } g_free(ret); g_unlink(new_filename); g_free(filename); g_free(new_filename); } else { skype_debug_warning("skype", "Error making temp file %s\n", error->message); g_error_free(error); } } if (api_supports_avatar == 2 || api_supports_avatar == -1) { const gchar *userfiles[] = {"user256", "user1024", "user4096", "user16384", "user32768", "user65536", "profile256", "profile1024", "profile4096", "profile16384", "profile32768", NULL}; char *username = g_strdup_printf("\x03\x10%s", buddy->name); for (fh = 0; userfiles[fh]; fh++) { #ifdef _WIN32 filename = g_strconcat(purple_home_dir(), "\\Skype\\", acct->username, "\\", userfiles[fh], ".dbb", NULL); #else #ifdef __APPLE__ filename = g_strconcat(purple_home_dir(), "/Library/Application Support/Skype/", acct->username, "/", userfiles[fh], ".dbb", NULL); #else filename = g_strconcat(purple_home_dir(), "/.Skype/", acct->username, "/", userfiles[fh], ".dbb", NULL); #endif #endif if (g_file_get_contents(filename, &image_data, &image_data_len, NULL)) { char *start = (char *)memmem(image_data, image_data_len, username, strlen(username)+1); api_supports_avatar = 2; if (start != NULL) { char *next = image_data; char *last = next; //find last index of l33l while ((next = (char *)memmem(next+4, start-next-4, "l33l", 4))) { last = next; } start = last; if (start != NULL) { char *img_start; //find end of l33l block char *end = (char *)memmem(start+4, image_data+image_data_len-start-4, "l33l", 4); if (!end) end = image_data+image_data_len; //look for start of JPEG block img_start = (char *)memmem(start, end-start, "\xFF\xD8", 2); if (img_start) { //look for end of JPEG block char *img_end = (char *)memmem(img_start, end-img_start, "\xFF\xD9", 2); if (img_end) { image_data_len = img_end - img_start + 2; purple_buddy_icons_set_for_user(acct, buddy->name, g_memdup(img_start, image_data_len), image_data_len, NULL); } } } } g_free(image_data); } g_free(filename); } g_free(username); } #else /* ifdef INSTANTBIRD */ api_supports_avatar = 3; #endif if (api_supports_avatar == 3 || api_supports_avatar == -1) { filename = g_strdup_printf("http://api.skype.com/users/%s/profile/avatar", purple_url_encode(buddy->name)); purple_util_fetch_url(filename, TRUE, NULL, FALSE, skype_got_buddy_icon_cb, buddy); g_free(filename); api_supports_avatar = 3; return; } } const char * skype_list_emblem(PurpleBuddy *buddy) { PurpleAccount *acct; acct = purple_buddy_get_account(buddy); SkypeBuddy *sbuddy; time_t now; struct tm *today_tm; if (buddy->name[0] == '+') { return "mobile"; } if (buddy && buddy->proto_data) { sbuddy = buddy->proto_data; if (sbuddy->birthday) { now = time(NULL); today_tm = localtime(&now); if (sbuddy->birthday->tm_mday == today_tm->tm_mday && sbuddy->birthday->tm_mon == today_tm->tm_mon && sbuddy->birthday->tm_year == today_tm->tm_year) { return "birthday"; } } if (purple_account_get_bool(acct, "video_capable", TRUE) && sbuddy->is_video_capable) { return "video"; } } return NULL; } void skype_update_buddy_alias(PurpleBuddy *buddy) { skype_send_message_nowait("GET USER %s DISPLAYNAME", buddy->name); skype_send_message_nowait("GET USER %s FULLNAME", buddy->name); } gboolean skype_update_buddy_status(PurpleBuddy *buddy) { PurpleAccount *acct; if (buddy->name[0] == '+') { return FALSE; } acct = purple_buddy_get_account(buddy); if (purple_account_is_connected(acct) == FALSE) { return FALSE; } skype_send_message_nowait("GET USER %s ONLINESTATUS", buddy->name); skype_send_message_nowait("GET USER %s MOOD_TEXT", buddy->name); skype_send_message_nowait("GET USER %s RICH_MOOD_TEXT", buddy->name); skype_send_message_nowait("GET USER %s IS_VIDEO_CAPABLE", buddy->name); /* if this function was called from another thread, don't loop over it */ return FALSE; } gboolean skype_login_cb(gpointer acct) { if (!is_skype_running()) { purple_timeout_add_seconds(5, skype_login_cb, acct); return FALSE; } skype_login(acct); return FALSE; } static guint retry_count = 0; void skype_login(PurpleAccount *acct) { PurpleConnection *gc; const gchar *skype_path; gboolean skype_started = FALSE; gchar *errormsg; PurpleAccount *test_account; if (acct == NULL) { return; } if (purple_get_blist() == NULL) { return; } //set the account, with misleading function name :) skype_get_account(acct); gc = purple_account_get_connection(acct); if (gc == NULL) return; gc->flags = PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_IMAGES; /* 1. connect to server */ // Work out if a skype connection is already running //purple_account_is_connected(acct) test_account = find_acct(purple_plugin_get_id(this_plugin), NULL); if (test_account && test_account != acct) { errormsg = g_strconcat("\n",_("Only one Skype account allowed"), NULL); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errormsg); g_free(errormsg); return; } retry_count = 0; purple_connection_update_progress(gc, _("Connecting"), 0, 5); if (!skype_connect()) { #ifndef INSTANTBIRD if (purple_account_get_bool(acct, "skype_autostart", TRUE)) { skype_debug_info("skype", "Should I start Skype?\n"); if (!is_skype_running()) { skype_debug_info("skype", "Yes, start Skype\n"); if ((skype_path = purple_account_get_string(acct, "skype_path", NULL)) && *skype_path) { skype_started = g_spawn_command_line_async(skype_path, NULL); } else { skype_started = exec_skype(); } if (skype_started) { purple_timeout_add_seconds(20, skype_login_cb, acct); return; } return; } } #endif purple_timeout_add_seconds(10, (GSourceFunc) skype_login_retry, acct); return; } purple_timeout_add(1, (GSourceFunc) skype_login_part2, acct); } gboolean skype_login_retry(PurpleAccount *acct) { gchar *errormsg; PurpleConnection *gc; gc = purple_account_get_connection(acct); if (retry_count++ == 3) { errormsg = g_strconcat("\n", _("Could not connect to Skype process.\nSkype not running?"), NULL); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errormsg); g_free(errormsg); return FALSE; } if (!skype_connect()) return TRUE; purple_timeout_add(1, (GSourceFunc) skype_login_part2, acct); return FALSE; } gboolean skype_login_part2(PurpleAccount *acct) { gchar *reply; PurpleConnection *gc; static guint missedmessagestimout = 0; gc = purple_account_get_connection(acct); purple_connection_update_progress(gc, _("Authorizing"), 1, /* which connection step this is */ 5); /* total number of steps */ #if SKYPENET || ! __APPLE__ #ifdef __APPLE__ reply = skype_send_message("NAME Adium"); #else reply = skype_send_message("NAME %s", g_get_application_name()); #endif if (reply == NULL || strlen(reply) == 0) { purple_timeout_add_seconds(10, (GSourceFunc) skype_login_retry, acct); return FALSE; } if (g_str_equal(reply, "CONNSTATUS OFFLINE")) { //this happens if we connect before skype has connected to the network purple_timeout_add_seconds(1, skype_login_cb, acct); g_free(reply); return FALSE; } g_free(reply); #endif purple_connection_update_progress(gc, _("Initializing"), 2, 5); reply = skype_send_message("PROTOCOL 7"); if (reply == NULL || strlen(reply) == 0) { purple_timeout_add_seconds(10, (GSourceFunc) skype_login_retry, acct); return FALSE; } sscanf(reply, "PROTOCOL %d", &protocol_version); g_free(reply); purple_connection_update_progress(gc, _("Hide Skype"), 3, 5); skype_silence(NULL, NULL); purple_connection_update_progress(gc, _("Connected"), 4, 5); skype_get_account_alias(acct); skype_get_account_username(acct); if (purple_account_get_bool(acct, "skype_sync", TRUE)) skype_set_status(acct, purple_account_get_active_status(acct)); skype_send_message_nowait("CREATE APPLICATION libpurple_typing"); purple_connection_set_state(gc, PURPLE_CONNECTED); //sync buddies after everything else has finished loading purple_timeout_add_seconds(1, (GSourceFunc)skype_set_buddies, (gpointer)acct); if (TRUE) { gint version_int = 0; gchar *version = skype_send_message("GET SKYPEVERSION"); sscanf(version, "%*s %d", &version_int); skype_debug_info("skype", "version [%s] - %d\n", version, version_int); g_free(version); if (version_int >= 5 && !missedmessagestimout) { missedmessagestimout = purple_timeout_add_seconds(10, (GSourceFunc)skype_check_missedmessages, (gpointer)acct); skype_debug_info("skype", "installing skype_check_missedmessages\n"); } else if (version_int < 5 && missedmessagestimout) { purple_timeout_remove(missedmessagestimout); missedmessagestimout = 0; skype_debug_info("skype", "removing skype_check_missedmessages\n"); } } return FALSE; } const char * skype_get_account_username(PurpleAccount *acct) { #ifdef SKYPENET if (acct == NULL) return NULL; return acct->username; #else char *ret; static char *username = NULL; if (username != NULL) return username; if (!acct) return "Skype"; ret = skype_send_message("GET CURRENTUSERHANDLE"); if (!ret || !strlen(ret)) { g_free(ret); return NULL; } username = g_strdup(&ret[18]); g_free(ret); if (acct && !g_str_equal(acct->username, username)) { skype_debug_info("skype", "Setting username to %s\n", username); purple_account_set_username(acct, username); } return username; #endif } void skype_get_account_alias(PurpleAccount *acct) { char *ret; char *alias; alias = (gchar *)purple_account_get_alias(acct); if (alias != NULL && alias[0] != '\0') { //Alias already set, dont do anything return; } ret = skype_send_message("GET PROFILE FULLNAME"); alias = g_strdup(&ret[17]); g_free(ret); purple_account_set_alias(acct, alias); g_free(alias); } void skype_close(PurpleConnection *gc) { GSList *buddies, *buddieslist; skype_debug_info("skype", "logging out\n"); //Close notification boxes (if any) purple_request_close_with_handle(gc); if (gc && purple_account_get_bool(gc->account, "skype_sync", TRUE)) skype_send_message_nowait("SET USERSTATUS OFFLINE"); skype_send_message_nowait("SET SILENT_MODE OFF"); skype_debug_info("skype", "disconnecting...\n"); skype_disconnect(); if (gc) { buddieslist = purple_find_buddies(gc->account, NULL); //make all buddies offline for(buddies = buddieslist; buddies != NULL; buddies = buddies->next) { PurpleBuddy *buddy = buddies->data; purple_prpl_got_user_status(buddy->account, buddy->name, "OFFLINE", NULL); } g_slist_free(buddieslist); } g_hash_table_destroy(messages_table); messages_table = NULL; g_hash_table_destroy(groups_table); groups_table = NULL; g_hash_table_destroy(chat_link_table); chat_link_table = NULL; g_hash_table_destroy(sms_convo_link_table); sms_convo_link_table = NULL; #ifndef INSTANTBIRD g_hash_table_destroy(call_media_hash); call_media_hash = NULL; #endif } int skype_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags) { char *stripped; char *chat_id; PurpleConversation *conv; conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, purple_connection_get_account(gc)); if (who[0] == '+' && conv && purple_conversation_get_data(conv, "sms_msg")) { return skype_send_sms(gc, who, message, flags); } stripped = purple_markup_strip_html(message); if (protocol_version < 5 || conv == NULL || purple_conversation_get_data(conv, "chat_id") == NULL) { skype_send_message_nowait("MESSAGE %s %s", who, stripped); } else { chat_id = purple_conversation_get_data(conv, "chat_id"); skype_send_message_nowait("CHATMESSAGE %s %s", chat_id, stripped); } #ifdef USE_SKYPE_SENT return 0; #else return 1; #endif } unsigned int skype_send_typing(PurpleConnection *gc, const gchar *name, PurpleTypingState state) { gchar *string_state = NULL, *temp = NULL; PurpleAccount *account; account = purple_connection_get_account(gc); //Don't send typing messages to SkypeOut if (name[0] == '+') return 0; switch(state) { case PURPLE_NOT_TYPING: string_state = "PURPLE_NOT_TYPING"; break; case PURPLE_TYPING: string_state = "PURPLE_TYPING"; break; case PURPLE_TYPED: string_state = "PURPLE_TYPED"; break; default: string_state = ""; break; } temp = g_strconcat("stream-", name, NULL); if (purple_account_get_string(account, temp, NULL)) { skype_send_message_nowait( "ALTER APPLICATION libpurple_typing DATAGRAM %s:%s %s", name, purple_account_get_string(account, temp, "1"), string_state); } else { //lets be dumb and try to connect without getting a stream skype_send_message_nowait("CREATE APPLICATION libpurple_typing"); skype_send_message_nowait( "ALTER APPLICATION libpurple_typing CONNECT %s", name); } g_free(temp); return 4; } gchar * timestamp_to_datetime(time_t timestamp) { return g_strdup(purple_date_format_long(localtime(×tamp))); } void set_skype_buddy_attribute(SkypeBuddy *sbuddy, const gchar *skype_buddy_property, const gchar *value) { if (sbuddy == NULL) return; if (skype_buddy_property == NULL) return; if (g_str_equal(skype_buddy_property, "FULLNAME")) { g_free(sbuddy->fullname); sbuddy->fullname = NULL; if (value && strlen(value)) sbuddy->fullname = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "MOOD_TEXT")) { g_free(sbuddy->mood); sbuddy->mood = NULL; if (value) { sbuddy->mood = g_strdup(value); purple_util_chrreplace(sbuddy->mood, '\n', ' '); } } else if (g_str_equal(skype_buddy_property, "BIRTHDAY")) { g_free(sbuddy->birthday); sbuddy->birthday = NULL; if (value && strlen(value) && !g_str_equal(value, "0")) { sbuddy->birthday = g_new(struct tm, 1); purple_str_to_time(value, FALSE, sbuddy->birthday, NULL, NULL); } } else if (g_str_equal(skype_buddy_property, "SEX")) { g_free(sbuddy->gender); sbuddy->gender = NULL; if (value && *value && !g_str_equal(value, "UNKNOWN")) sbuddy->gender = g_ascii_strdown(value, -1); } else if (g_str_equal(skype_buddy_property, "LANGUAGE")) { g_free(sbuddy->language); sbuddy->language = NULL; if (value && strlen(value)) sbuddy->language = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "COUNTRY")) { g_free(sbuddy->country); sbuddy->country = NULL; if (value && strlen(value)) sbuddy->country = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "IS_VIDEO_CAPABLE")) { if (value && strlen(value) && g_str_equal(value, "TRUE")) sbuddy->is_video_capable = TRUE; else sbuddy->is_video_capable = FALSE; } else if (g_str_equal(skype_buddy_property, "ISAUTHORIZED")) { if (value && strlen(value) && g_str_equal(value, "TRUE")) sbuddy->is_authorized = TRUE; else sbuddy->is_authorized = FALSE; } else if (g_str_equal(skype_buddy_property, "ISBLOCKED")) { if (value && strlen(value) && g_str_equal(value, "TRUE")) sbuddy->is_blocked = TRUE; else sbuddy->is_blocked = FALSE; } else if (g_str_equal(skype_buddy_property, "LASTONLINETIMESTAMP")) { sbuddy->last_online = 0; if (value) sbuddy->last_online = (time_t) atoi(value); } else if (g_str_equal(skype_buddy_property, "TIMEZONE")) { sbuddy->timezone_offset = 0; if (value) sbuddy->timezone_offset = (g_ascii_strtod(value, NULL) / 3600) - 24; } else if (g_str_equal(skype_buddy_property, "NROF_AUTHED_BUDDIES")) { sbuddy->number_of_buddies = 0; if (value) sbuddy->number_of_buddies = g_ascii_strtoull(value, NULL, 10); } else if (g_str_equal(skype_buddy_property, "ABOUT")) { g_free(sbuddy->about); sbuddy->about = NULL; if (value && strlen(value)) { sbuddy->about = g_strdup(value); } } else if (g_str_equal(skype_buddy_property, "PROVINCE")) { g_free(sbuddy->province); sbuddy->province = NULL; if (value && strlen(value)) { sbuddy->province = g_strdup(value); } } else if (g_str_equal(skype_buddy_property, "CITY")) { g_free(sbuddy->city); sbuddy->city = NULL; if (value && strlen(value)) sbuddy->city = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "PHONE_HOME")) { g_free(sbuddy->phone_home); sbuddy->phone_home = NULL; if (value && strlen(value) && !g_str_equal(value, "N/A")) sbuddy->phone_home = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "PHONE_OFFICE")) { g_free(sbuddy->phone_office); sbuddy->phone_office = NULL; if (value && strlen(value) && !g_str_equal(value, "N/A")) sbuddy->phone_office = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "PHONE_MOBILE")) { g_free(sbuddy->phone_mobile); sbuddy->phone_mobile = NULL; if (value && strlen(value) && !g_str_equal(value, "N/A")) sbuddy->phone_mobile = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "HOMEPAGE")) { g_free(sbuddy->homepage); sbuddy->homepage = NULL; if (value && strlen(value)) sbuddy->homepage = g_strdup(value); } else if (g_str_equal(skype_buddy_property, "HASCALLEQUIPMENT")) { if (value && strlen(value) && g_str_equal(value, "TRUE")) sbuddy->has_call_equipment = TRUE; else sbuddy->has_call_equipment = FALSE; } else if (g_str_equal(skype_buddy_property, "IS_VOICEMAIL_CAPABLE")) { if (value && strlen(value) && g_str_equal(value, "TRUE")) sbuddy->is_voicemail_capable = TRUE; else sbuddy->is_voicemail_capable = FALSE; } else if (g_str_equal(skype_buddy_property, "CAN_LEAVE_VM")) { if (value && strlen(value) && g_str_equal(value, "TRUE")) sbuddy->can_leave_voicemail = TRUE; else sbuddy->can_leave_voicemail = FALSE; } } SkypeBuddy * skype_buddy_new(PurpleBuddy *buddy) { SkypeBuddy *newbuddy = g_new0(SkypeBuddy, 1); newbuddy->buddy = buddy; buddy->proto_data = newbuddy; //this bit used in Get Info newbuddy->handle = g_strdup(buddy->name); skype_send_message_nowait("GET USER %s FULLNAME", buddy->name); if (buddy->name[0] != '+') { skype_send_message_nowait("GET USER %s ONLINESTATUS", buddy->name); skype_send_message_nowait("GET USER %s IS_VIDEO_CAPABLE", buddy->name); } return newbuddy; } void skype_buddy_free(PurpleBuddy *buddy) { SkypeBuddy *sbuddy; gchar *temp; g_return_if_fail(buddy != NULL); if (buddy->proto_data) { sbuddy = buddy->proto_data; buddy->proto_data = NULL; g_free(sbuddy->handle); g_free(sbuddy->fullname); g_free(sbuddy->mood); g_free(sbuddy->birthday); g_free(sbuddy->gender); g_free(sbuddy->language); g_free(sbuddy->country); g_free(sbuddy->about); g_free(sbuddy->province); g_free(sbuddy->city); g_free(sbuddy->phone_home); g_free(sbuddy->phone_office); g_free(sbuddy->phone_mobile); g_free(sbuddy->homepage); g_free(sbuddy); } if (buddy->name && buddy->account) { temp = g_strdup_printf("stream-%s", buddy->name); if (purple_account_get_string(buddy->account, temp, NULL)) purple_account_set_string(buddy->account, temp, NULL); g_free(temp); } } gboolean skype_display_buddy_info(PurpleBuddy *buddy) { PurpleNotifyUserInfo *user_info; SkypeBuddy *sbuddy; gchar *timezone = NULL; gchar *buddy_count = NULL; gchar *lastonline = NULL; if (buddy && buddy->proto_data) { #define _SKYPE_USER_INFO(prop, key) if (prop) \ purple_notify_user_info_add_pair(user_info, _(key), (prop)); sbuddy = buddy->proto_data; user_info = purple_notify_user_info_new(); _SKYPE_USER_INFO(sbuddy->handle, "Skype Name"); _SKYPE_USER_INFO(sbuddy->fullname, "Full Name"); if (strlen(sbuddy->mood) != 0) _SKYPE_USER_INFO(sbuddy->mood, "Mood Text"); purple_notify_user_info_add_section_break(user_info); if (sbuddy->birthday != 0) _SKYPE_USER_INFO(purple_date_format_short(sbuddy->birthday),"Birthday"); _SKYPE_USER_INFO(sbuddy->gender, "Gender"); _SKYPE_USER_INFO(sbuddy->language, "Preferred Language"); _SKYPE_USER_INFO(sbuddy->country, "Country"); if (sbuddy->timezone_offset) { timezone = g_strdup_printf("UMT %+.1f", sbuddy->timezone_offset); _SKYPE_USER_INFO(timezone, "Timezone"); } _SKYPE_USER_INFO(sbuddy->province, "Province"); _SKYPE_USER_INFO(sbuddy->city, "City"); _SKYPE_USER_INFO(sbuddy->phone_mobile, "Phone Mobile"); _SKYPE_USER_INFO(sbuddy->phone_office, "Phone Office"); _SKYPE_USER_INFO(sbuddy->phone_home, "Phone Home"); _SKYPE_USER_INFO(sbuddy->homepage, "Homepage"); purple_notify_user_info_add_section_break(user_info); _SKYPE_USER_INFO(sbuddy->is_video_capable?_("Yes"):_("No"), "Video Capable"); _SKYPE_USER_INFO(sbuddy->has_call_equipment?_("Yes"):_("No"), "Call Equipment"); _SKYPE_USER_INFO(sbuddy->is_voicemail_capable?_("Yes"):_("No"), "VoiceMail Capable"); _SKYPE_USER_INFO(sbuddy->can_leave_voicemail?_("Yes"):_("No"), "Can Leave VoiceMail"); _SKYPE_USER_INFO(sbuddy->is_authorized?_("Yes"):_("No"), "Authorization Granted"); _SKYPE_USER_INFO(sbuddy->is_blocked?_("Yes"):_("No"), "Blocked"); buddy_count = g_strdup_printf("%d", sbuddy->number_of_buddies); _SKYPE_USER_INFO(buddy_count, "Number of buddies"); lastonline = timestamp_to_datetime(sbuddy->last_online); _SKYPE_USER_INFO(lastonline, "Last online"); purple_notify_user_info_add_section_break(user_info); _SKYPE_USER_INFO(sbuddy->about, "About"); purple_notify_userinfo(purple_account_get_connection(buddy->account), buddy->name, user_info, (PurpleNotifyCloseCallback)purple_notify_user_info_destroy, user_info); g_free(timezone); g_free(buddy_count); g_free(lastonline); } return FALSE; } void skype_get_info(PurpleConnection *gc, const gchar *username) { PurpleBuddy *buddy; buddy = purple_find_buddy(gc->account, username); if (buddy) { skype_get_all_buddy_info(buddy); purple_timeout_add_seconds(2, (GSourceFunc)skype_display_buddy_info, buddy); } } gchar * skype_get_user_info(const gchar *username, const gchar *property) { gchar *outstr; gchar *return_str; outstr = skype_send_message("GET USER %s %s", username, property); if (strlen(outstr) == 0) return outstr; return_str = g_strdup(&outstr[7+strlen(username)+strlen(property)]); g_free(outstr); /* skype_debug_info("skype", "User %s's %s is %s", username, property, return_str); */ if (return_str == NULL) return NULL; return return_str; } void skype_set_status(PurpleAccount *account, PurpleStatus *status) { PurpleStatusType *type; const char *message; type = purple_status_get_type(status); skype_send_message_nowait("SET USERSTATUS %s", purple_status_type_get_id(type)); message = purple_status_get_attr_string(status, "message"); if (message == NULL) message = ""; else message = purple_markup_strip_html(message); if (purple_account_get_bool(account, "update_status", TRUE)) skype_send_message_nowait("SET PROFILE MOOD_TEXT %s", message); } void skype_set_idle(PurpleConnection *gc, int time) { skype_send_message_nowait("SET AUTOAWAY OFF"); skype_send_message_nowait("RESETIDLETIMER"); } void skype_put_buddies_in_groups() { skype_send_message_nowait("SEARCH GROUPS CUSTOM"); } gboolean groups_table_find_group(gpointer key, gpointer value, gpointer user_data) { gchar *group_name = user_data; gint group_number = GPOINTER_TO_INT(key); PurpleGroup *group = value; if (g_str_equal(group_name, group->name)) { purple_blist_node_set_int(&group->node, "skype_group_number", group_number); return TRUE; } return FALSE; } int skype_find_group_with_name(const char *group_name_in) { PurpleGroup *purple_group; gint group_number; purple_group = purple_find_group(group_name_in); if (purple_group != NULL) { group_number = purple_blist_node_get_int(&purple_group->node, "skype_group_number"); if (group_number) return group_number; } if (groups_table == NULL) { skype_send_message_nowait("SEARCH GROUPS CUSTOM"); return -1; } purple_group = g_hash_table_find(groups_table, groups_table_find_group, (gpointer)group_name_in); if (purple_group == NULL) { return 0; } return purple_blist_node_get_int(&purple_group->node, "skype_group_number"); } //use this to try and regroup a buddy after a few seconds struct _cheat_skype_group_buddy_struct { PurpleConnection *gc; gchar *who; gchar *old_group; gchar *new_group; }; gboolean skype_group_buddy_timeout(struct _cheat_skype_group_buddy_struct *cheat); gboolean skype_group_buddy_timeout(struct _cheat_skype_group_buddy_struct *cheat) { if (!cheat) return FALSE; skype_group_buddy(cheat->gc, cheat->who, cheat->old_group, cheat->new_group); g_free(cheat->who); g_free(cheat->old_group); g_free(cheat->new_group); g_free(cheat); return FALSE; } void skype_group_buddy(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group) { int group_number = 0; //add to new group group_number = skype_find_group_with_name(new_group); if (group_number <= 0) { struct _cheat_skype_group_buddy_struct *cheat; if (group_number == 0) skype_send_message_nowait("CREATE GROUP %s", new_group); cheat = g_new(struct _cheat_skype_group_buddy_struct, 1); cheat->gc = gc; cheat->who = g_strdup(who); cheat->old_group = old_group?g_strdup(old_group):NULL; cheat->new_group = new_group?g_strdup(new_group):NULL; purple_timeout_add_seconds(5, (GSourceFunc)skype_group_buddy_timeout, cheat); return; } skype_send_message_nowait("ALTER GROUP %d ADDUSER %s", group_number, who); if (old_group == NULL) return; //remove from old group group_number = skype_find_group_with_name(old_group); if (!group_number) return; skype_send_message_nowait("ALTER GROUP %d REMOVEUSER %s", group_number, who); } void skype_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) { int group_number = 0; group_number = skype_find_group_with_name(old_name); if (group_number <= 0) { //TODO Safely rename group back to the old group name to indicate failure return; } skype_send_message_nowait("SET GROUP %d DISPLAYNAME %s", group_number, group->name); } void skype_remove_group(PurpleConnection *gc, PurpleGroup *group) { int group_number = 0; group_number = skype_find_group_with_name(group->name); if (group_number <= 0) return; skype_send_message_nowait("DELETE GROUP %d", group_number); } void skype_add_buddy_with_invite(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) { skype_send_message_nowait("SET USER %s BUDDYSTATUS 2 %s", buddy->name, message); if (buddy->alias == NULL || strlen(buddy->alias) == 0) skype_update_buddy_alias(buddy); else skype_alias_buddy(gc, buddy->name, buddy->alias); if (group && group->name) { skype_group_buddy(gc, buddy->name, NULL, group->name); } skype_add_permit(gc, buddy->name); skype_rem_deny(gc, buddy->name); skype_update_buddy_status(buddy); } void skype_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { skype_add_buddy_with_invite(gc, buddy, group, _("Please authorize me so I can add you to my buddy list.")); } void skype_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { // Check that the buddy doesn't exist in other groups (ie, is the UI being weird) GSList *buddies, *current; PurpleBuddy *otherbuddy; PurpleGroup *othergroup; int group_number; buddies = purple_find_buddies(buddy->account, buddy->name); if (buddies == NULL) return; for(current = buddies; current; current = g_slist_next(current)) { otherbuddy = current->data; if (otherbuddy == NULL) continue; othergroup = purple_buddy_get_group(otherbuddy); if (othergroup == NULL) continue; if (!g_str_equal(othergroup->name, group->name)) { //If we're here, we've found the buddy in another group, just remove them from this group for now group_number = skype_find_group_with_name(group->name); if (group_number) skype_send_message_nowait("ALTER GROUP %d REMOVEUSER %s", group_number, buddy->name); g_slist_free(buddies); return; } } g_slist_free(buddies); //If we're here, the buddy was only in that one group, so remove them completely skype_send_message_nowait("SET USER %s BUDDYSTATUS 1", buddy->name); } void skype_add_deny(PurpleConnection *gc, const char *who) { skype_send_message_nowait("SET USER %s ISBLOCKED TRUE", who); } void skype_rem_deny(PurpleConnection *gc, const char *who) { skype_send_message_nowait("SET USER %s ISBLOCKED FALSE", who); skype_send_message_nowait("SET USER %s BUDDYSTATUS 2", who); } void skype_add_permit(PurpleConnection *gc, const char *who) { skype_send_message_nowait("SET USER %s ISAUTHORIZED TRUE", who); } void skype_rem_permit(PurpleConnection *gc, const char *who) { skype_send_message_nowait("SET USER %s ISAUTHORIZED FALSE", who); } static time_t last_ping = 0; gboolean skype_check_keepalive(PurpleConnection *gc) { if (last_pong < last_ping) purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("\nSkype not responding")); return FALSE; } void skype_keepalive(PurpleConnection *gc) { last_ping = time(NULL); skype_send_message_nowait("PING"); purple_timeout_add_seconds(20, (GSourceFunc)skype_check_keepalive, gc); } void skype_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) { gchar *path; if (img != NULL) { path = g_build_filename(purple_buddy_icons_get_cache_dir(), purple_imgstore_get_filename(img), NULL); skype_send_message_nowait("SET AVATAR 1 %s:1", path); g_free(path); } else skype_send_message_nowait("SET AVATAR 1"); } char * skype_status_text(PurpleBuddy *buddy) { char *mood_text; PurplePresence *presence; PurpleStatus *status; PurpleStatusType *type; SkypeBuddy *sbuddy = buddy->proto_data; if (sbuddy && sbuddy->mood != NULL && strlen(sbuddy->mood)) return skype_strdup_withhtml(sbuddy->mood); if (sbuddy == NULL || sbuddy->mood == NULL) { if (buddy->name[0] != '+') skype_send_message_nowait("GET USER %s MOOD_TEXT", buddy->name); return NULL; } //If we're at this point, they don't have a mood. //Use away status instead presence = purple_buddy_get_presence(buddy); if (presence == NULL) return NULL; status = purple_presence_get_active_status(presence); if (status == NULL) return NULL; type = purple_status_get_type(status); if (type == NULL || g_str_equal(purple_status_type_get_id(type), "ONLINE") || purple_status_type_get_primitive(type) == PURPLE_STATUS_OFFLINE) return NULL; mood_text = (char *)purple_status_type_get_name(type); if (mood_text != NULL && strlen(mood_text)) return skype_strdup_withhtml(mood_text); return NULL; } void skype_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *userinfo, gboolean full) { PurplePresence *presence; PurpleStatus *status; SkypeBuddy *sbuddy = buddy->proto_data; presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); purple_notify_user_info_add_pair(userinfo, _("Status"), purple_status_get_name(status)); if (sbuddy && sbuddy->mood && strlen(sbuddy->mood)) purple_notify_user_info_add_pair(userinfo, _("Message"), sbuddy->mood); } const char * skype_list_icon(PurpleAccount *account, PurpleBuddy *buddy) { //when pidgin requests the icon for a skypeout buddy, it also requests the icon //for the account, which overwrites the buddy's skypeout icon, so cheat //and store it :) static gboolean last_icon_was_skypeout = FALSE; if (last_icon_was_skypeout) { last_icon_was_skypeout = FALSE; return "skypeout"; } if (buddy && buddy->name[0] == '+') { last_icon_was_skypeout = TRUE; return "skypeout"; } return "skype"; } int skype_send_raw(PurpleConnection *gc, const char *buf, int len) { gchar *newbuf = NULL; int newlen = 0; newbuf = g_strndup(buf, len); newlen = strlen(newbuf); if (newbuf != NULL) { skype_send_message_nowait(newbuf); g_free(newbuf); } return newlen; } static gboolean skype_check_missedmessages(PurpleAccount *account) { skype_send_message_nowait("SEARCH MISSEDCHATMESSAGES"); //return FALSE; //dont keep looking for missed messages return TRUE; //keep looking for missed messages } static gboolean skype_check_missedvoicemails(PurpleAccount *account) { int i; gchar **messages; gchar *message; gchar *messages_start; //gchar *subject, *from, *to, *url; gchar *temp; message = skype_send_message("SEARCH MISSEDVOICEMAILS"); if (!message || *message == '\0') return FALSE; //there was an error messages_start = strchr(message, ' '); if (messages_start != NULL) { messages = g_strsplit(messages_start + 1, ", ", 0); for (i = 0; messages[i]; i++) { temp = skype_send_message("GET VOICEMAIL %s PARTNER_DISPNAME", messages[i]); g_free(temp); temp = skype_send_message("GET VOICEMAIL %s PARTNER_HANDLE", messages[i]); g_free(temp); //url = g_strdup_printf("skype:%s?voicemailplay", messages[i]); //ALTER VOICEMAIL %s STARTPLAYBACK //purple_notify_email(purple_account_get_connection(account), // subject, from, to, url, NULL, NULL); } g_strfreev(messages); } g_free(message); return FALSE; } static void skype_initiate_chat(PurpleBlistNode *node, gpointer data) { PurpleBuddy *buddy; //PurpleGroup *group; PurpleConversation *conv; gchar *msg; gchar chat_id[200]; static int chat_number = 1000; if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { buddy = (PurpleBuddy *) node; msg = skype_send_message("CHAT CREATE"); printf("Chat create response: '%s'\n", msg); sscanf(msg, "CHAT %s ", chat_id); g_free(msg); conv = serv_got_joined_chat(purple_account_get_connection(purple_buddy_get_account(buddy)), chat_number, chat_id); skype_send_message_nowait("ALTER CHAT %s ADDMEMBERS %s", chat_id, buddy->name); skype_debug_info("skype", "Conv Hash Table: %d\n", conv->data); skype_debug_info("skype", "chat_id: %s\n", chat_id); purple_conversation_set_data(conv, "chat_id", g_strdup(chat_id)); purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), skype_get_account_username(buddy->account), NULL, PURPLE_CBFLAGS_NONE, FALSE); purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), buddy->name, NULL, PURPLE_CBFLAGS_NONE, FALSE); purple_conv_chat_set_id(PURPLE_CONV_CHAT(conv), chat_number++); } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { /* group = (PurpleGroup *) node; msg = skype_send_message("CHAT CREATE"); sscanf(msg, "CHAT %s ", chat_id); g_free(msg); conv = serv_got_joined_chat(purple_account_get_connection(purple_buddy_get_account(buddy)), chat_number, chat_id); purple_conversation_set_data(conv, "chat_id", g_strdup(chat_id)); purple_conv_chat_set_id(PURPLE_CONV_CHAT(conv), chat_number++); //loop through all the child nodes of the group //add buddy to the chat //add self to the chat */ } } static void skype_chat_invite(PurpleConnection *gc, int id, const char *msg, const char *who) { PurpleConversation *conv; gchar *chat_id; conv = purple_find_chat(gc, id); chat_id = (gchar *)g_hash_table_lookup(conv->data, "chat_id"); skype_send_message_nowait("ALTER CHAT %s ADDMEMBERS %s", chat_id, who); purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), who, NULL, PURPLE_CBFLAGS_NONE, TRUE); } static void skype_chat_leave(PurpleConnection *gc, int id) { PurpleConversation *conv; gchar* chat_id; conv = purple_find_chat(gc, id); chat_id = (gchar *)g_hash_table_lookup(conv->data, "chat_id"); skype_send_message_nowait("ALTER CHAT %s LEAVE", chat_id); //once you leave a chat, you can't go back //TODO delete chat from buddy-list, if it's not a public chat } static int skype_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) { PurpleConversation *conv; gchar* chat_id; char *stripped; stripped = purple_markup_strip_html(message); conv = purple_find_chat(gc, id); skype_debug_info("skype", "chat_send; conv: %d, conv->data: %d, id: %d\n", conv, conv->data, id); chat_id = (gchar *)g_hash_table_lookup(conv->data, "chat_id"); skype_debug_info("skype", "chat_id: %s\n", chat_id); skype_send_message_nowait("CHATMESSAGE %s %s", chat_id, stripped); serv_got_chat_in(gc, id, purple_account_get_username(purple_connection_get_account(gc)), PURPLE_MESSAGE_SEND, message, time(NULL)); return 1; } static void skype_set_chat_topic(PurpleConnection *gc, int id, const char *topic) { PurpleConversation *conv; gchar* chat_id; conv = purple_find_chat(gc, id); chat_id = (gchar *)g_hash_table_lookup(conv->data, "chat_id"); skype_send_message_nowait("ALTER CHAT %s SETTOPIC %s", chat_id, topic); //serv_got_chat_in(gc, id, purple_account_get_username(purple_connection_get_account(gc)), PURPLE_MESSAGE_SYSTEM, // skype_strdup_withhtml(g_strdup_printf(_("%s has changed the topic to: %s"), purple_account_get_username(purple_connection_get_account(gc)), topic)), time(NULL)); //purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic); } /*PurpleChat * skype_find_blist_chat(PurpleAccount *account, const char *name) { GList *glist_temp = NULL; PurpleConversation *conv = NULL; PurpleChat *chat = NULL; glist_temp = g_list_find_custom(purple_get_conversations(), name, (GCompareFunc)skype_find_chat); if (glist_temp && glist_temp->data) { conv = glist_temp->data; chat = PURPLE_CONV_CHAT(conv); } return chat; }*/ void skype_join_chat(PurpleConnection *gc, GHashTable *data) { SkypeChat *chat; PurpleConvChat *convchat; gchar *password; gchar *chat_id = (gchar *)g_hash_table_lookup(data, "chat_id"); if (chat_id == NULL) { return; } chat = skype_find_chat(chat_id, gc->account); skype_send_message_nowait("ALTER CHAT %s JOIN", chat_id); password = (gchar *)g_hash_table_lookup(data, "password"); if (password != NULL) { skype_send_message_nowait("ALTER CHAT %s ENTERPASSWORD %s", chat_id, password); } if (!chat->conv) { chat->prpl_chat_id = g_str_hash(chat_id); chat->conv = serv_got_joined_chat(gc, chat->prpl_chat_id, chat_id); purple_conversation_set_data(chat->conv, "chat_id", g_strdup(chat_id)); if (password != NULL) purple_conversation_set_data(chat->conv, "password", g_strdup(password)); } else { convchat = purple_conversation_get_chat_data(chat->conv); convchat->left = FALSE; } skype_send_message_nowait("GET CHAT %s MEMBERS", chat_id); if (purple_blist_find_chat(gc->account, chat_id) != NULL) { skype_send_message_nowait("ALTER CHAT %s BOOKMARK", chat_id); } } gchar * skype_get_chat_name(GHashTable *data) { gchar *temp; if (data == NULL) return g_strdup(""); temp = g_hash_table_lookup(data, "chat_id"); if (temp == NULL) return g_strdup(""); return g_strdup(temp); } gchar * skype_cb_real_name(PurpleConnection *gc, int id, const char *who) { PurpleBuddy *buddy; SkypeBuddy *sbuddy; printf("CB Real name for %s in %d\n", who, id); buddy = purple_find_buddy(gc->account, who); if (buddy && buddy->alias) { return g_strdup(buddy->alias); } else if (buddy && buddy->server_alias) { return g_strdup(buddy->server_alias); } else if (buddy && buddy->proto_data) { sbuddy = buddy->proto_data; if (sbuddy->fullname) return g_strdup(sbuddy->fullname); } return NULL; } GList * skype_join_chat_info(PurpleConnection *gc) { GList *m = NULL; struct proto_chat_entry *pce; pce = g_new0(struct proto_chat_entry, 1); pce->label = _("Skype Name"); pce->identifier = "chat_id"; pce->required = TRUE; m = g_list_append(m, pce); pce = g_new0(struct proto_chat_entry, 1); pce->label = _("Password"); pce->identifier = "password"; pce->required = FALSE; m = g_list_append(m, pce); return m; } GHashTable * skype_join_chat_info_defaults(PurpleConnection *gc, const char *chat_name) { GHashTable *defaults; defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); if (chat_name != NULL) { g_hash_table_insert(defaults, "chat_id", g_strdup(chat_name)); } return defaults; } void skype_alias_buddy(PurpleConnection *gc, const char *who, const char *alias) { if (who == NULL) return; if (alias == NULL) alias = ""; skype_send_message_nowait("SET USER %s DISPLAYNAME %s", who, alias); } gboolean skype_offline_msg(const PurpleBuddy *buddy) { return TRUE; } void skype_get_chatmessage_info(int message) { skype_send_message_nowait("GET CHATMESSAGE %d TYPE", message); skype_send_message_nowait("GET CHATMESSAGE %d CHATNAME", message); skype_send_message_nowait("GET CHATMESSAGE %d BODY", message); skype_send_message_nowait("GET CHATMESSAGE %d FROM_HANDLE", message); skype_send_message_nowait("GET CHATMESSAGE %d USERS", message); skype_send_message_nowait("GET CHATMESSAGE %d LEAVEREASON", message); skype_send_message_nowait("GET CHATMESSAGE %d TIMESTAMP", message); } void skype_show_search_users(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection *) action->context; purple_request_input(gc, _("Search for Skype Users"), _("Search for Skype Users"), _("Type the Skype Name, full name or e-mail address of the buddy you are " "searching for."), NULL, FALSE, FALSE, NULL, _("_Search"), G_CALLBACK(skype_search_users), _("_Cancel"), NULL, purple_connection_get_account(gc), NULL, NULL, gc); } static void skype_search_users(PurpleConnection *gc, const gchar *searchterm) { PurpleNotifySearchResults *results; PurpleNotifySearchColumn *column; gchar *userlist; gchar **list_of_users; int i = 0; results = purple_notify_searchresults_new(); if (results == NULL) return; //columns: Full Name, Skype Name, Country/Region, Profile Link column = purple_notify_searchresults_column_new(_("Full Name")); purple_notify_searchresults_column_add(results, column); column = purple_notify_searchresults_column_new(_("Skype Name")); purple_notify_searchresults_column_add(results, column); column = purple_notify_searchresults_column_new(_("Country/Region")); purple_notify_searchresults_column_add(results, column); //buttons: Add Skype Contact, Close purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD, skype_searchresults_add_buddy); userlist = skype_send_message("SEARCH USERS %s", searchterm); list_of_users = g_strsplit(&userlist[6], ", ", -1); while(list_of_users[i]) { GList *row = NULL; row = g_list_append(row, skype_get_user_info(list_of_users[i], "FULLNAME")); row = g_list_append(row, g_strdup(list_of_users[i])); row = g_list_append(row, g_strconcat(skype_get_user_info(list_of_users[i], "CITY"), ", ", skype_get_user_info(list_of_users[i], "COUNTRY"), NULL)); purple_notify_searchresults_row_add(results, row); i++; } g_strfreev(list_of_users); g_free(userlist); purple_notify_searchresults(gc, NULL, NULL, NULL, results, NULL, NULL); } void skype_searchresults_add_buddy(PurpleConnection *gc, GList *row, void *user_data) { purple_blist_request_add_buddy(purple_connection_get_account(gc), g_list_nth_data(row, 1), NULL, NULL); } /* Like purple_strdup_withhtml, but escapes htmlentities too */ gchar * skype_strdup_withhtml(const gchar *src) { gulong destsize, i, j; gchar *dest; g_return_val_if_fail(src != NULL, NULL); /* New length is (length of src) + (number of \n's * 3) + (number of &'s * 5) + (number of <'s * 4) + (number of >'s *4) + (number of "'s * 6) - (number of \r's) + 1 */ destsize = 1; for (i = 0; src[i] != '\0'; i++) { if (src[i] == '\n' || src[i] == '<' || src[i] == '>') destsize += 4; else if (src[i] == '&') destsize += 5; else if (src[i] == '"') destsize += 6; else if (src[i] != '\r') destsize++; } dest = g_malloc(destsize); /* Copy stuff, ignoring \r's, because they are dumb */ for (i = 0, j = 0; src[i] != '\0'; i++) { if (src[i] == '\n') { strcpy(&dest[j], "
"); j += 4; } else if (src[i] == '<') { strcpy(&dest[j], "<"); j += 4; } else if (src[i] == '>') { strcpy(&dest[j], ">"); j += 4; } else if (src[i] == '&') { strcpy(&dest[j], "&"); j += 5; } else if (src[i] == '"') { strcpy(&dest[j], """); j += 6; } else if (src[i] != '\r') dest[j++] = src[i]; } dest[destsize-1] = '\0'; return dest; } static void skype_display_skype_credit(PurplePluginAction *action) { gchar *temp, *currency; double balance; temp = skype_send_message("GET PROFILE PSTN_BALANCE"); balance = atol(&temp[21]); g_free(temp); balance = balance / 100; temp = skype_send_message("GET PROFILE PSTN_BALANCE_CURRENCY"); currency = g_strdup(&temp[30]); g_free(temp); temp = g_strdup_printf("%s %.2f", currency, balance); skype_debug_info("skype", "Balance: '%s'\n", temp); purple_notify_info(this_plugin, _("Skype Balance"), _("Your current Skype credit balance is:"), temp); g_free(temp); g_free(currency); } gchar * skype_set_next_sms_number_for_conversation(PurpleConversation *conv, const gchar *who) { gchar *sms_reply; gchar skype_sms_number[10]; if (sms_convo_link_table == NULL) { skype_debug_info("skype", "Creating convo_link_table\n"); sms_convo_link_table = g_hash_table_new(g_str_hash, g_str_equal); } sms_reply = skype_send_message("CREATE SMS OUTGOING %s", who); sscanf(sms_reply, "SMS %10s ", skype_sms_number); g_free(sms_reply); sms_reply = g_strdup(skype_sms_number); skype_debug_info("skype", "putting SMS number %s mobile number %s into the table\n", sms_reply, who); g_hash_table_insert(sms_convo_link_table, sms_reply, g_strdup(who)); purple_conversation_set_data(conv, "skype_next_sms_number", sms_reply); return sms_reply; } static void skype_request_auth_from_blist(PurpleBlistNode *node, gpointer data) { PurpleBuddy *buddy; if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) return; buddy = (PurpleBuddy *)node; skype_send_message_nowait("SET USER %s BUDDYSTATUS 2", buddy->name); } static void skype_open_sms_im(PurpleBlistNode *node, gpointer data) { PurpleBuddy *buddy; SkypeBuddy *sbuddy; PurpleConversation *conv; gchar *mobile_number = NULL; if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) return; buddy = (PurpleBuddy *)node; sbuddy = (SkypeBuddy *)buddy->proto_data; if (buddy->name[0] == '+') mobile_number = buddy->name; else if (sbuddy && sbuddy->phone_mobile) mobile_number = sbuddy->phone_mobile; if (mobile_number == NULL) return; //Open a conversation window conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, mobile_number, buddy->account); if (conv == NULL) { conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, buddy->account, mobile_number); } else { purple_conversation_present(conv); } purple_conversation_write(conv, NULL, _("This is an SMS message and will cost you money"), PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL)); //store the fact that it's an SMS convo in the conversation purple_conversation_set_data(conv, "sms_msg", "TRUE"); skype_set_next_sms_number_for_conversation(conv, mobile_number); } int skype_send_sms(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags) { PurpleConversation *conv; gchar *sms_number; gchar *stripped; if (who[0] != '+') return -1; conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, purple_connection_get_account(gc)); if (purple_conversation_get_data(conv, "sms_msg") == NULL) return -1; stripped = purple_markup_strip_html(message); sms_number = purple_conversation_get_data(conv, "skype_next_sms_number"); if (sms_number == NULL) { sms_number = skype_set_next_sms_number_for_conversation(conv, who); } skype_send_message_nowait("SET SMS %s BODY %s", sms_number, stripped); skype_send_message_nowait("ALTER SMS %s SEND", sms_number); //delete the sms number from the conversation skype_set_next_sms_number_for_conversation(conv, who); return 1; } static void dump_hash_table(gchar *key, gchar *value, gpointer data) { printf("'%s' = '%s'\n", key, value); } static gboolean skype_uri_handler(const char *proto, const char *cmd, GHashTable *params) { PurpleAccount *acct; gchar *temp; //only deal with skype: uri's if (!g_str_equal(proto, "skype") && !(g_str_equal(proto, "\"skype"))) return FALSE; /*uri's: skype: //does nothing skype:{buddynames} //open im with {buddynames} skype:{buddynames}?chat //open multi-user chat with {buddynames} skype:?chat&blob={blob id} //open public multi-user chat with the blob id of {blob id} skype:?chat&id={chat id} //open multi-user chat with the id of {chat id} skype:{buddynames}?call //call {buddynames} skype:{buddyname}?add //add user to buddy list //not sure about these ones skype:{buddynames}?userinfo //get buddy's info? skype:{buddynames}?voicemail //send a voice mail message? skype:{buddynames}?sendfile //send a file? */ acct = find_acct(purple_plugin_get_id(this_plugin), g_hash_table_lookup(params, "account")); //lets have a look at the hash table! skype_debug_info("skype", "dumping uri handler hashtable\n"); g_hash_table_foreach(params, (GHFunc)dump_hash_table, NULL); if (g_hash_table_lookup(params, "chat")) { if (!strlen(cmd)) { //probably a public multi-user chat? if ((temp = g_hash_table_lookup(params, "blob"))) { temp = skype_send_message("CHAT CREATEUSINGBLOB %s", temp); if (!strlen(temp)) { g_free(temp); return FALSE; } *strchr(strchr(temp, ' '), ' ') = '\0'; skype_send_message("ALTER %s JOIN", temp); g_free(temp); return TRUE; } else if ((temp = g_hash_table_lookup(params, "id"))) { skype_send_message_nowait("ALTER CHAT %s JOIN", temp); return TRUE; } } } else if (g_hash_table_lookup(params, "add")) { purple_blist_request_add_buddy(acct, cmd, NULL, g_hash_table_lookup(params, "displayname")); return TRUE; } else if (g_hash_table_lookup(params, "call")) { skype_send_message_nowait("CALL %s", cmd); return TRUE; } else if (g_hash_table_lookup(params, "userinfo")) { skype_get_info(NULL, cmd); return TRUE; } else if (g_hash_table_lookup(params, "voicemail") || g_hash_table_lookup(params, "sendfile")) { //not sure? } else if (strlen(cmd)) { //there'll be a bunch of usernames, seperated by comma skype_send_message_nowait("CHAT CREATE %s", cmd); return TRUE; } //we don't know how to handle this return FALSE; } #ifndef INSTANTBIRD /* Skype info from developer.skype.com and forum.skype.com: Audio: Audio format File: WAV PCM Sockets: raw PCM samples 16 KHz mono, 16 bit The 16-bit samples are stored as 2's-complement signed integers, ranging from -32768 to 32767. there must be a call in progress when these API Audio calls are made to Skype to define the ports. big-endian and it uses NO headers when using the Port form of the Audio API. ALTER CALL SET_INPUT SOUNDCARD="default" | PORT="port_no" | FILE="FILE_LOCATION" This enables you to set a port or a wav file as a source of your voice, instead of a microphone. ALTER CALL SET_OUTPUT SOUNDCARD="default" | PORT="port_no" | FILE="FILE_LOCATION" Redirects incoming transmission to a port or a wav file. With SET INPUT Skype acts like a Server, meaning, it waits to receive Audio Data from your application, so it does NOT act like a client. It will Open a Port and wait for your application to send Audio Data on the port defined, this is what a server does, a web server waits for a request on Port 80 for example. With SET OUTPUT Skype acts like a Client, meaning, it sends data to your application, your application is waiting for Skype to send Audio data, which means your application acts as a listener ("Server"). Video: SET VIDE0_IN [] Have to sniff for video window/object, very platform dependant */ //our call to someone else got ended static void skype_handle_call_got_ended(char *callnumber_string) { PurpleMedia *media; if (call_media_hash == NULL) { return; } media = g_hash_table_lookup(call_media_hash, callnumber_string); if (media != NULL) { purple_media_end(media, NULL, NULL); } } //called when the user accepts an incomming call from the ui static void skype_send_call_accept(char *callnumber_string) { char *temp; if (!callnumber_string || !strlen(callnumber_string)) return; temp = skype_send_message("ALTER CALL %s ANSWER", callnumber_string); if (!temp || strlen(temp) == 0) { //there was an error, hang up the the call return skype_handle_call_got_ended(callnumber_string); } } //called when the user rejects an incomming call from the ui static void skype_send_call_reject(char *callnumber_string) { if (!callnumber_string || !strlen(callnumber_string)) return; skype_send_message_nowait("ALTER CALL %s END HANGUP", callnumber_string); } //called when the user ends a call from the ui static void skype_send_call_end(char *callnumber_string) { if (!callnumber_string || !strlen(callnumber_string)) return; skype_send_message_nowait("ALTER CALL %s HANGUP", callnumber_string); } //purple_smarshal_VOID__ENUM_STRING_STRING static void skype_media_state_changed(PurpleMedia *media, PurpleMediaState state, gchar *session_id, gchar *participant, gchar *callnumber_string) { if (state == PURPLE_MEDIA_STATE_END) { g_hash_table_remove(call_media_hash, callnumber_string); } } static void skype_stream_info_changed(PurpleMedia *media, PurpleMediaInfoType type, gchar *session_id, gchar *participant, gboolean local, gchar *callnumber_string) { if (type == PURPLE_MEDIA_INFO_ACCEPT) { skype_send_call_accept(callnumber_string); } else if (type == PURPLE_MEDIA_INFO_HANGUP) { skype_send_call_end(callnumber_string); } else if (type == PURPLE_MEDIA_INFO_REJECT) { skype_send_call_reject(callnumber_string); } } //called by the UI to say, please start a media call gboolean skype_media_initiate(PurpleAccount *account, const char *who, PurpleMediaSessionType type) { PurpleMedia *media; gchar *temp; gchar *callnumber_string; if (call_media_hash == NULL) call_media_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); //Use skype's own audio/video stuff for now media = purple_media_manager_create_media(purple_media_manager_get(), account, "fsrtpconference", who, TRUE); temp = skype_send_message("CALL %s", who); if (!temp || !strlen(temp)) { g_free(temp); return FALSE; } callnumber_string = g_new(gchar, 10+1); sscanf(temp, "CALL %s ", callnumber_string); if (media != NULL) { purple_media_set_prpl_data(media, callnumber_string); g_hash_table_insert(call_media_hash, callnumber_string, media); g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(skype_send_call_accept), callnumber_string); g_signal_connect(G_OBJECT(media), "state-changed", G_CALLBACK(skype_media_state_changed), callnumber_string); g_signal_connect(G_OBJECT(media), "stream-info", G_CALLBACK(skype_stream_info_changed), callnumber_string); } else { skype_debug_info("skype_media", "media is NULL\n"); } return TRUE; } PurpleMediaCaps skype_get_media_caps(PurpleAccount *account, const char *who) { PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE; PurpleBuddy *buddy = NULL; SkypeBuddy *sbuddy = NULL; buddy = purple_find_buddy(account, who); if (buddy != NULL) sbuddy = buddy->proto_data; caps |= PURPLE_MEDIA_CAPS_AUDIO; if (buddy && sbuddy && sbuddy->is_video_capable) caps |= PURPLE_MEDIA_CAPS_AUDIO_VIDEO; return caps; } //there's an incoming call... deal with it static void skype_handle_incoming_call(PurpleConnection *gc, char *callnumber_string) { PurpleMedia *media; gchar *temp; gchar *who; if (call_media_hash == NULL) { call_media_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); } else { media = g_hash_table_lookup(call_media_hash, callnumber_string); if (media != NULL) { //we've already dealt with this return; } } temp = skype_send_message("GET CALL %s PARTNER_HANDLE", callnumber_string); if (!temp || !strlen(temp)) return; who = g_strdup(&temp[21+strlen(callnumber_string)]); g_free(temp); media = purple_media_manager_create_media(purple_media_manager_get(), purple_connection_get_account(gc), "fsrtpconference", who, FALSE); if (media != NULL) { purple_media_set_prpl_data(media, callnumber_string); g_hash_table_insert(call_media_hash, callnumber_string, media); purple_media_add_stream(media, "skype-audio1", who, PURPLE_MEDIA_AUDIO, FALSE, "nice", 0, NULL); purple_media_add_stream(media, "skype-audio2", who, PURPLE_MEDIA_AUDIO, FALSE, "rawudp", 0, NULL); //g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_NEW, "skype-audio", who); //g_signal_emit_by_name(media, "state-changed", 0, PURPLE_MEDIA_STATE_NEW, "skype-audio", who); g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(skype_send_call_accept), callnumber_string); g_signal_connect(G_OBJECT(media), "state-changed", G_CALLBACK(skype_media_state_changed), callnumber_string); g_signal_connect(G_OBJECT(media), "stream-info", G_CALLBACK(skype_stream_info_changed), callnumber_string); } else { skype_debug_info("skype_media", "purple_mmcm returned NULL\n"); } } #endif struct _skype_account_alias_cb { PurpleAccount *account; gchar *message; _account_char_fn callback; }; gboolean skype_account_alias_cb(gpointer data) { struct _skype_account_alias_cb *info = data; info->callback(info->account, info->message); g_free(info->message); g_free(info); return FALSE; } void skype_set_public_alias(PurpleConnection *gc, const char *alias, _account_char_fn success_cb, _account_char_fn failure_cb) { struct _skype_account_alias_cb *info; if (alias && *alias) { skype_send_message_nowait("SET PROFILE FULLNAME %s", alias); if (success_cb) { info = g_new0(struct _skype_account_alias_cb, 1); info->callback = success_cb; info->account = gc->account; info->message = g_strdup(alias); purple_timeout_add(0, skype_account_alias_cb, info); } } else { if (failure_cb) { info = g_new0(struct _skype_account_alias_cb, 1); info->callback = failure_cb; info->account = gc->account; info->message = g_strdup("Cannot be blank"); purple_timeout_add(0, skype_account_alias_cb, info); } } } void skype_get_public_alias(PurpleConnection *gc, _account_char_fn success_cb, _account_char_fn failure_cb) { struct _skype_account_alias_cb *info; gchar *alias; info = g_new0(struct _skype_account_alias_cb, 1); alias = skype_send_message("GET PROFILE FULLNAME"); info = g_new0(struct _skype_account_alias_cb, 1); info->callback = success_cb; info->account = gc->account; info->message = g_strdup(&alias[16]); purple_timeout_add(0, skype_account_alias_cb, info); g_free(alias); }