diff -Nur libpurple/plugins/libskype.c new/libpurple/plugins/libskype.c --- libpurple/plugins/libskype.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/libskype.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,1684 @@ +/* + * 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. + * + * 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 + +#include + +#include + +#include +#include +#include +#include +#include /* for purple_debug_info */ +#include +#include +#include + +#ifdef USE_FARSIGHT +#include +PurpleMedia *skype_media_initiate(PurpleConnection *gc, const char *who, PurpleMediaStreamType type); +#endif + +#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); +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_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); +char *skype_get_account_username(PurpleAccount *acct); +static PurpleAccount *skype_get_account(PurpleAccount *newaccount); +void skype_update_buddy_icon(PurpleBuddy *buddy); +static void skype_send_file_from_blist(PurpleBlistNode *node, gpointer data); +static GList *skype_node_menu(PurpleBlistNode *node); +static void skype_call_user_from_blist(PurpleBlistNode *node, gpointer data); +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 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); +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_program_update_check(void); +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); +void skype_program_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); +void skype_call_number(gpointer ignore, gchar *number); +void skype_call_number_request(PurplePlugin *plugin, gpointer data); + +#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 */ + +PurplePluginProtocolInfo prpl_info = { + /* options */ + OPT_PROTO_NO_PASSWORD|OPT_PROTO_REGISTER_NOSCREENNAME|OPT_PROTO_CHAT_TOPIC, + + 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 */ + NULL, /* list_emblems */ + 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 */ + NULL, /* chat_info_defaults */ + skype_login, /* login */ + skype_close, /* close */ + skype_send_im, /* send_im */ + NULL, /* set_info */ + NULL, /* send_typing */ + skype_get_info, /* get_info */ + skype_set_status, /* set_status */ + skype_set_idle, /* set_idle */ + NULL, /* change_passwd */ + skype_add_buddy, /* add_buddy */ + 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 */ + NULL, /* group_buddy */ + NULL, /* rename_group */ + NULL, /* buddy_free */ + NULL, /* convo_closed */ + skype_normalize, /* normalize */ + skype_set_buddy_icon,/* set_buddy_icon */ + NULL, /* remove_group */ + skype_cb_real_name, /* 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 */ + NULL, /* send_raw */ + NULL, /* roomlist_room_serialize */ + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* attention_types */ + (gpointer)sizeof(PurplePluginProtocolInfo) /* struct_size */ +#ifdef USE_FARSIGHT + , skype_media_initiate /* initiate_media */ +#endif +}; + +static PurplePluginInfo info = { + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_PROTOCOL, /* type */ + NULL, /* ui_requirement */ + 0, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + "prpl-bigbrownchunx-skype", /* id */ + "Skype", /* name */ + "1.2", /* version */ + "Allows using Skype IM functions from within Pidgin", /* summary */ + "Allows using Skype IM functions from within Pidgin", /* description */ + "Eion Robb ", /* author */ + "http://tinyurl.com/2by8rw", /* 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 (!g_thread_supported ()) + g_thread_init (NULL); + this_plugin = plugin; + /* plugin's path at + this_plugin->path */ +/* +#ifdef __APPLE__ + //Adium demands a server and port, but there isn't one + option = purple_account_option_string_new(_("Server"), "server", "localhost"); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_int_new(_("Port"), "port", 0); + 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(_("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(_("Auto-start Skype if not running"), "skype_autostart", TRUE); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + +} + +PURPLE_INIT_PLUGIN(skype, plugin_init, info); + + + +static PurpleAccount * +skype_get_account(PurpleAccount *newaccount) +{ + static PurpleAccount* account; + if (newaccount != NULL) + account = newaccount; + return account; +} + +gboolean +plugin_load(PurplePlugin *plugin) +{ + return TRUE; +} + +gboolean +plugin_unload(PurplePlugin *plugin) +{ + return TRUE; +} + +static GList * +skype_node_menu(PurpleBlistNode *node) +{ + GList *m = NULL; + PurpleMenuAction *act; + + if(PURPLE_BLIST_NODE_IS_BUDDY(node)) + { +#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 + act = purple_menu_action_new(_("Call..."), + PURPLE_CALLBACK(skype_call_user_from_blist), + NULL, NULL); + m = g_list_append(m, act); + + act = purple_menu_action_new(_("Initiate Chat"), + PURPLE_CALLBACK(skype_initiate_chat), + NULL, NULL); + m = g_list_append(m, act); + } + return m; +} + +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); + } +} + +const char * +skype_normalize(const PurpleAccount *acct, const char *who) +{ + return g_utf8_strdown(who, -1); +} + +GList * +skype_actions(PurplePlugin *plugin, gpointer context) +{ + GList *m = NULL; + PurpleMenuAction *act; + + 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); + + 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); + + act = purple_menu_action_new(_("Call..."), + PURPLE_CALLBACK(skype_call_number_request), + NULL, NULL); + m = g_list_append(m, act); + + return m; +} + +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_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://myjob", "space.co.nz/images/pidgin/", "?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); + purple_debug_info("skype", "Server filemtime: %d, Local filemtime: %d\n", servertime, mtime); + if (servertime > mtime) + { + purple_notify_info(this_plugin, _("Update 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"), _("No updates found"), _("You have the latest version of the Skype plugin")); + } +} + +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, _("Update 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"), _("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); +} + +GList * +skype_status_types(PurpleAccount *acct) +{ + GList *types; + PurpleStatusType *status; + + purple_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,name) status = \ + purple_status_type_new_with_attrs( \ + prim, /* PurpleStatusPrimitive */ \ + NULL, /* id - use default */ \ + _(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"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_AWAY, "Away"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_EXTENDED_AWAY, "Not Available"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_UNAVAILABLE, "Do Not Disturb"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_INVISIBLE, "Invisible"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_OFFLINE, "Offline"); + + + return types; +} + +gboolean +skype_set_buddies(PurpleAccount *acct) +{ + char *friends_text; + char **friends; + GSList *existing_friends; + GSList *found_buddy; + PurpleBuddy *buddy; + PurpleGroup *skype_group; + PurpleGroup *skypeout_group; + int i; + + skype_group = purple_group_new("Skype"); + skypeout_group = purple_group_new("SkypeOut"); + purple_blist_add_group(skype_group, NULL); + purple_blist_add_group(skypeout_group, NULL); + + + friends_text = skype_send_message("SEARCH FRIENDS"); + friends = g_strsplit_set(friends_text, ", ", 0); + 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=1; friends[i]; i++) + { + if (strlen(friends[i]) == 0) + continue; + //If already in list, dont recreate, reuse + purple_debug_info("skype", "Searching for friend %s\n", friends[i]); + found_buddy = g_slist_find_custom(existing_friends, + friends[i], + skype_slist_friend_search); + if (found_buddy != NULL) + { + //the buddy was already in the list + buddy = (PurpleBuddy *)found_buddy->data; + purple_debug_info("skype","Buddy already in list: %s (%s)\n", buddy->name, friends[i]); + } else { + purple_debug_info("skype","Buddy not in list %s\n", friends[i]); + buddy = purple_buddy_new(acct, g_strdup(friends[i]), NULL); + if (friends[i][0] == '+') + purple_blist_add_buddy(buddy, NULL, skypeout_group, NULL); + else + 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 ((found_buddy = g_slist_find_custom(existing_friends, skype_get_account_username(acct), skype_slist_friend_search))) + { + buddy = (PurpleBuddy *)found_buddy->data; + 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); + } + + purple_debug_info("skype", "Friends Count: %d\n", i); + g_strfreev(friends); + + 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 (strcmp(buddy->name, skype_get_account_username(NULL)) == 0) + { + //we must have put ourselves on our own list in pidgin, ignore + return; + } + + for(i=1; friends[i]; i++) + { + if (strlen(friends[i]) == 0) + continue; + if (strcmp(buddy->name, friends[i]) == 0) + return; + } + purple_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); +} + +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; + static gboolean api_supports_avatar = TRUE; + + if (api_supports_avatar == FALSE) + { + return; + } + + purple_debug_info("skype", "Updating buddy icon for %s\n", buddy->name); + + acct = purple_buddy_get_account(buddy); + 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) + { + purple_debug_warning("skype", "Error: Protocol doesn't suppot AVATAR\n"); + api_supports_avatar = FALSE; + } else { + g_file_get_contents(new_filename, &image_data, &image_data_len, NULL); + } + g_unlink(new_filename); + g_free(filename); + g_free(new_filename); + } else { + purple_debug_warning("skype", "Error making temp file %s\n", error->message); + g_error_free(error); + } + + purple_buddy_icons_set_for_user(acct, buddy->name, g_memdup(image_data,image_data_len), image_data_len, NULL); +} + + +void +skype_update_buddy_alias(PurpleBuddy *buddy) +{ + char *alias; + + alias = skype_get_user_info(buddy->name, "DISPLAYNAME"); + if (strlen(alias) > 0) + { + purple_blist_server_alias_buddy(buddy, alias); + return; + } + + alias = skype_get_user_info(buddy->name, "FULLNAME"); + if (strlen(alias) > 0) + { + purple_blist_server_alias_buddy(buddy, alias); + } +} + +gboolean +skype_update_buddy_status(PurpleBuddy *buddy) +{ + char *status; + PurpleStatusPrimitive primitive; + PurpleAccount *acct; + + acct = purple_buddy_get_account(buddy); + if (purple_account_is_connected(acct) == FALSE) + { + return FALSE; + } + status = skype_get_user_info(buddy->name, "ONLINESTATUS"); + if (strlen(status) == 0) + { + primitive = PURPLE_STATUS_OFFLINE; + purple_prpl_got_user_status(acct, buddy->name, purple_primitive_get_id_from_type(primitive), NULL); + return FALSE; + } + purple_debug_info("skype", "User %s status is %s\n", buddy->name, status); + + if (strcmp(status, "OFFLINE") == 0) + { + primitive = PURPLE_STATUS_OFFLINE; + if (strcmp(skype_get_user_info(buddy->name, "IS_VOICEMAIL_CAPABLE"), "TRUE") == 0) + { + buddy->proto_data = g_strdup(_("Offline with Voicemail")); + } else if (strcmp(skype_get_user_info(buddy->name, "IS_CF_ACTIVE"), "TRUE") == 0) + { + buddy->proto_data = g_strdup(_("Offline with Call Forwarding")); + } + } else if (strcmp(status, "ONLINE") == 0 || + strcmp(status, "SKYPEME") == 0) + { + primitive = PURPLE_STATUS_AVAILABLE; + } else if (strcmp(status, "AWAY") == 0) + { + primitive = PURPLE_STATUS_AWAY; + } else if (strcmp(status, "NA") == 0) + { + primitive = PURPLE_STATUS_EXTENDED_AWAY; + } else if (strcmp(status, "DND") == 0) + { + primitive = PURPLE_STATUS_UNAVAILABLE; + } else if (strcmp(status, "SKYPEOUT") == 0) + { + if (purple_account_get_bool(buddy->account, "skypeout_online", TRUE)) + { + primitive = PURPLE_STATUS_AVAILABLE; + } else { + primitive = PURPLE_STATUS_OFFLINE; + } + buddy->proto_data = g_strdup(_("SkypeOut")); + } else + { + primitive = PURPLE_STATUS_UNSET; + } + + //Dont say we got their status unless its changed + if (strcmp(purple_status_get_id(purple_presence_get_active_status(purple_buddy_get_presence(buddy))), purple_primitive_get_id_from_type(primitive)) != 0) + purple_prpl_got_user_status(acct, buddy->name, purple_primitive_get_id_from_type(primitive), NULL); + + if (primitive != PURPLE_STATUS_OFFLINE && + strcmp(status, "SKYPEOUT") != 0 && + primitive != PURPLE_STATUS_UNSET) + skype_send_message_nowait("GET USER %s MOOD_TEXT", buddy->name); + + /* if this function was called from another thread, don't loop over it */ + return FALSE; +} + +void +skype_login(PurpleAccount *acct) +{ + PurpleConnection *gc; + gchar *reply; + gboolean connect_successful; + + if(acct == NULL) + { + return; + } + + gc = purple_account_get_connection(acct); + gc->flags = PURPLE_CONNECTION_NO_BGCOLOR | + PURPLE_CONNECTION_NO_URLDESC | + PURPLE_CONNECTION_NO_FONTSIZE | + PURPLE_CONNECTION_NO_IMAGES; + + /* 1. connect to server */ + /* TODO: Work out if a skype connection is already running */ + if(FALSE) + { + purple_connection_error(gc, g_strconcat("\n",_("Only one Skype account allowed"), NULL)); + return; + } + + + purple_connection_update_progress(gc, _("Connecting"), 0, 5); + + connect_successful = skype_connect(); + if (!connect_successful) + { + purple_connection_error(gc, g_strconcat("\n", _("Could not connect to Skype process\nSkype not running?"), NULL)); + if (purple_account_get_bool(acct, "skype_autostart", TRUE)) + { + purple_debug_info("skype", "Should I start Skype?\n"); + if (!is_skype_running()) + { + purple_debug_info("skype", "Yes, start Skype\n"); + exec_skype(); + } + } + return; + } + + purple_connection_update_progress(gc, _("Authorizing"), + 0, /* which connection step this is */ + 4); /* total number of steps */ + +#ifndef __APPLE__ + reply = skype_send_message("NAME Pidgin"); + if (reply == NULL || strlen(reply) == 0) + { + purple_connection_error(gc, g_strconcat("\n",_("Skype client not ready"), NULL)); + return; + } + g_free(reply); +#endif + purple_connection_update_progress(gc, _("Initializing"), 1, 4); + reply = skype_send_message("PROTOCOL 5"); + if (reply == NULL || strlen(reply) == 0) + { + purple_connection_error(gc, g_strconcat("\n",_("Skype client not ready"), NULL)); + return; + } + g_free(reply); + + purple_connection_update_progress(gc, _("Silencing Skype"), 2, 4); + skype_silence(NULL, NULL); + purple_connection_update_progress(gc, _("Connected"), 3, 4); + purple_connection_set_state(gc, PURPLE_CONNECTED); + + skype_get_account(acct); + 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)); + //sync buddies after everything else has finished loading + purple_timeout_add(10, (GSourceFunc)skype_set_buddies, (gpointer)acct); +} + +char * +skype_get_account_username(PurpleAccount *acct) +{ + char *ret; + static char *username = NULL; + + if (username != NULL) + return username; + + 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 && strcmp(acct->username, username) != 0) + { + purple_debug_info("skype", "Setting username to %s\n", username); + purple_account_set_username(acct, username); + } + return username; +} + +void +skype_get_account_alias(PurpleAccount *acct) +{ + char *ret; + char *alias; + ret = skype_send_message("GET PROFILE FULLNAME"); + alias = g_strdup(&ret[17]); + g_free(ret); + purple_account_set_alias(acct, alias); +} + +void +skype_slist_remove_messages(gpointer buddy_pointer, gpointer unused) +{ + PurpleBuddy *buddy = (PurpleBuddy *)buddy_pointer; + if (buddy && buddy->proto_data) + { + buddy->proto_data = NULL; + } +} + +void +skype_close(PurpleConnection *gc) +{ + GSList *buddies; + + purple_debug_info("skype", "logging out\n"); + if (gc && purple_account_get_bool(gc->account, "skype_sync", TRUE)) + skype_send_message("SET USERSTATUS OFFLINE"); + skype_send_message_nowait("SET SILENT_MODE OFF"); + skype_disconnect(); + if (gc) + { + buddies = purple_find_buddies(gc->account, NULL); + if (buddies != NULL && g_slist_length(buddies) > 0) + g_slist_foreach(buddies, skype_slist_remove_messages, NULL); + } +} + +int +skype_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, + PurpleMessageFlags flags) +{ + char *stripped; + char *temp; + char *chat_id; + PurpleConversation *conv; + + stripped = purple_markup_strip_html(message); + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, purple_connection_get_account(gc)); + if (conv == NULL || purple_conversation_get_data(conv, "chat_id") == NULL) + { + chat_id = g_new(char, 200); + temp = skype_send_message("CHAT CREATE %s", who); + sscanf(temp, "CHAT %s ", chat_id); + g_free(temp); + if (conv != NULL) + { + purple_conversation_set_data(conv, "chat_id", chat_id); + } + } else { + chat_id = purple_conversation_get_data(conv, "chat_id"); + } + + skype_send_message_nowait("CHATMESSAGE %s %s", chat_id, stripped); + + return 0; +} + +gchar * +timestamp_to_datetime(time_t timestamp) +{ + return g_strdup(purple_date_format_long(localtime(×tamp))); +} + +void +skype_get_info(PurpleConnection *gc, const gchar *username) +{ + PurpleNotifyUserInfo *user_info; + double timezoneoffset; + char timezone_str[9]; + struct tm *birthday_time = g_new(struct tm, 1); + int time; + + user_info = purple_notify_user_info_new(); +#define _SKYPE_USER_INFO(prop, key) \ + purple_notify_user_info_add_pair(user_info, _(key),\ + g_strdup(skype_get_user_info(username, prop))); + + purple_notify_user_info_add_section_header(user_info, _("Contact Information")); + _SKYPE_USER_INFO("HANDLE", "Handle"); + _SKYPE_USER_INFO("FULLNAME", "Full Name"); + purple_notify_user_info_add_section_break(user_info); + purple_notify_user_info_add_section_header(user_info, _("Personal Information")); + //_SKYPE_USER_INFO("BIRTHDAY", "Birthday"); + purple_str_to_time(skype_get_user_info(username, "BIRTHDAY"), FALSE, birthday_time, NULL, NULL); + purple_notify_user_info_add_pair(user_info, _("Birthday"), g_strdup(purple_date_format_short(birthday_time))); + _SKYPE_USER_INFO("SEX", "Gender"); + _SKYPE_USER_INFO("LANGUAGE", "Language"); + _SKYPE_USER_INFO("COUNTRY", "Country"); + _SKYPE_USER_INFO("ABOUT", "About"); + _SKYPE_USER_INFO("IS_VIDEO_CAPABLE", "Is Video Capable"); + _SKYPE_USER_INFO("ISAUTHORIZED", "Authorised"); + _SKYPE_USER_INFO("ISBLOCKED", "Blocked"); + //_SKYPE_USER_INFO("LASTONLINETIMESTAMP", "Last Online"); //timestamp + time = atoi(skype_get_user_info(username, "LASTONLINETIMESTAMP")); + purple_debug_info("skype", "time: %d\n", time); + purple_notify_user_info_add_pair(user_info, _("Last Online"), + timestamp_to_datetime((time_t) time)); + // g_strdup(purple_date_format_long(localtime((time_t *)(void *)&time)))); + //_SKYPE_USER_INFO("TIMEZONE", "Timezone"); //in seconds + timezoneoffset = atof(skype_get_user_info(username, "TIMEZONE")) / 3600; + timezoneoffset -= 24; //timezones are offset by 24 hours to keep them valid and unsigned + g_snprintf(timezone_str, 9, "UMT +%.1f", timezoneoffset); + purple_notify_user_info_add_pair(user_info, _("Timezone"), g_strdup(timezone_str)); + + _SKYPE_USER_INFO("NROF_AUTHED_BUDDIES", "Number of buddies"); + purple_notify_user_info_add_section_break(user_info); + _SKYPE_USER_INFO("MOOD_TEXT", "Mood"); + + + purple_notify_userinfo(gc, username, user_info, NULL, NULL); + purple_notify_user_info_destroy(user_info); + + g_free(birthday_time); +} + +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); + /* purple_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); + switch (purple_status_type_get_primitive(type)) { + case PURPLE_STATUS_AVAILABLE: + skype_send_message_nowait("SET USERSTATUS ONLINE"); + break; + case PURPLE_STATUS_AWAY: + skype_send_message_nowait("SET USERSTATUS AWAY"); + break; + case PURPLE_STATUS_EXTENDED_AWAY: + skype_send_message_nowait("SET USERSTATUS NA"); + break; + case PURPLE_STATUS_UNAVAILABLE: + skype_send_message_nowait("SET USERSTATUS DND"); + break; + case PURPLE_STATUS_INVISIBLE: + skype_send_message_nowait("SET USERSTATUS INVISIBLE"); + break; + case PURPLE_STATUS_OFFLINE: + skype_send_message_nowait("SET USERSTATUS OFFLINE"); + break; + default: + skype_send_message_nowait("SET USERSTATUS UNKNOWN"); + } + + message = purple_status_get_attr_string(status, "message"); + if (message == NULL) + message = ""; + else + message = purple_markup_strip_html(message); + skype_send_message_nowait("SET PROFILE MOOD_TEXT %s", message); +} + +void +skype_set_idle(PurpleConnection *gc, int time) +{ + skype_send_message("SET AUTOAWAY OFF"); + if (time <= 0) { + skype_send_message_nowait("SET USERSTATUS ONLINE"); + } else if ((time >= 300) && (time < 1200)) { + skype_send_message_nowait("SET USERSTATUS AWAY"); + } else if (time >= 1200) { + skype_send_message_nowait("SET USERSTATUS NA"); + } +} + + +void +skype_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) +{ + if (strcmp(skype_get_user_info(buddy->name, "ONLINESTATUS"), "UNKNOWN") == 0) + { + //this user doesn't exist + purple_blist_remove_buddy(buddy); + purple_notify_error(gc, "Error", "User does not exist", "The user does not exist in Skype"); + return; + } + + skype_send_message_nowait("SET USER %s BUDDYSTATUS 2 %s", buddy->name, _("Please authorize me so I can add you to my buddy list.")); + if (buddy->alias == NULL || strlen(buddy->alias) == 0) + skype_update_buddy_alias(buddy); + if (group && group->name) + { + /* TODO: Deal with groups */ + } + skype_add_permit(gc, buddy->name); + skype_rem_deny(gc, buddy->name); +} + +void +skype_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) +{ + 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); +} + +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); +} + +void +skype_keepalive(PurpleConnection *gc) +{ + gchar *connected; + connected = skype_send_message("PING"); + if (strlen(connected) == 0) + { + purple_connection_error(gc, _("\nSkype not responding")); + } + g_free(connected); +} + +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); + } + else + skype_send_message_nowait("SET AVATAR 1"); +} + +char * +skype_status_text(PurpleBuddy *buddy) +{ + char *mood_text; + PurplePresence *presence; + PurpleStatus *status; + PurpleStatusType *type; + int i; + + if (buddy->proto_data != NULL && strlen(buddy->proto_data)) + return g_strdup((char *)buddy->proto_data); + + if (buddy->proto_data == NULL) + { + mood_text = skype_get_user_info(buddy->name, "MOOD_TEXT"); + if (mood_text != NULL && strlen(mood_text)) + { + for (i=0; iproto_data = skype_strdup_withhtml(mood_text); + return g_strdup(mood_text); + } + } + + //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 || + purple_status_type_get_primitive(type) == PURPLE_STATUS_AVAILABLE || + 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; + + 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 (buddy->proto_data && strlen(buddy->proto_data)) + purple_notify_user_info_add_pair(userinfo, _("Message"), buddy->proto_data); +} + +const char * +skype_list_icon(PurpleAccount *account, PurpleBuddy *buddy) +{ + return "skype"; +} + +static void +skype_initiate_chat(PurpleBlistNode *node, gpointer data) +{ + PurpleBuddy *buddy; + 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"); + sscanf(msg, "CHAT %s ", chat_id); + g_free(msg); + //conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, buddy->account, buddy->name); + 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); + purple_debug_info("skype", "Conv Hash Table: %d\n", conv->data); + purple_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++); + } +} + +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); +} + +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); +} + +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); + purple_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"); + purple_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_strconcat(_("You changed the topic to "), topic, NULL)), time(NULL)); + purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic); +} + +void +skype_join_chat(PurpleConnection *gc, GHashTable *data) +{ + gchar *chat_id = (gchar *)g_hash_table_lookup(data, "chat_id"); + if (chat_id == NULL) + { + return; + } + skype_send_message_nowait("ALTER CHAT %s JOIN", chat_id); +} + +gchar * +skype_get_chat_name(GHashTable *data) +{ + return g_strdup(g_hash_table_lookup(data, "chat_id")); +} + +gchar * +skype_cb_real_name(PurpleConnection *gc, int id, const char *who) +{ + return skype_get_user_info(who, "FULLNAME"); +} + +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 = _("_Name:"); + pce->identifier = "chat_id"; + pce->required = TRUE; + m = g_list_append(m, pce); + + return m; +} + +void +skype_alias_buddy(PurpleConnection *gc, const char *who, const char *alias) +{ + skype_send_message_nowait("SET USER %s DISPLAYNAME %s", who, alias); +} + +gboolean +skype_offline_msg(const PurpleBuddy *buddy) +{ + return TRUE; +} + +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); + + purple_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); +} + +#ifdef USE_FARSIGHT +/* +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 + +*/ + +//called by the UI to say, please start a media call +PurpleMedia * +skype_media_initiate(PurpleConnection *gc, const char *who, PurpleMediaStreamType type) +{ + gchar *temp; + gchar *callnumber_string; + + //FarsightSession *fs = farsight_session_factory_make("rtp"); + //if (!fs) { + // purple_debug_error("jabber", "Farsight's rtp plugin not installed"); + // return NULL; + //} + //FarsightStream *audio_stream = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_AUDIO, FARSIGHT_STREAM_DIRECTION_BOTH); + //FarsightStream *video_stream = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_VIDEO, FARSIGHT_STREAM_DIRECTION_BOTH); + + //Use skype's own audio/video stuff for now + PurpleMedia *media = purple_media_manager_create_media(purple_media_manager_get(), gc, who, NULL, NULL); + + /*farsight_stream_set_source(audio_stream, purple_media_get_audio_src(media)); + farsight_stream_set_sink(audio_stream, purple_media_get_audio_sink(media)); + farsight_stream_set_source(video_stream, purple_media_get_video_src(media)); + farsight_stream_set_sink(video_stream, purple_media_get_video_sink(media)); + + g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(google_send_call_accept), callnumber_string); + g_signal_connect_swapped(G_OBJECT(media), "reject", G_CALLBACK(google_send_call_reject), callnumber_string);*/ + + temp = skype_send_message("CALL %s", who); + if (!temp || !strlen(temp)) + { + g_free(temp); + return NULL; + } + callnumber_string = g_new(gchar, 10+1); + sscanf(temp, "CALL %s ", &callnumber_string); + + g_signal_connect_swapped(G_OBJECT(media), "hangup", G_CALLBACK(google_send_call_end), callnumber_string); + g_signal_connect_swapped(G_OBJECT(media), "got-hangup", G_CALLBACK(google_send_call_end), callnumber_string); + + return media; +} + +//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); +} + +int +skype_find_media(PurpleMedia *media, const char *who) +{ + const char *screenname = purple_media_get_screenname(media); + return strcmp(screenname, who); +} + +//our call to someone else got ended +static void +skype_handle_call_got_ended(char *callnumber_string) +{ + char *temp; + char *who; + PurpleMediaManager *manager; + PurpleMedia *media; + GList glist_temp; + + 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); + + manager = purple_media_manager_get(); + + glist_temp = g_list_find_custom(manager->priv->medias, who, skype_find_media); + if (!glist_temp || !glist_temp->data) + return; + + media = glist_temp->data; + purple_media_got_hangup(media); +} + +//there's an incoming call... deal with it +static void +skype_handle_incoming_call(PurpleConnection *gc, char *callnumber_string) +{ + PurpleMedia *media; + 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(), gc, who, NULL, NULL); + + g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(skype_send_call_accept), callnumber_string); + g_signal_connect_swapped(G_OBJECT(media), "reject", G_CALLBACK(skype_send_call_reject), callnumber_string); + g_signal_connect_swapped(G_OBJECT(media), "hangup", G_CALLBACK(skype_send_call_end), callnumber_string); + + purple_media_ready(media); +} +#endif + diff -Nur libpurple/plugins/skype_events.c new/libpurple/plugins/skype_events.c --- libpurple/plugins/skype_events.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/skype_events.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,637 @@ +#include + +void skype_auth_allow(gpointer sender); +void skype_auth_deny(gpointer sender); +static gboolean skype_handle_received_message(char *message); +gint skype_find_filetransfer(PurpleXfer *transfer, char *skypeid); +void skype_accept_transfer(PurpleXfer *transfer); +void skype_decline_transfer(PurpleXfer *transfer); +gint skype_find_chat(PurpleConversation *conv, char *chat_id); +static void purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status); +void skype_call_accept_cb(gchar *call); +void skype_call_reject_cb(gchar *call); + +gboolean skype_update_buddy_status(PurpleBuddy *buddy); +void skype_update_buddy_alias(PurpleBuddy *buddy); +void skype_update_buddy_icon(PurpleBuddy *buddy); +static PurpleAccount *skype_get_account(PurpleAccount *account); +char *skype_get_account_username(PurpleAccount *acct); +gchar *skype_get_user_info(const gchar *username, const gchar *property); +gchar *skype_strdup_withhtml(const gchar *src); + +char *skype_send_message(char *message, ...); + +/* + This function must only be called from the main loop, using purple_timeout_add +*/ +static gboolean +skype_handle_received_message(char *message) +{ + char command[255]; + char **string_parts = NULL; + PurpleAccount *this_account; + PurpleConnection *gc; + char *my_username; + PurpleBuddy *buddy; + char *body; + char *body_html; + char *msg_num; + char *sender; + char *type; + int mtime; + char *chatname; + char *temp; + char *chat_type; + char **chatusers = NULL; + PurpleXfer *transfer = NULL; + PurpleConversation *conv = NULL; + GList *glist_temp = NULL; + int i; + static int chat_count = 0; + + sscanf(message, "%s ", command); + this_account = skype_get_account(NULL); + if (this_account == NULL) + return FALSE; + gc = purple_account_get_connection(this_account); + my_username = skype_get_account_username(this_account); + string_parts = g_strsplit(message, " ", 4); + + if (strcmp(command, "USERSTATUS") == 0) + { + + } else if (strcmp(command, "CONNSTATUS") == 0) + { + if (strcmp(string_parts[1], "LOGGEDOUT") == 0) + { + purple_connection_error(gc, _("\nSkype program closed")); + } + } else if ((strcmp(command, "USER") == 0) && (strcmp(string_parts[1], my_username) != 0)) + { + buddy = purple_find_buddy(this_account, string_parts[1]); + if (buddy != NULL) + { + if (strcmp(string_parts[2], "ONLINESTATUS") == 0) + { + skype_update_buddy_status(buddy); + skype_update_buddy_icon(buddy); + } else if (strcmp(string_parts[2], "MOOD_TEXT") == 0) + { + if (buddy->proto_data != NULL) + g_free(buddy->proto_data); + for (i=0; iproto_data = skype_strdup_withhtml(string_parts[3]); + } else if (strcmp(string_parts[2], "DISPLAYNAME") == 0) + { + purple_blist_server_alias_buddy(buddy, g_strdup(string_parts[3])); + } else if ((strcmp(string_parts[2], "BUDDYSTATUS") == 0) && + (strcmp(string_parts[3], "1") == 0)) + { + purple_blist_remove_buddy(buddy); + } + } else if (strcmp(string_parts[2], "BUDDYSTATUS") == 0) + { + if (strcmp(string_parts[3], "3") == 0) + { + purple_debug_info("skype", "Buddy %s just got added\n", string_parts[1]); + //buddy just got added.. handle it + if (purple_find_buddy(this_account, string_parts[1]) == NULL) + { + purple_debug_info("skype", "Buddy not in list\n"); + buddy = purple_buddy_new(this_account, g_strdup(string_parts[1]), NULL); + if (string_parts[1][0] == '+') + purple_blist_add_buddy(buddy, NULL, purple_group_new("SkypeOut"), NULL); + else + purple_blist_add_buddy(buddy, NULL, purple_group_new("Skype"), NULL); + skype_update_buddy_status(buddy); + skype_update_buddy_alias(buddy); + purple_prpl_got_user_idle(this_account, buddy->name, FALSE, 0); + skype_update_buddy_icon(buddy); + } + } + } else if (strcmp(string_parts[2], "RECEIVEDAUTHREQUEST") == 0) + { + //this event can be fired directly after authorising someone + temp = skype_get_user_info(string_parts[1], "ISAUTHORIZED"); + if (strcmp(temp, "TRUE") != 0) + { + purple_debug_info("skype", "User %s requested authorisation\n", string_parts[1]); + purple_account_request_authorization(this_account, string_parts[1], NULL, skype_get_user_info(string_parts[1], "FULLNAME"), + string_parts[3], (purple_find_buddy(this_account, string_parts[1]) != NULL), + skype_auth_allow, skype_auth_deny, (gpointer)g_strdup(string_parts[1])); + } + g_free(temp); + } + } else if (strcmp(command, "MESSAGE") == 0) + { + if (strcmp(string_parts[3], "RECEIVED") == 0) + { + msg_num = string_parts[1]; + temp = skype_send_message("GET MESSAGE %s TYPE", msg_num); + type = g_strdup(&temp[14+strlen(msg_num)]); + g_free(temp); + if (strcmp(type, "TEXT") == 0 || + strcmp(type, "AUTHREQUEST") == 0) + { + temp = skype_send_message("GET MESSAGE %s PARTNER_HANDLE", msg_num); + sender = g_strdup(&temp[24+strlen(msg_num)]); + g_free(temp); + temp = skype_send_message("GET MESSAGE %s BODY", msg_num); + body = g_strdup(&temp[14+strlen(msg_num)]); + g_free(temp); + temp = skype_send_message("GET MESSAGE %s TIMESTAMP", msg_num); + mtime = atoi(&temp[19+strlen(msg_num)]); + g_free(temp); + + /* Escape the body to HTML */ + body_html = skype_strdup_withhtml(body); + g_free(body); + + if (strcmp(type, "TEXT")==0) + { + if (strcmp(sender, my_username) == 0) + { + temp = skype_send_message("GET CHATMESSAGE %s CHATNAME", msg_num); + chatname = g_strdup(&temp[18+strlen(msg_num)]); + g_free(temp); + //purple_debug_info("skype", "Chatname: '%s'\n", chatname); + chatusers = g_strsplit_set(chatname, "/;", 3); + if (strcmp(&chatusers[0][1], my_username) == 0) + sender = &chatusers[1][1]; + else + sender = &chatusers[0][1]; + serv_got_im(gc, sender, body_html, PURPLE_MESSAGE_SEND, mtime); + g_strfreev(chatusers); + } else { + serv_got_im(gc, sender, body_html, PURPLE_MESSAGE_RECV, mtime); + } + }/* else if (strcmp(type, "AUTHREQUEST") == 0 && strcmp(sender, my_username) != 0) + { + purple_debug_info("User %s requested alternate authorisation\n", sender); + purple_account_request_authorization(this_account, sender, NULL, skype_get_user_info(sender, "FULLNAME"), + body, (purple_find_buddy(this_account, sender) != NULL), + skype_auth_allow, skype_auth_deny, (gpointer)g_strdup(sender)); + }*/ + + skype_send_message("SET MESSAGE %s SEEN", msg_num); + } + } else if (strcmp(string_parts[3], "SENT") == 0) + { + /* mark it as seen, to remove notification from skype ui */ + + /* dont async this -> infinite loop */ + skype_send_message("SET MESSAGE %s SEEN", string_parts[1]); + } + } else if (strcmp(command, "CHATMESSAGE") == 0) + { + if ((strcmp(string_parts[3], "RECEIVED") == 0) || + (strcmp(string_parts[3], "SENT") == 0)) + { + msg_num = string_parts[1]; + temp = skype_send_message("GET CHATMESSAGE %s TYPE", msg_num); + type = g_strdup(&temp[18+strlen(msg_num)]); + g_free(temp); + temp = skype_send_message("GET CHATMESSAGE %s CHATNAME", msg_num); + chatname = g_strdup(&temp[22+strlen(msg_num)]); + g_free(temp); + + glist_temp = g_list_find_custom(purple_get_conversations(), chatname, (GCompareFunc)skype_find_chat); + if (glist_temp == NULL || glist_temp->data == NULL) + { + temp = skype_send_message("GET CHAT %s STATUS", chatname); + chat_type = g_strdup(&temp[13+strlen(chatname)]); + g_free(temp); + if (strcmp(chat_type, "DIALOG") == 0 || strcmp(chat_type, "LEGACY_DIALOG") == 0) + { + temp = skype_send_message("GET CHAT %s MEMBERS", chatname); + body = g_strdup(&temp[14+strlen(chatname)]); + g_free(temp); + chatusers = g_strsplit(body, " ", 0); + if (strcmp(chatusers[0], my_username) == 0) + sender = g_strdup(chatusers[1]); + else + sender = g_strdup(chatusers[0]); + g_strfreev(chatusers); + g_free(body); + ////if they have an IM window open, assign it the chatname + // + ////if they dont have an IM window open, open one, then look again + //serv_got_im(gc, sender, " ", PURPLE_MESSAGE_SYSTEM & PURPLE_MESSAGE_NO_LOG & PURPLE_MESSAGE_NOTIFY, 1); + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sender, this_account); + //TODO need to be able to fix for adium which doesn't create conversations + //if (conv == NULL) + // conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, this_account, sender); + } else { + conv = serv_got_joined_chat(gc, chat_count++, chatname); + //conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, this_account, chatname); + temp = skype_send_message("GET CHAT %s MEMBERS", chatname); + body = g_strdup(&temp[14+strlen(chatname)]); + g_free(temp); + chatusers = g_strsplit(body, " ", 0); + for (i=0; chatusers[i]; i++) + purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), chatusers[i], NULL, PURPLE_CBFLAGS_NONE, FALSE); + g_strfreev(chatusers); + g_free(body); + temp = skype_send_message("GET CHAT %s TOPIC", chatname); + body = g_strdup(&temp[12+strlen(chatname)]); + g_free(temp); + purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), this_account->username, body); + purple_debug_info("skype", "set topic to: %s\n", body); + } + purple_conversation_set_data(conv, "chat_id", chatname); + //g_hash_table_insert(conv->data, "chat_id", chatname); + //conv = purple_conversation_new(PURPLE_CONV_TYPE_ANY, this_account, chatname); + } else { + conv = glist_temp->data; + } + //Types of chat message are: + // SETTOPIC - change of chat topic + // SAID - IM + // ADDEDMEMBERS - invited someone to chat + // SAWMEMBERS - chat participant has seen other members + // CREATEDCHATWITH - chat to multiple people is created + // LEFT - someone left chat + if (strcmp(type, "SETTOPIC") == 0 && conv && conv->type == PURPLE_CONV_TYPE_CHAT) + { + temp = skype_send_message("GET CHATMESSAGE %s BODY", msg_num); + body = g_strdup(&temp[18+strlen(msg_num)]); + g_free(temp); + purple_debug_info("skype", "Topic changed: %s\n", body); + purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, body); + temp = skype_send_message("GET CHATMESSAGE %s FROM_HANDLE", msg_num); + sender = g_strdup(&temp[25+strlen(msg_num)]); + g_free(temp); + serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), sender, PURPLE_MESSAGE_SYSTEM, skype_strdup_withhtml(g_strconcat(sender, _(" changed the topic to "), body, NULL)), time(NULL)); + } else if (strcmp(type, "SAID") == 0 || + strcmp(type, "TEXT") == 0) + { + temp = skype_send_message("GET CHATMESSAGE %s BODY", msg_num); + body = g_strdup(&temp[18+strlen(msg_num)]); + g_free(temp); + //purple_debug_info("skype", "Message received: %s\n", body); + temp = skype_send_message("GET CHATMESSAGE %s FROM_HANDLE", msg_num); + sender = g_strdup(&temp[25+strlen(msg_num)]); + g_free(temp); + temp = skype_send_message("GET CHATMESSAGE %s TIMESTAMP", msg_num); + mtime = atoi(&temp[23+strlen(msg_num)]); + g_free(temp); + + /* Escape the body to HTML */ + body_html = skype_strdup_withhtml(body); + g_free(body); + if (conv && conv->type == PURPLE_CONV_TYPE_CHAT) + { + if (strcmp(string_parts[3], "RECEIVED") == 0) + serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), sender, PURPLE_MESSAGE_RECV, body_html, mtime); + else + serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), sender, PURPLE_MESSAGE_SEND, body_html, mtime); + } + else + { + if (strcmp(sender, my_username) == 0) + { + g_free(sender); + temp = skype_send_message("GET CHAT %s MEMBERS", chatname); + chatusers = g_strsplit(&temp[14+strlen(chatname)], " ", 0); + if (strcmp(chatusers[0], my_username) == 0) + sender = g_strdup(chatusers[1]); + else + sender = g_strdup(chatusers[0]); + g_strfreev(chatusers); + g_free(temp); + serv_got_im(gc, sender, body_html, PURPLE_MESSAGE_SEND, mtime); + } else { + serv_got_im(gc, sender, body_html, PURPLE_MESSAGE_RECV, mtime); + } + } + } else if (strcmp(type, "ADDEDMEMBERS") == 0 && conv && conv->type == PURPLE_CONV_TYPE_CHAT) + { + temp = skype_send_message("GET CHATMESSAGE %s USERS", msg_num); + body = g_strdup(&temp[19+strlen(msg_num)]); + g_free(temp); + purple_debug_info("skype", "Friends added: %s\n", body); + chatusers = g_strsplit(body, " ", 0); + for (i=0; chatusers[i]; i++) + purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), chatusers[i], NULL, PURPLE_CBFLAGS_NONE, FALSE); + g_strfreev(chatusers); + g_free(body); + } else if (strcmp(type, "LEFT") == 0 && conv && conv->type == PURPLE_CONV_TYPE_CHAT) + { + temp = skype_send_message("GET CHATMESSAGE %s USERS", msg_num); + body = g_strdup(&temp[19+strlen(msg_num)]); + g_free(temp); + purple_debug_info("skype", "Friends left: %s\n", body); + temp = skype_send_message("GET CHATMESSAGE %s LEAVEREASON", msg_num); + purple_conv_chat_remove_user(PURPLE_CONV_CHAT(conv), body, g_strdup(&temp[25+strlen(msg_num)])); + } + /* dont async this -> infinite loop */ + skype_send_message("SET CHATMESSAGE %s SEEN", msg_num); + } + } else if (strcmp(command, "FILETRANSFER") == 0) + { + //lookup current file transfers to see if there's already one there + glist_temp = g_list_find_custom(purple_xfers_get_all(), + string_parts[1], + (GCompareFunc)skype_find_filetransfer); + if (glist_temp == NULL && strcmp(string_parts[2], "TYPE") == 0) + { + temp = skype_send_message("GET FILETRANSFER %s PARTNER_HANDLE", string_parts[1]); + sender = g_strdup(&temp[29+strlen(string_parts[1])]); + g_free(temp); + if (strcmp(string_parts[3], "INCOMING") == 0) + { + transfer = purple_xfer_new(this_account, PURPLE_XFER_RECEIVE, sender); + } else { + transfer = purple_xfer_new(this_account, PURPLE_XFER_SEND, sender); + } + transfer->data = g_strdup(string_parts[1]); + purple_xfer_set_init_fnc(transfer, skype_accept_transfer); + purple_xfer_set_request_denied_fnc(transfer, skype_decline_transfer); + temp = skype_send_message("GET FILETRANSFER %s FILENAME", string_parts[1]); + purple_debug_info("skype", "Filename: '%s'\n", &temp[23+strlen(string_parts[1])]); + purple_xfer_set_filename(transfer, g_strdup(&temp[23+strlen(string_parts[1])])); + g_free(temp); + temp = skype_send_message("GET FILETRANSFER %s FILEPATH", string_parts[1]); + if (strlen(&temp[23+strlen(string_parts[1])])) + purple_xfer_set_local_filename(transfer, g_strdup(&temp[23+strlen(string_parts[1])])); + else + purple_xfer_set_local_filename(transfer, purple_xfer_get_filename(transfer)); + g_free(temp); + temp = skype_send_message("GET FILETRANSFER %s FILESIZE", string_parts[1]); + purple_xfer_set_size(transfer, atol(&temp[23+strlen(string_parts[1])])); + g_free(temp); + purple_xfer_add(transfer); + } else if (glist_temp != NULL) { + transfer = glist_temp->data; + } + if (transfer != NULL) + { + /*if (strcmp(string_parts[2], "TYPE") == 0) + { + if (strcmp(string_parts[3], "INCOMING") == 0) + { + transfer->type = PURPLE_XFER_RECEIVE; + } else { + transfer->type = PURPLE_XFER_SEND; + } + } else if (strcmp(string_parts[2], "PARTNER_HANDLE") == 0) + { + transfer->who = g_strdup(string_parts[3]); + } else*/ if (strcmp(string_parts[2], "FILENAME") == 0) + { + purple_xfer_set_filename(transfer, string_parts[3]); + } else if (strcmp(string_parts[2], "FILEPATH") == 0) + { + if (strlen(string_parts[3])) + purple_xfer_set_local_filename(transfer, string_parts[3]); + } else if (strcmp(string_parts[2], "STATUS") == 0) + { + if (strcmp(string_parts[3], "NEW") == 0 || + strcmp(string_parts[3], "WAITING_FOR_ACCEPT") == 0) + { + //Skype API doesn't let us accept transfers + //purple_xfer_request(transfer); + if (purple_xfer_get_type(transfer) == PURPLE_XFER_RECEIVE) + { +# ifndef __APPLE__ + skype_send_message("OPEN FILETRANSFER"); +# else + purple_notify_info(this_account, "Incoming File", g_strconcat("User ", purple_xfer_get_remote_user(transfer), " wishes to send you a file. Please open Skype to accept this file.", NULL), NULL); +# endif + purple_xfer_conversation_write(transfer, g_strconcat(purple_xfer_get_remote_user(transfer), " is sending a file to users of this chat.", NULL), FALSE); + } + purple_xfer_set_status(transfer, PURPLE_XFER_STATUS_NOT_STARTED); + } else if (strcmp(string_parts[3], "COMPLETED") == 0) + { + purple_xfer_set_completed(transfer, TRUE); + } else if (strcmp(string_parts[3], "CONNECTING") == 0 || + strcmp(string_parts[3], "TRANSFERRING") == 0 || + strcmp(string_parts[3], "TRANSFERRING_OVER_RELAY") == 0) + { + purple_xfer_set_status(transfer, PURPLE_XFER_STATUS_STARTED); + transfer->start_time = time(NULL); + } else if (strcmp(string_parts[3], "CANCELLED") == 0) + { + //transfer->end_time = time(NULL); + //transfer->bytes_remaining = 0; + //purple_xfer_set_status(transfer, PURPLE_XFER_STATUS_CANCEL_LOCAL); + purple_xfer_cancel_local(transfer); + }/* else if (strcmp(string_parts[3], "FAILED") == 0) + { + //transfer->end_time = time(NULL); + //purple_xfer_set_status(transfer, PURPLE_XFER_STATUS_CANCEL_REMOTE); + purple_xfer_cancel_remote(transfer); + }*/ + purple_xfer_update_progress(transfer); + } else if (strcmp(string_parts[2], "STARTTIME") == 0) + { + transfer->start_time = atol(string_parts[3]); + purple_xfer_update_progress(transfer); + /*} else if (strcmp(string_parts[2], "FINISHTIME") == 0) + { + if (strcmp(string_parts[3], "0") != 0) + transfer->end_time = atol(string_parts[3]); + purple_xfer_update_progress(transfer);*/ + } else if (strcmp(string_parts[2], "BYTESTRANSFERRED") == 0) + { + purple_xfer_set_bytes_sent(transfer, atol(string_parts[3])); + purple_xfer_update_progress(transfer); + } else if (strcmp(string_parts[2], "FILESIZE") == 0) + { + purple_xfer_set_size(transfer, atol(string_parts[3])); + } else if (strcmp(string_parts[2], "FAILUREREASON") == 0 && + strcmp(string_parts[3], "UNKNOWN") != 0) + { + temp = NULL; + if (strcmp(string_parts[3], "SENDER_NOT_AUTHORIZED") == 0) + { + temp = g_strdup(_("Not authorized")); + } else if (strcmp(string_parts[3], "REMOTELY_CANCELLED") == 0) + { + purple_xfer_cancel_remote(transfer); + purple_xfer_update_progress(transfer); + } else if (strcmp(string_parts[3], "FAILED_READ") == 0) + { + temp = g_strdup(_("Read error on local machine")); + } else if (strcmp(string_parts[3], "FAILED_REMOTE_READ") == 0) + { + temp = g_strdup(_("Read error on remote machine")); + } else if (strcmp(string_parts[3], "FAILED_WRITE") == 0) + { + temp = g_strdup(_("Write error on local machine")); + } else if (strcmp(string_parts[3], "FAILED_REMOTE_WRITE") == 0) + { + temp = g_strdup(_("Write error on remote machine")); + } else if (strcmp(string_parts[3], "REMOTE_DOES_NOT_SUPPORT_FT") == 0) + { + temp = g_strdup(_("Receiver does not support file transfers")); + } else if (strcmp(string_parts[3], "REMOTE_OFFLINE_FOR_TOO_LONG") == 0) + { + temp = g_strdup(_("Recipient not available")); + } + if (temp && strlen(temp)) + { + purple_xfer_error(transfer->type, this_account, transfer->who, temp); + g_free(temp); + } + } + } + } else if (strcmp(command, "WINDOWSTATE") == 0) + { + if (strcmp(string_parts[1], "HIDDEN") == 0) + { + skype_send_message("SET SILENT_MODE ON"); + } +#ifdef USE_FARSIGHT + } else if (strcmp(command, "CALL") == 0) + { + if (strcmp(string_parts[2], "STATUS") == 0) + { + if (strcmp(string_parts[3], "RINGING") == 0) + { + skype_handle_incoming_call(gc, string_parts[1]); + } else if (strcmp(string_parts[3], "FINISHED") == 0 || + strcmp(string_parts[3], "CANCELLED") == 0 || + strcmp(string_parts[3], "FAILED") == 0) + { + skype_handle_call_got_ended(string_parts[1]); + } + } +#else + } else if (strcmp(command, "CALL") == 0) + { + if (strcmp(string_parts[2], "STATUS") == 0 && + strcmp(string_parts[3], "RINGING") == 0) + { + temp = skype_send_message("GET CALL %s TYPE", string_parts[1]); + type = g_new0(gchar, 9); + sscanf(temp, "CALL %*s TYPE %[^_]", type); + g_free(temp); + temp = skype_send_message("GET CALL %s PARTNER_HANDLE", string_parts[1]); + sender = g_strdup(&temp[21+strlen(string_parts[1])]); + g_free(temp); + if (strcmp(type, "INCOMING") == 0) + { + purple_request_action(gc, _("Incoming Call"), g_strconcat(sender, " is calling you", NULL), "Do you want to accept their call?", + 0, this_account, sender, NULL, g_strdup(string_parts[1]), 2, _("Accept"), + G_CALLBACK(skype_call_accept_cb), _("Reject"), G_CALLBACK(skype_call_reject_cb)); + } + g_free(sender); + g_free(type); + } +#endif + } + if (string_parts) + { + g_strfreev(string_parts); + } + return FALSE; +} + +void +skype_call_accept_cb(gchar *call) +{ + skype_send_message("ALTER CALL %s ANSWER", call); + g_free(call); +} + +void +skype_call_reject_cb(gchar *call) +{ + skype_send_message("ALTER CALL %s HANGUP", call); + g_free(call); +} + +void +skype_auth_allow(gpointer sender) +{ + skype_send_message("SET USER %s ISAUTHORIZED TRUE", sender); +} + +void +skype_auth_deny(gpointer sender) +{ + skype_send_message("SET USER %s ISAUTHORIZED FALSE", sender); +} + +gint +skype_find_filetransfer(PurpleXfer *transfer, char *skypeid) +{ + if (transfer == NULL || transfer->data == NULL || skypeid == NULL) + return -1; + return strcmp(transfer->data, skypeid); +} + +void +skype_accept_transfer(PurpleXfer *transfer) +{ + //can't accept transfers +} + +void +skype_decline_transfer(PurpleXfer *transfer) +{ + //can't reject transfers +} + +gint +skype_find_chat(PurpleConversation *conv, char *chat_id) +{ + char *lookup; + if (chat_id == NULL || conv == NULL || conv->data == NULL) + return -1; + //lookup = g_hash_table_lookup(conv->data, "chat_id"); + lookup = purple_conversation_get_data(conv, "chat_id"); + if (lookup == NULL) + return -1; + return strcmp(lookup, chat_id); +} + +/* Since this function isn't public, and we need it to be, redefine it here */ +static void +purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status) +{ + g_return_if_fail(xfer != NULL); + + if(xfer->type == PURPLE_XFER_SEND) { + switch(status) { + case PURPLE_XFER_STATUS_ACCEPTED: + purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer); + break; + case PURPLE_XFER_STATUS_STARTED: + purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer); + break; + case PURPLE_XFER_STATUS_DONE: + purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer); + break; + case PURPLE_XFER_STATUS_CANCEL_LOCAL: + case PURPLE_XFER_STATUS_CANCEL_REMOTE: + purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer); + break; + default: + break; + } + } else if(xfer->type == PURPLE_XFER_RECEIVE) { + switch(status) { + case PURPLE_XFER_STATUS_ACCEPTED: + purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer); + break; + case PURPLE_XFER_STATUS_STARTED: + purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer); + break; + case PURPLE_XFER_STATUS_DONE: + purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer); + break; + case PURPLE_XFER_STATUS_CANCEL_LOCAL: + case PURPLE_XFER_STATUS_CANCEL_REMOTE: + purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer); + break; + default: + break; + } + } + + xfer->status = status; +} diff -Nur libpurple/plugins/skype_messaging.c new/libpurple/plugins/skype_messaging.c --- libpurple/plugins/skype_messaging.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/skype_messaging.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,163 @@ +#include + +#include "skype_events.c" + +static gboolean skype_handle_received_message(char *message); + +static void skype_message_received(char *message); +static gboolean skype_connect(); +static void skype_disconnect(); +static void send_message(char* message); +static void hide_skype(); +static gboolean exec_skype(); + +void skype_send_message_nowait(char *message, ...); +char *skype_send_message(char *message, ...); + +// Sort through the mess of different OS's to get the right proto + +#ifdef _WIN32 +# include "skype_messaging_win32.c" +#else /*if !win32 */ +# ifdef __APPLE__ +# include "skype_messaging_carbon.c" +# else /*if !apple */ +# if 1 +# include "skype_messaging_x11.c" +# else +# include "skype_messaging_dbus.c" +# endif /* !x11 */ +# endif /* !apple */ +#endif /* win32 */ + + +typedef struct { + gpointer sender; + gpointer body; + int time; + gpointer chatname; +} SkypeMessage; + +static GHashTable *message_queue = NULL; +static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + +#ifdef _WIN32 +//these two #defines override g_static_mutex_lock and +// g_static_mutex_unlock so as to remove "strict-aliasing" +// compiler warnings +#define g_static_mutex_lock2(mutex) \ + g_mutex_lock (g_static_mutex_get_mutex ((GMutex **)(void*)mutex)) +#define g_static_mutex_unlock2(mutex) \ + g_mutex_unlock (g_static_mutex_get_mutex ((GMutex **)(void*)mutex)) +#else +#define g_static_mutex_lock2 g_static_mutex_lock +#define g_static_mutex_unlock2 g_static_mutex_unlock +#endif + +static void +skype_message_received(char *orig_message) +{ + guint request_number; + guint *key; + int string_pos; + char *message; + + if (strlen(orig_message) == 0) + return; + + message = g_strdup(orig_message); + + purple_debug_info("skype", "Received: %s\n", message); + + if(message[0] == '#') + { + //It's a reply from a call we've made - update the hash table + sscanf(message, "#%u %n", &request_number, &string_pos); + key = g_new(guint, 1); + *key = request_number; + + g_static_mutex_lock2(&mutex); + g_hash_table_insert(message_queue, key, g_strdup(&message[string_pos])); + g_static_mutex_unlock2(&mutex); + + g_free(message); + } else { + purple_timeout_add(1, (GSourceFunc)skype_handle_received_message, (gpointer)message); + } +} + +void +skype_send_message_nowait(char *message_format, ...) +{ + va_list args; + char* message; + + va_start(args, message_format); + message = g_strdup_vprintf(message_format, args); + va_end(args); + + purple_debug_info("skype", "Sending: '%s'\n", message); + g_thread_create((GThreadFunc)send_message, message, FALSE, NULL); + +} + +char *skype_send_message(char *message_format, ...) +{ + static guint next_message_num = 0; + guint cur_message_num; + char *message; + char *return_msg; + va_list args; + unsigned int timeout = 0; + + va_start(args, message_format); + message = g_strdup_vprintf(message_format, args); + va_end(args); + + if (!message_queue) + message_queue = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); + + g_static_mutex_lock2(&mutex); + cur_message_num = next_message_num++; + if (next_message_num == G_MAXUINT) + next_message_num = 0; + g_static_mutex_unlock2(&mutex); + + //Send message asynchronously + skype_send_message_nowait("#%u %s", cur_message_num, message); + g_free(message); + + g_static_mutex_lock2(&mutex); + //Wait for a response + while(g_hash_table_lookup(message_queue, &cur_message_num) == NULL) + { + g_static_mutex_unlock2(&mutex); + g_thread_yield(); +#ifdef __APPLE__ + RunCurrentEventLoop(0); +#endif +#ifndef _WIN32 + usleep(1000); +#else + Sleep(1); +#endif + g_static_mutex_lock2(&mutex); + + if(timeout++ == 10000) + { + g_hash_table_remove(message_queue, &cur_message_num); + g_static_mutex_unlock2(&mutex); + return g_strdup(""); + } + } + return_msg = (char *)g_hash_table_lookup(message_queue, &cur_message_num); + g_hash_table_remove(message_queue, &cur_message_num); + g_static_mutex_unlock2(&mutex); + + if (strncmp(return_msg, "ERROR", 5) == 0) + { + g_free(return_msg); + return g_strdup(""); + } + return return_msg; +} diff -Nur libpurple/plugins/skype_messaging_carbon.c new/libpurple/plugins/skype_messaging_carbon.c --- libpurple/plugins/skype_messaging_carbon.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/skype_messaging_carbon.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,219 @@ +#include +//#include +#include +#include + +#include "AutoreleasePoolInit.h" + +//change this to 0 if using an old version of the Skype.framework +#define SENDSKYPERETURNS 0 + +#include "skype_messaging_carbon2.c" + +static gboolean connected_to_skype = FALSE; + +void +SkypeNotificationReceived(CFStringRef input) +{ + char *output = NULL; + GError *error = NULL; + void *pool = initAutoreleasePool(); + int strlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(input), kCFStringEncodingUTF8); + + printf("Message received"); + output = (char *)CFStringGetCStringPtr(input, kCFStringEncodingUTF8); + if (!output) + { + output = NewPtr(strlen+1); + CFStringGetCString(input, output, strlen+1, kCFStringEncodingUTF8); + } + printf(" %s\n", output); + //g_thread_create((GThreadFunc)skype_message_received, (void *)output, FALSE, &error); + skype_message_received(output); + if (error) + { + printf("Could not create new thread!!! %s\n", error->message); + g_error_free(error); + } + + destroyAutoreleasePool(pool); +} + +void +SkypeAttachResponse(unsigned int aAttachResponseCode) +{ + if (aAttachResponseCode) + { + printf("Skype attached successfully :)\n"); + connected_to_skype = TRUE; + } + else + { + printf("Skype couldn't connect :(\n"); + connected_to_skype = FALSE; + } +} + +void +SkypeBecameAvailable(CFPropertyListRef aNotification) +{ + printf("Skype became available\n"); + connected_to_skype = TRUE; +} + +void +SkypeBecameUnavailable(CFPropertyListRef aNotification) +{ + printf("Skype became unavailable\n"); + connected_to_skype = FALSE; + g_thread_create((GThreadFunc)skype_message_received, "CONNSTATUS LOGGEDOUT", FALSE, NULL); +} + +static struct SkypeDelegate skypeDelegate = { + CFSTR("Adium"), /* clientAppName */ + SkypeNotificationReceived, + SkypeAttachResponse, + SkypeBecameAvailable, + SkypeBecameUnavailable +}; + +/*static gboolean +skype_connect_thread(gpointer data) +{ + static gboolean started = FALSE; + if (started) + return FALSE; + started = TRUE; + + void *pool = initAutoreleasePool(); + + printf("Start inner event loop\n"); + while(true) + { + //RunApplicationEventLoop(); + RunCurrentEventLoop(1); + } + printf("End of event loop\n"); + started = FALSE; + + destroyAutoreleasePool(pool); + + //don't loop this thread + return FALSE; +}*/ + +static gpointer static_pool; + +static gboolean +skype_connect() +{ + gboolean is_skype_running = FALSE; + + if (!static_pool) + static_pool = initAutoreleasePool(); + + is_skype_running = IsSkypeRunning(); + + printf("Is Skype running? '%s'\n", (is_skype_running?"Yes":"No")); + if (!is_skype_running) + return FALSE; + + if (connected_to_skype) + skype_disconnect(); + + SetSkypeDelegate(&skypeDelegate); + ConnectToSkype(); + + //g_thread_create((GThreadFunc)skype_connect_thread, NULL, FALSE, NULL); + while(connected_to_skype == FALSE) + { + RunCurrentEventLoop(1); + } + printf("Connected to skype\n"); + return TRUE; +} + +static void +skype_disconnect() +{ + connected_to_skype = FALSE; + DisconnectFromSkype(); + RemoveSkypeDelegate(); + RunCurrentEventLoop(1); +} + +static void +send_message(char* message) +{ + CFStringRef messageString = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8); + if (!connected_to_skype) + { + if (message[0] == '#') + { + int message_num; + char error_return[40]; + //And we're expecting a response + sscanf(message, "#%d ", &message_num); + sprintf(error_return, "#%d ERROR", message_num); + g_thread_create((GThreadFunc)skype_message_received, (void *)g_strdup(error_return), FALSE, NULL); + } + CFRelease(messageString); + return; + } + + gpointer pool = initAutoreleasePool(); + printf("Skype send message "); +#if SENDSKYPERETURNS + CFStringRef returnString = NULL; + returnString = SendSkypeCommand(messageString); + if (returnString) + SkypeNotificationReceived(returnString); +#else + SendSkypeCommand(messageString); +#endif + destroyAutoreleasePool(pool); + printf("%s\n", message); + CFRelease(messageString); +} + +static void +hide_skype() +{ + OSStatus status = noErr; + ProcessSerialNumber psn = {kNoProcess, kNoProcess}; + unsigned int procNameLength = 32; + unsigned char procName[procNameLength]; + unsigned int i = 0; + ProcessInfoRec info; + info.processInfoLength = sizeof(ProcessInfoRec); + info.processName = procName; + info.processAppSpec = NULL; + + while(status == noErr) + { + for(i = 0; i < procNameLength; i++) + procName[i] = '\0'; + + status = GetNextProcess(&psn); + if (status == noErr) + if (GetProcessInformation(&psn, &info) == noErr) + //for some reason first character is poisioned + if (strcmp((char *)&procName[1], "Skype") == 0) + { + ShowHideProcess(&psn, FALSE); + return; + } + } +} + +static gboolean +exec_skype() +{ + return g_spawn_command_line_async("/Applications/Skype.app/Contents/MacOS/Skype", NULL); +} + +static gboolean +is_skype_running() +{ + return IsSkypeRunning(); +} diff -Nur libpurple/plugins/skype_messaging_carbon2.c new/libpurple/plugins/skype_messaging_carbon2.c --- libpurple/plugins/skype_messaging_carbon2.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/skype_messaging_carbon2.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,398 @@ + + +#include +#include + + +typedef struct SkypeDelegate +{ + // Required member + CFStringRef clientApplicationName; + + // Optional members, can be NULL + void (*SkypeNotificationReceived)(CFStringRef aNotificationString); + void (*SkypeAttachResponse)(unsigned int aAttachResponseCode); // 0 - failed, 1 - success + void (*SkypeBecameAvailable)(CFPropertyListRef aNotification); + void (*SkypeBecameUnavailable)(CFPropertyListRef aNotification); +} SkypeDelegate; + +static SkypeDelegate *delegate = NULL; +static int isavailable = 0; +static int client_id = 0; + +char * +CFStringToCString(CFStringRef input) +{ + if (input == NULL) + return NULL; + int strlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(input), kCFStringEncodingUTF8); + char *output = NewPtr(strlen+1); + CFStringGetCString(input, output, strlen+1, kCFStringEncodingUTF8); + return output; +} + +int +CFNumberToCInt(CFNumberRef input) +{ + if (input == NULL) + return 0; + int output; + CFNumberGetValue(input, kCFNumberIntType, &output); + return output; +} + +void +availabilityUpdateCallback( + CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(userInfo, CFSTR("SKYPE_API_AVAILABILITY")); + isavailable = CFNumberToCInt(number); +} + +void +debugCallback( + CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + int i = 0; + + printf("Debug callback: %s\n", CFStringToCString(name)); + if (!userInfo) + return; + + CFIndex count = CFDictionaryGetCount(userInfo); + const void *keys[count]; + const void *values[count]; + CFDictionaryGetKeysAndValues(userInfo, keys, values); + for(i = 0; i < count; i++) + { + printf("For i=%d, key: %s\n", i, + CFStringToCString((CFStringRef)keys[i])); + } +} + + +void +apiNotificationCallback( + CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + CFNumberRef number = (CFNumberRef) CFDictionaryGetValue(userInfo, CFSTR("SKYPE_API_CLIENT_ID")); + int client_number = CFNumberToCInt(number); + if (client_number != 999 && (!client_id || client_id != client_number)) + { + return; + } + CFStringRef string = (CFStringRef) CFDictionaryGetValue(userInfo, CFSTR("SKYPE_API_NOTIFICATION_STRING")); + if (string && delegate && delegate->SkypeNotificationReceived) + { + delegate->SkypeNotificationReceived(string); + } +} + + +void +attachResponseCallback( + CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + CFNumberRef responseNumber = (CFNumberRef)CFDictionaryGetValue(userInfo, CFSTR("SKYPE_API_ATTACH_RESPONSE")); + int response = CFNumberToCInt(responseNumber); + client_id = response; + if (delegate && delegate->SkypeAttachResponse) + { + delegate->SkypeAttachResponse(response?1:0); + } +} + + +void +skypeQuitCallback( + CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + if (delegate && delegate->SkypeBecameAvailable) + delegate->SkypeBecameUnavailable(NULL); +} + + +void +skypeAvailableCallback( + CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + if (delegate && delegate->SkypeBecameAvailable) + delegate->SkypeBecameAvailable(NULL); +} + + +// STANDARD SKYPE.H BITS: +void RemoveSkypeDelegate(void); + +void +SetSkypeDelegate(SkypeDelegate *aDelegate) +{ + if (!aDelegate->clientApplicationName) + { + printf("Deletegate requires application name\n"); + delegate = NULL; + return; + } + + if (delegate) + { + RemoveSkypeDelegate(); + } + + delegate = aDelegate; + + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + + CFNotificationCenterAddObserver( + center, + delegate->clientApplicationName, + apiNotificationCallback, + CFSTR("SKSkypeAPINotification"), + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); + + CFNotificationCenterAddObserver( + center, + delegate->clientApplicationName, + skypeQuitCallback, + CFSTR("SKSkypeWillQuit"), + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); + + CFNotificationCenterAddObserver( + center, + delegate->clientApplicationName, + skypeAvailableCallback, + CFSTR("SKSkypeBecameAvailable"), + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); + + CFNotificationCenterAddObserver( + center, + delegate->clientApplicationName, + availabilityUpdateCallback, + CFSTR("SKAvailabilityUpdate"), + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); + + CFNotificationCenterAddObserver( + center, + delegate->clientApplicationName, + attachResponseCallback, + CFSTR("SKSkypeAttachResponse"), + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); +} + +SkypeDelegate * +GetSkypeDelegate(void) +{ + return delegate; +} + +void +RemoveSkypeDelegate(void) +{ + if (delegate && delegate->clientApplicationName) + { + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + + CFNotificationCenterRemoveObserver( + center, + delegate->clientApplicationName, + CFSTR("SKSkypeAPINotification"), + NULL); + + CFNotificationCenterRemoveObserver( + center, + delegate->clientApplicationName, + CFSTR("SKSkypeWillQuit"), + NULL); + + CFNotificationCenterRemoveObserver( + center, + delegate->clientApplicationName, + CFSTR("SKSkypeBecameAvailable"), + NULL); + + CFNotificationCenterRemoveObserver( + center, + delegate->clientApplicationName, + CFSTR("SKAvailabilityUpdate"), + NULL); + + CFNotificationCenterRemoveObserver( + center, + delegate->clientApplicationName, + CFSTR("SKSkypeAttachResponse"), + NULL); + } + delegate = NULL; +} + +int +IsSkypeAvailable(void) +{ + //is skype available? + isavailable = 0; + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + + CFNotificationCenterPostNotification( + center, + CFSTR("SKSkypeAPIAvailabilityRequest"), + NULL, + NULL, + TRUE); + + //Should only take 1 second or less to reply + RunCurrentEventLoop(1); + int avail = isavailable; + isavailable = 0; + return avail; +} + +int +IsSkypeRunning(void) +{ + OSStatus status = noErr; + ProcessSerialNumber psn = {kNoProcess, kNoProcess}; + unsigned int procNameLength = 32; + unsigned char procName[procNameLength]; + unsigned int i = 0; + ProcessInfoRec info; + info.processInfoLength = sizeof(ProcessInfoRec); + info.processName = procName; + info.processAppSpec = NULL; + pid_t pid = 0; + + while(status == noErr) + { + for(i = 0; i < procNameLength; i++) + procName[i] = '\0'; + + status = GetNextProcess(&psn); + if (status == noErr) + { + if (GetProcessInformation(&psn, &info) == noErr) + { + //for some reason first character is poisioned + if (strcmp((char *)&procName[1], "Skype") == 0) + { + if (GetProcessPID(&psn, &pid) == noErr) + { + return (int)pid; + } + } + } + } + } + return 0; +} + +void +ConnectToSkype(void) +{ + if (!delegate || !delegate->clientApplicationName) + { + printf("Error: Delegate not set\n"); + return; + } + + if (!IsSkypeAvailable()) + { + printf("Error: Skype not available\n"); + } + + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + + //do the connect + CFNotificationCenterPostNotification( + center, + CFSTR("SKSkypeAPIAttachRequest"), + delegate->clientApplicationName, + NULL, + TRUE); +} + +void SendSkypeCommand(CFStringRef command) +{ + if (delegate == NULL) + { + printf("Can't send message, no delegate set\n"); + return; + } + if (command == NULL) + return; + if (!client_id) + { + printf("Can't send message, not connected\n"); + return; + } + + CFRetain(command); + + CFNumberRef id_number = CFNumberCreate(NULL, kCFNumberIntType, &client_id); + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + const void *keys[] = {(void *)CFSTR("SKYPE_API_COMMAND"), (void *)CFSTR("SKYPE_API_CLIENT_ID")}; + const void *values[] = {command, id_number}; + CFDictionaryRef userInfo = CFDictionaryCreate(NULL, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + //send message + CFNotificationCenterPostNotification( + center, + CFSTR("SKSkypeAPICommand"), + NULL, + userInfo, + FALSE); + + CFRelease(command); + CFRelease(id_number); + CFRelease(userInfo); +} + +void DisconnectFromSkype(void) +{ + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + + if (client_id) + { + CFNumberRef id_number = CFNumberCreate(NULL, kCFNumberIntType, &client_id); + const void *keys[] = {(void *)CFSTR("SKYPE_API_CLIENT_ID")}; + const void *values[] = {id_number}; + CFDictionaryRef userInfo = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + //disconnect + CFNotificationCenterPostNotification( + center, + CFSTR("SKSkypeAPIDetachRequest"), + NULL, + userInfo, + FALSE); + + client_id = 0; + } +} diff -Nur libpurple/plugins/skype_messaging_dbus.c new/libpurple/plugins/skype_messaging_dbus.c --- libpurple/plugins/skype_messaging_dbus.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/skype_messaging_dbus.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,84 @@ +#define DBUS_API_SUBJECT_TO_CHANGE + +#include + +#define SKYPE_DBUS_BUS DBUS_BUS_SESSION + +static DBusGConnection *connection = NULL; +static DBusGProxy *proxy = NULL; +static DBusGProxy *proxy_receive = NULL; + +static void +notify_handler(DBusGProxy *object, const char *message, gpointer user_data) +{ + purple_debug_info("skype_dbus", "notify_handler called: %s\n", message); +} + +static gboolean +skype_connect() +{ + GError *error = NULL; + connection = dbus_g_bus_get (SKYPE_DBUS_BUS, &error); + if (connection == NULL && error != NULL) + { + purple_debug_info("skype_dbus", "Error: %s\n", error->message); + g_error_free(error); + return FALSE; + } + + proxy = dbus_g_proxy_new_for_name (connection, + "com.Skype.API", + "/com/Skype", + "com.Skype.API"); + proxy_receive = dbus_g_proxy_new_for_name (connection, + "com.Skype.API", + "/com/Skype/Client", + "com.Skype.API"); + + dbus_g_proxy_add_signal(proxy_receive, "Notify", G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(proxy_receive, "Notify", G_CALLBACK(notify_handler), NULL, NULL); + + return TRUE; +} + +static void +skype_disconnect() +{ + /*if (connection != NULL) + g_free(connection); + if (proxy != NULL) + g_free(proxy); + if (proxy_receive != NULL) + g_free(proxy_receive);*/ +} + +static void +send_message(char* message) +{ + GError *error = NULL; + gchar *str = NULL; + int message_num; + gchar error_return[30]; + + purple_debug_info("skype_dbus", "con %d, proxy %d, rec %d\n", connection, proxy, proxy_receive); + if (!dbus_g_proxy_call (proxy, "Invoke", &error, G_TYPE_STRING, message, G_TYPE_INVALID, + G_TYPE_STRING, &str, G_TYPE_INVALID)) + { + if (error && error->message) + { + purple_debug_info("skype_dbus", "Error: %s\n", error->message); + if (message[0] == '#') + { + //We're expecting a response + sscanf(message, "#%d ", &message_num); + sprintf(error_return, "#%d ERROR", message_num); + g_thread_create((GThreadFunc)skype_message_received, (void *)g_strdup(error_return), FALSE, NULL); + } + } + else + purple_debug_info("skype_dbus", "no response\n"); + } + if (str != NULL) + g_thread_create((GThreadFunc)skype_message_received, (void *)str, FALSE, NULL); +} + diff -Nur libpurple/plugins/skype_messaging_win32.c new/libpurple/plugins/skype_messaging_win32.c --- libpurple/plugins/skype_messaging_win32.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/skype_messaging_win32.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,228 @@ +#include +#include +#include + +#define SKYPE_WIN32_CLASS_NAME "Skype-libpurple-Joiner" + +static LRESULT APIENTRY Skype_WindowProc(HWND hWindow, UINT uiMessage, + WPARAM uiParam, LPARAM ulParam); +static void win32_message_loop(void); + +HINSTANCE hInit_ProcessHandle = NULL; +static UINT uiGlobal_MsgID_SkypeControlAPIAttach = 0; +static UINT uiGlobal_MsgID_SkypeControlAPIDiscover = 0; +static HWND hInit_MainWindowHandle = NULL; +static HWND hGlobal_SkypeAPIWindowHandle = NULL; +HANDLE hEvent = NULL; + +static gboolean +skype_connect() +{ + int i = 0; + PDWORD_PTR sendMessageResult = NULL; + + if (!uiGlobal_MsgID_SkypeControlAPIAttach) + uiGlobal_MsgID_SkypeControlAPIAttach = RegisterWindowMessage("SkypeControlAPIAttach"); + if (!uiGlobal_MsgID_SkypeControlAPIDiscover) + uiGlobal_MsgID_SkypeControlAPIDiscover = RegisterWindowMessage("SkypeControlAPIDiscover"); + + if (!hInit_ProcessHandle) + hInit_ProcessHandle = (HINSTANCE)OpenProcess( PROCESS_DUP_HANDLE, FALSE, GetCurrentProcessId()); + purple_debug_info("skype_win32", "ProcessId %d\n", GetCurrentProcessId()); + purple_debug_info("skype_win32", "hInit_ProcessHandle %d\n", hInit_ProcessHandle); + + g_thread_create((GThreadFunc)win32_message_loop, NULL, FALSE, NULL); + while(hInit_MainWindowHandle == NULL) + { + Sleep(10); + } + + purple_debug_info("skype_win32", "hInit_MainWindowHandle %d\n", hInit_MainWindowHandle); + purple_debug_info("skype_win32", "Sending broadcast message\n"); + SendMessageTimeout( HWND_BROADCAST, uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM)hInit_MainWindowHandle, 0, SMTO_NORMAL, 1000, sendMessageResult); + purple_debug_info("skype_win32", "Broadcast message sent\n"); + + while(hGlobal_SkypeAPIWindowHandle == NULL && i < 100) + { + i++; + Sleep(10); + } + + if (hGlobal_SkypeAPIWindowHandle == NULL) + return FALSE; + + return TRUE; +} + +static void +win32_message_loop(void) +{ + MSG msg; + WNDCLASS oWindowClass; + int classRegistration; + static gboolean message_loop_started = FALSE; + + if (message_loop_started) + return; + message_loop_started = TRUE; + + oWindowClass.style = CS_HREDRAW|CS_VREDRAW; + oWindowClass.lpfnWndProc = (WNDPROC)&Skype_WindowProc; + oWindowClass.cbClsExtra = 0; + oWindowClass.cbWndExtra = 0; + oWindowClass.hInstance = hInit_ProcessHandle; + oWindowClass.hIcon = NULL; + oWindowClass.hCursor = NULL; + oWindowClass.hbrBackground = NULL; + oWindowClass.lpszMenuName = NULL; + oWindowClass.lpszClassName = SKYPE_WIN32_CLASS_NAME; + classRegistration = RegisterClass(&oWindowClass); + hInit_MainWindowHandle = CreateWindow(SKYPE_WIN32_CLASS_NAME, SKYPE_WIN32_CLASS_NAME, + WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, + NULL, NULL, hInit_ProcessHandle, NULL); + + ShowWindow(hInit_MainWindowHandle, SW_HIDE); + UpdateWindow(hInit_MainWindowHandle); + while (GetMessage(&msg, NULL, 0, 0) != 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + purple_debug_info("skype_win32", "Finished message loop\n"); + DestroyWindow(hInit_MainWindowHandle); + hInit_MainWindowHandle = NULL; + message_loop_started = FALSE; +} + +static void +skype_disconnect() +{ + UnregisterClass(SKYPE_WIN32_CLASS_NAME, hInit_ProcessHandle); + CloseHandle(hInit_ProcessHandle); + hInit_ProcessHandle = NULL; + + if (hInit_MainWindowHandle != NULL) + { + //tell win32_message_loop() thread to die gracefully + PostMessage(hInit_MainWindowHandle, WM_QUIT, 0, 0); + } +} + +static void +send_message(char* message) +{ + int message_num; + char error_return[15]; + + COPYDATASTRUCT oCopyData; + oCopyData.dwData = 0; + oCopyData.lpData = (void *)message; + oCopyData.cbData = strlen(message) + 1; + + if (SendMessage( hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, + (WPARAM)hInit_MainWindowHandle, (LPARAM)&oCopyData) == FALSE) + { + hGlobal_SkypeAPIWindowHandle = NULL; + //There was an error + if (message[0] == '#') + { + //And we're expecting a response + sscanf(message, "#%d ", &message_num); + sprintf(error_return, "#%d ERROR WIN32", message_num); + g_thread_create((GThreadFunc)skype_message_received, (void *)g_strdup(error_return), FALSE, NULL); + } + } +} + +static LRESULT CALLBACK +Skype_WindowProc(HWND hWindow, UINT uiMessage, WPARAM uiParam, LPARAM ulParam) +{ + + if(uiMessage == WM_COPYDATA && hGlobal_SkypeAPIWindowHandle == (HWND)uiParam) + { + PCOPYDATASTRUCT poCopyData = (PCOPYDATASTRUCT)ulParam; + g_thread_create((GThreadFunc)skype_message_received, (void *)g_strdup(poCopyData->lpData), FALSE, NULL); + return 1; + } else if (uiMessage == uiGlobal_MsgID_SkypeControlAPIAttach) { + hGlobal_SkypeAPIWindowHandle = (HWND)uiParam; + purple_debug_info("skype_win32", "Attached process %d %d\n", uiParam, ulParam); + if (ulParam == 0) + purple_debug_info("skype_win32", "Attach success\n"); + else if (ulParam == 1) + purple_debug_info("skype_win32", "Pending auth\n"); + else if (ulParam == 2) + purple_debug_info("skype_win32", "Refused\n"); + else if (ulParam == 3) + purple_debug_info("skype_win32", "Not ready\n"); + else if (ulParam == 0x8001) + purple_debug_info("skype_win32", "Skype became ready\n"); + return 1; + } + return DefWindowProc(hWindow, uiMessage, uiParam, ulParam); +} + +static void +hide_skype() +{ + //don't need to since SILENT_MODE ON works + return; +} + +static gboolean +exec_skype() +{ + DWORD size = 0; + gchar *path, *pathtemp; + HKEY regkey; + gboolean success = FALSE; + + //HKCU\Software\Skype\Phone\SkypePath or HKLM\Software\Skype\Phone\SkypePath + + RegOpenKey(HKEY_CURRENT_USER, "Software\\Skype\\Phone", ®key); + RegQueryValueEx(regkey, "SkypePath", NULL, NULL, NULL, &size); + if (size != 0) + { + path = g_new(gchar, size); + RegQueryValueEx(regkey, "SkypePath", NULL, NULL, (LPBYTE)path, &size); + } else { + RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Skype\\Phone", ®key); + RegQueryValueEx(regkey, "SkypePath", NULL, NULL, NULL, &size); + if (size != 0) + { + path = g_new(gchar, size); + RegQueryValueEx(regkey, "Software\\Skype\\Phone\\SkypePath", NULL, NULL, (LPBYTE)path, &size); + } else { + path = g_strdup("C:\\Program Files\\Skype\\Phone\\Skype.exe"); + } + } + + pathtemp = g_strconcat("\"", path, "\" /nosplash /minimized", NULL); + purple_debug_info("skype_win32", "Path to Skype: %s\n", pathtemp); + g_free(path); + + success = g_spawn_command_line_async(pathtemp, NULL); + g_free(pathtemp); + return success; +} + +static gboolean +is_skype_running() +{ +#ifdef _TLHELP32_H + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + HANDLE temp = NULL; + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + Process32First(snapshot, &entry); + do { + if (strcmp("Skype.exe", entry.szExeFile) == 0) + { + temp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID); + CloseHandle(snapshot); + return TRUE; + } + } while (Process32Next(snapshot, &entry)); + CloseHandle(snapshot); +#endif + return FALSE; +} diff -Nur libpurple/plugins/skype_messaging_x11.c new/libpurple/plugins/skype_messaging_x11.c --- libpurple/plugins/skype_messaging_x11.c 1970-01-01 09:00:00.000000000 +0900 +++ new/libpurple/plugins/skype_messaging_x11.c 2008-02-27 20:32:24.000000000 +0900 @@ -0,0 +1,272 @@ +#include +#include + +static Display *disp = NULL; +static Window win = (Window)-1; +Window skype_win = (Window)-1; +GThread *receiving_thread; +Atom message_start, message_continue; +static gboolean run_loop = FALSE; +static unsigned char x11_error_code = 0; + +static void receive_message_loop(void); +int x11_error_handler(Display *disp, XErrorEvent *error); + +static gboolean +skype_connect() +{ + Window root; + Atom skype_inst; + Atom type_ret; + int format_ret; + unsigned long nitems_ret; + unsigned long bytes_after_ret; + unsigned char *prop; + int status; + + XSetErrorHandler(x11_error_handler); + disp = XOpenDisplay(getenv("DISPLAY")); + if (disp == NULL) + { + purple_debug_info("skype", "Couldn't open display\n"); + return FALSE; + } + message_start = XInternAtom( disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False ); + message_continue = XInternAtom( disp, "SKYPECONTROLAPI_MESSAGE", False ); + root = DefaultRootWindow( disp ); + win = XCreateSimpleWindow( disp, root, 0, 0, 1, 1, + 0, BlackPixel( disp, DefaultScreen( disp ) ), + BlackPixel( disp, DefaultScreen( disp ) )); + XFlush(disp); + if (win == -1) + { + purple_debug_info("skype", "Could not create X11 messaging window\n"); + return FALSE; + } + skype_inst = XInternAtom(disp, "_SKYPE_INSTANCE", True); + if (skype_inst == None) + { + skype_win = (Window)-1; + purple_debug_info("skype_x11", "Could not create skype Atom\n"); + return FALSE; + } + status = XGetWindowProperty(disp, root, skype_inst, 0, 1, False, XA_WINDOW, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, &prop); + if(status != Success || format_ret != 32 || nitems_ret != 1) + { + skype_win = (Window)-1; + purple_debug_info("skype", "Skype instance not found\n"); + return FALSE; + } + skype_win = * (const unsigned long *) prop & 0xffffffff; + + run_loop = TRUE; + + receiving_thread = g_thread_create((GThreadFunc)receive_message_loop, NULL, FALSE, NULL); + + return TRUE; +} + + +static void +skype_disconnect() +{ + XEvent *e; + + run_loop = FALSE; + skype_win = (Window)-1; + + e = g_new0(XEvent, 1); + e->xclient.type = DestroyNotify; + XSendEvent(disp, win, False, 0, e); + + //wait here for the event to be handled + + + XDestroyWindow(disp, win); + XCloseDisplay(disp); + + win = (Window)-1; + disp = NULL; +} + +int +x11_error_handler(Display *disp, XErrorEvent *error) +{ + x11_error_code = error->error_code; + return FALSE; +} + + +static void +send_message(char* message) +{ + unsigned int pos = 0; + unsigned int len = strlen( message ); + XEvent e; + int message_num; + char error_return[15]; + unsigned int i; + + if (skype_win == -1 || win == -1 || disp == NULL) + { + //There was an error + if (message[0] == '#') + { + //And we're expecting a response + sscanf(message, "#%d ", &message_num); + sprintf(error_return, "#%d ERROR", message_num); + g_thread_create((GThreadFunc)skype_message_received, (void *)g_strdup(error_return), FALSE, NULL); + } + return; + } + + memset(&e, 0, sizeof(e)); + e.xclient.type = ClientMessage; + e.xclient.message_type = message_start; /* first message */ + e.xclient.display = disp; + e.xclient.window = win; + e.xclient.format = 8; /* 8-bit values */ + + do + { + for( i = 0; i < 20 && i + pos <= len; ++i ) + e.xclient.data.b[ i ] = message[ i + pos ]; + XSendEvent( disp, skype_win, False, 0, &e ); + + e.xclient.message_type = message_continue; /* 2nd or greater message */ + pos += i; + } while( pos <= len ); + + //XFlush(disp); + + if (x11_error_code == BadWindow) + { + //There was an error + if (message[0] == '#') + { + //And we're expecting a response + sscanf(message, "#%d ", &message_num); + sprintf(error_return, "#%d ERROR", message_num); + g_thread_create((GThreadFunc)skype_message_received, (void *)g_strdup(error_return), FALSE, NULL); + } + g_thread_create((GThreadFunc)skype_message_received, "CONNSTATUS LOGGEDOUT", FALSE, NULL); + return; + } +} + +static void +receive_message_loop(void) +{ + XEvent e; + GString *msg = NULL; + char msg_temp[21]; + size_t len; + Bool event_bool; + + msg_temp[20] = '\0'; + while(run_loop) + { + //XNextEvent(disp, &e); + //if (e.type != ClientMessage) + //{ + // purple_debug_info("skype_x11", "Unknown event received: %d\n", e.xclient.type); + // XFlush(disp); + // continue; + //} + if (!disp) + { + purple_debug_error("skype_x11", "display has disappeared\n"); + break; + } + event_bool = XCheckTypedEvent(disp, ClientMessage, &e); + if (!event_bool) + { + usleep(1000); + continue; + } + + strncpy(msg_temp, e.xclient.data.b, 20); + len = strlen(msg_temp); + if (e.xclient.message_type == message_start) + msg = g_string_new_len(msg_temp, len); + else if (e.xclient.message_type == message_continue) + msg = g_string_append_len(msg, msg_temp, len); + else + { + purple_debug_info("skype_x11", "unknown message type: %d\n", e.xclient.message_type); + XFlush(disp); + continue; + } + + if (len < 20) + { + //if (msg->str[0] == '#') + g_thread_create((GThreadFunc)skype_message_received, (void *)g_string_free(msg, FALSE), FALSE, NULL); + //else + // purple_timeout_add(1, (GSourceFunc)skype_handle_received_message, (gpointer)g_string_free(msg, FALSE)); + XFlush(disp); + } + } +} + +static void +hide_skype() +{ + +} + +static gboolean +exec_skype() +{ + GError *error; + if (g_spawn_command_line_async("skype", &error)) + { + return TRUE; + } else { + purple_debug_error("skype", "Could not start skype: %s\n", error->message); + return FALSE; + } +} + +static gboolean +is_skype_running() +{ + const gchar *temp; + int pid; + gchar* stat_path; + FILE *fh; + gchar exec_name[15]; + struct stat *statobj = NULL; + //open /proc + GDir *procdir = g_dir_open("/proc", 0, NULL); + //go through directories that are numbers + while((temp = g_dir_read_name(procdir))) + { + pid = atoi(temp); + if (!pid) + continue; + // /proc/{pid}/stat contains lots of juicy info + stat_path = g_strdup_printf("/proc/%d/stat", pid); + fh = fopen(stat_path, "r"); + // fscanf (file, "%*d (%15[^)]" + pid = fscanf(fh, "%*d (%15[^)]", exec_name); + fclose(fh); + if (strcmp(exec_name, "skype") != 0) + { + g_free(stat_path); + continue; + } + //get uid/owner of stat file by using fstat() + g_stat(stat_path, statobj); + g_free(stat_path); + //compare uid/owner of stat file (in g_stat->st_uid) to getuid(); + if (statobj->st_uid == getuid()) + { + //this copy of skype was started by us + return TRUE; + } + } + g_dir_close(procdir); + return FALSE; +} + --- libpurple/plugins/Makefile.in.orig 2008-02-27 21:17:52.000000000 +0900 +++ libpurple/plugins/Makefile.in 2008-02-27 21:20:54.000000000 +0900 @@ -121,6 +121,14 @@ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(idle_la_LDFLAGS) \ $(LDFLAGS) -o $@ @PLUGINS_TRUE@am_idle_la_rpath = -rpath $(plugindir) +@PLUGINS_TRUE@libskype_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__libskype_la_SOURCES_DIST = libskype.c +@PLUGINS_TRUE@am_libskype_la_OBJECTS = libskype.lo +libskype_la_OBJECTS = $(am_libskype_la_OBJECTS) +libskype_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(libskype_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@PLUGINS_TRUE@am_libskype_la_rpath = -rpath $(plugindir) @PLUGINS_TRUE@joinpart_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am__joinpart_la_SOURCES_DIST = joinpart.c @PLUGINS_TRUE@am_joinpart_la_OBJECTS = joinpart.lo @@ -218,7 +226,7 @@ SOURCES = $(autoaccept_la_SOURCES) $(buddynote_la_SOURCES) \ $(ciphertest_la_SOURCES) $(codeinline_la_SOURCES) \ $(dbus_example_la_SOURCES) $(debug_example_la_SOURCES) \ - $(helloworld_la_SOURCES) $(idle_la_SOURCES) \ + $(helloworld_la_SOURCES) $(idle_la_SOURCES) $(libskype_la_SOURCES) \ $(joinpart_la_SOURCES) $(log_reader_la_SOURCES) \ $(newline_la_SOURCES) $(notify_example_la_SOURCES) \ $(offlinemsg_la_SOURCES) $(pluginpref_example_la_SOURCES) \ @@ -230,7 +238,7 @@ $(am__codeinline_la_SOURCES_DIST) \ $(am__dbus_example_la_SOURCES_DIST) \ $(am__debug_example_la_SOURCES_DIST) \ - $(am__helloworld_la_SOURCES_DIST) $(am__idle_la_SOURCES_DIST) \ + $(am__helloworld_la_SOURCES_DIST) $(am__idle_la_SOURCES_DIST) $(am__libskype_la_SOURCES_DIST) \ $(am__joinpart_la_SOURCES_DIST) \ $(am__log_reader_la_SOURCES_DIST) \ $(am__newline_la_SOURCES_DIST) \ @@ -525,6 +533,7 @@ debug_example_la_LDFLAGS = -module -avoid-version helloworld_la_LDFLAGS = -module -avoid-version idle_la_LDFLAGS = -module -avoid-version +libskype_la_LDFLAGS = -module -avoid-version joinpart_la_LDFLAGS = -module -avoid-version log_reader_la_LDFLAGS = -module -avoid-version newline_la_LDFLAGS = -module -avoid-version @@ -542,6 +551,7 @@ @PLUGINS_TRUE@ autoaccept.la \ @PLUGINS_TRUE@ buddynote.la \ @PLUGINS_TRUE@ idle.la \ +@PLUGINS_TRUE@ libskype.la \ @PLUGINS_TRUE@ joinpart.la \ @PLUGINS_TRUE@ log_reader.la \ @PLUGINS_TRUE@ newline.la \ @@ -567,6 +577,7 @@ @PLUGINS_TRUE@debug_example_la_SOURCES = debug_example.c @PLUGINS_TRUE@helloworld_la_SOURCES = helloworld.c @PLUGINS_TRUE@idle_la_SOURCES = idle.c +@PLUGINS_TRUE@libskype_la_SOURCES = libskype.c @PLUGINS_TRUE@joinpart_la_SOURCES = joinpart.c @PLUGINS_TRUE@log_reader_la_SOURCES = log_reader.c @PLUGINS_TRUE@newline_la_SOURCES = newline.c @@ -582,6 +593,7 @@ @PLUGINS_TRUE@ciphertest_la_LIBADD = $(GLIB_LIBS) @PLUGINS_TRUE@codeinline_la_LIBADD = $(GLIB_LIBS) @PLUGINS_TRUE@idle_la_LIBADD = $(GLIB_LIBS) +@PLUGINS_TRUE@libskype_la_LIBADD = $(GLIB_LIBS) @PLUGINS_TRUE@joinpart_la_LIBADD = $(GLIB_LIBS) @PLUGINS_TRUE@log_reader_la_LIBADD = $(GLIB_LIBS) @PLUGINS_TRUE@newline_la_LIBADD = $(GLIB_LIBS) @@ -704,6 +716,8 @@ $(helloworld_la_LINK) $(am_helloworld_la_rpath) $(helloworld_la_OBJECTS) $(helloworld_la_LIBADD) $(LIBS) idle.la: $(idle_la_OBJECTS) $(idle_la_DEPENDENCIES) $(idle_la_LINK) $(am_idle_la_rpath) $(idle_la_OBJECTS) $(idle_la_LIBADD) $(LIBS) +libskype.la: $(libskype_la_OBJECTS) $(libskype_la_DEPENDENCIES) + $(libskype_la_LINK) $(am_libskype_la_rpath) $(libskype_la_OBJECTS) $(libskype_la_LIBADD) $(LIBS) joinpart.la: $(joinpart_la_OBJECTS) $(joinpart_la_DEPENDENCIES) $(joinpart_la_LINK) $(am_joinpart_la_rpath) $(joinpart_la_OBJECTS) $(joinpart_la_LIBADD) $(LIBS) log_reader.la: $(log_reader_la_OBJECTS) $(log_reader_la_DEPENDENCIES) @@ -739,6 +753,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug_example.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helloworld.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libskype.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/joinpart.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log_reader.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newline.Plo@am__quote@ --- libpurple/plugins/Makefile.am.orig 2007-12-07 23:37:03.000000000 +0900 +++ libpurple/plugins/Makefile.am 2008-02-27 21:19:07.000000000 +0900 @@ -31,6 +31,7 @@ debug_example_la_LDFLAGS = -module -avoid-version helloworld_la_LDFLAGS = -module -avoid-version idle_la_LDFLAGS = -module -avoid-version +libskype_la_LDFLAGS = -module -avoid-version joinpart_la_LDFLAGS = -module -avoid-version log_reader_la_LDFLAGS = -module -avoid-version newline_la_LDFLAGS = -module -avoid-version @@ -51,6 +52,7 @@ autoaccept.la \ buddynote.la \ idle.la \ + libskype.la \ joinpart.la \ log_reader.la \ newline.la \ @@ -76,6 +78,7 @@ debug_example_la_SOURCES = debug_example.c helloworld_la_SOURCES = helloworld.c idle_la_SOURCES = idle.c +libskype_la_SOURCES = libskype.c joinpart_la_SOURCES = joinpart.c log_reader_la_SOURCES = log_reader.c newline_la_SOURCES = newline.c @@ -92,6 +95,7 @@ ciphertest_la_LIBADD = $(GLIB_LIBS) codeinline_la_LIBADD = $(GLIB_LIBS) idle_la_LIBADD = $(GLIB_LIBS) +libskype_la_LIBADD = $(GLIB_LIBS) joinpart_la_LIBADD = $(GLIB_LIBS) log_reader_la_LIBADD = $(GLIB_LIBS) newline_la_LIBADD = $(GLIB_LIBS)