diff options
-rw-r--r-- | libskypekit/libskypekit.cpp | 1845 | ||||
-rw-r--r-- | libskypekit/libskypekit.h | 361 |
2 files changed, 2206 insertions, 0 deletions
diff --git a/libskypekit/libskypekit.cpp b/libskypekit/libskypekit.cpp new file mode 100644 index 0000000..1c4a79c --- /dev/null +++ b/libskypekit/libskypekit.cpp @@ -0,0 +1,1845 @@ +/* + * libskypekit.cpp + * skypekit + * + * Created by MyMacSpace on 19/04/10. + * Copyright 2010 __MyCompanyName__. All rights reserved. + * + */ + +#include "libskypekit.h" + +Contact::Ref find_contact(Skype *skype, const char *who) +{ + SEString skypename = who; + ContactGroup::Ref cg; + Contact::Refs contacts; + if (!skype->GetHardwiredContactGroup(ContactGroup::ALL_KNOWN_CONTACTS, cg) || !cg->GetContacts(contacts)) { + //Unable to get contact list + return Contact::Ref(0); + } + + Sid::String identity; + for (uint i = 0; i < contacts.size(); i++) { + contacts[i]->GetIdentity(identity); + if (contacts[i] && identity == skypename) { + return contacts[i]; + } + } + + return Contact::Ref(0); +} + +ContactGroup::Ref find_group(Skype *skype, Sid::String& groupname) +{ + ContactGroup::Refs cgs; + ContactGroup::Ref cg; + if (!skype->GetCustomContactGroups(cgs)) + return ContactGroup::Ref(0); + + while((cg = cgs.peek())) + { + SEString displayname; + cg->GetPropGivenDisplayname(displayname); + if (displayname.equals(groupname)) + return cg; + } + + return ContactGroup::Ref(0); +} + +Conversation::Ref find_conversation(Skype *skype, const char *who) +{ + SEStringList participants; + Conversation::Ref conv; + Sid::String skypename = who; + + participants.append(skypename); + skype->GetConversationByParticipants(participants, conv, true, false); + + return conv; +} + +const char *skype_list_icon(PurpleAccount *account, PurpleBuddy *buddy) +{ + return "skype"; +} + +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,id,name) status = \ + purple_status_type_new_with_attrs( \ + prim, /* PurpleStatusPrimitive */ \ + id, /* id */ \ + _(name), /* name */ \ + TRUE, /* savable */ \ + TRUE, /* user_settable */ \ + FALSE, /* not independent */ \ + \ + /* Attributes - each status can have a message. */ \ + "message", \ + _("Mood"), \ + purple_value_new(PURPLE_TYPE_STRING), \ + NULL); \ + \ + \ + types = g_list_append(types, status) + + + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE, "ONLINE", "Online"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_AVAILABLE, "SKYPEME", "SkypeMe"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_AWAY, "AWAY", "Away"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_EXTENDED_AWAY, "NA", "Not Available"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_UNAVAILABLE, "DND", "Do Not Disturb"); + _SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_INVISIBLE, "INVISIBLE", "Invisible"); + //_SKYPE_ADD_NEW_STATUS(PURPLE_STATUS_OFFLINE, "Offline"); + + //User status could be 'logged out' - make not settable + status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "LOGGEDOUT", _("Logged out"), FALSE, FALSE, FALSE); + types = g_list_append(types, status); + + //User could be a SkypeOut contact + if (purple_account_get_bool(acct, "skypeout_online", TRUE)) + { + status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, "SKYPEOUT", _("SkypeOut"), FALSE, FALSE, FALSE); + } else { + status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "SKYPEOUT", _("SkypeOut"), FALSE, FALSE, FALSE); + } + types = g_list_append(types, status); + + //Offline people shouldn't have status messages + status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, "OFFLINE", _("Offline"), FALSE, TRUE, FALSE); + types = g_list_append(types, status); + + return types; +} + +void skype_login(PurpleAccount *acct) +{ + PurpleConnection *pc; + SEString AppToken = SKYPE_APP_TOKEN; + MyAccount::Ref A; + MySkype *skype = 0; + SETCPTransport *transport = 0; + SEString accountName = acct->username; + SEString accountPW = acct->password; + + pc = purple_account_get_connection(acct); + pc->proto_data = NULL; + pc->flags = (PurpleConnectionFlags) (PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR); + + transport = new SETCPTransport(purple_account_get_string(acct, "host", "127.0.0.1"), + purple_account_get_int(acct, "port", 8963)); + skype = new MySkype(transport); + pc->proto_data = skype; + + purple_debug_info("skypekit", "Pre init()\n"); + if (!skype->init(1, 1)) + { + purple_connection_error(pc, "Could not connect"); + return; + } + purple_debug_info("skypekit", "Pre start()\n"); + skype->start(); + purple_debug_info("skypekit", "Post start()\n"); + skype->SetApplicationToken(AppToken); + purple_debug_info("skypekit", "Set token to %s\n", SKYPE_APP_TOKEN); + + skype->pa = acct; + skype->pc = pc; + + if (skype->GetAccount(accountName, A)) + { + purple_debug_info("skypekit", "Got account\n"); + if (!A->LoginWithPassword(accountPW, false, true)) + { + purple_connection_error(pc, "Invalid username or password2"); + return; + } + } else { + purple_connection_error(pc, "Invalid username or password"); + return; + } + + A->SetAvailability(Contact::ONLINE); + +} + +void skype_close(PurpleConnection *pc) +{ + Skype *skype = (Skype *) pc->proto_data; + PurpleAccount *account = pc->account; + MyAccount::Ref A; + SEString accountName = account->username; + + if (!pc || !pc->proto_data) + return; + + purple_debug_info("skypekit", "Logging out\n"); + if(skype->GetAccount(accountName, A)) + { + purple_debug_info("skypekit", "...\n"); + A->Logout(true); + } + purple_debug_info("skypekit", "Logged out\n"); + + skype->stop(); + purple_debug_info("skypekit", "Stopped\n"); + skype->cleanup(); + purple_debug_info("skypekit", "Clean\n"); + + delete skype; +} + +int skype_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags) +{ + Skype *skype = (Skype *)gc->proto_data; + Message::Ref msg; + + ConversationRef conv = find_conversation(skype, who); + + if (conv->PostText(message, msg, true)) + { + return strlen(message); + } else { + return -1; + } +} + +int +skype_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) +{ + MySkype *skype = (MySkype *)gc->proto_data; + Conversation *conv = skype->newConversation(id); + Message::Ref msg; + + if (conv->PostText(message, msg, true)) + { + serv_got_chat_in(gc, id, purple_account_get_username(purple_connection_get_account(gc)), PURPLE_MESSAGE_SEND, + message, time(NULL)); + + return 1; + } else { + return -1; + } +} + +void +skype_set_chat_topic(PurpleConnection *gc, int id, const char *topic) +{ + MySkype *skype = (MySkype *)gc->proto_data; + Conversation *conv = skype->newConversation(id); + + conv->SetTopic(topic); +} + +void +skype_set_status(PurpleAccount *account, PurpleStatus *status) +{ + PurpleConnection *pc = purple_account_get_connection(account); + MySkype *skype = (MySkype *)pc->proto_data; + PurpleStatusType *type; + const char *message; + const char *statusid; + MyAccount::Ref A; + SEString accountName = account->username; + + if (!skype->GetAccount(accountName, A)) + return; + + type = purple_status_get_type(status); + statusid = purple_status_type_get_id(type); + if (g_str_equal(statusid, "ONLINE")) + { + A->SetAvailability(Contact::ONLINE); + } else if (g_str_equal(statusid, "SKYPEME")) + { + A->SetAvailability(Contact::SKYPE_ME); + } else if (g_str_equal(statusid, "AWAY")) + { + A->SetAvailability(Contact::AWAY); + } else if (g_str_equal(statusid, "NA")) + { + A->SetAvailability(Contact::NOT_AVAILABLE); + } else if (g_str_equal(statusid, "DND")) + { + A->SetAvailability(Contact::DO_NOT_DISTURB); + } else if (g_str_equal(statusid, "INVISIBLE")) + { + A->SetAvailability(Contact::INVISIBLE); + } else if (g_str_equal(statusid, "OFFLINE")) + { + A->SetAvailability(Contact::OFFLINE); + } + + message = purple_status_get_attr_string(status, "message"); + if (message == NULL) + message = ""; + else + message = purple_markup_strip_html(message); + + A->SetStrProperty(Contact::P_MOOD_TEXT, message); +} + +unsigned int +skype_send_typing(PurpleConnection *gc, const gchar *who, PurpleTypingState state) +{ + Skype *skype = (Skype *)gc->proto_data; + Message::Ref msg; + + //Don't send typing notifications to self + if (g_str_equal(who, gc->account->username)) + return 999; + + ConversationRef conv = find_conversation(skype, who); + + switch(state) + { + case PURPLE_NOT_TYPING: + conv->SetMyTextStatusTo(Participant::TEXT_NA); + break; + case PURPLE_TYPING: + conv->SetMyTextStatusTo(Participant::WRITING); + break; + case PURPLE_TYPED: + conv->SetMyTextStatusTo(Participant::READING); + break; + default: + break; + } + + return 999; +} + +const char * +skype_normalize(const PurpleAccount *acct, const char *who) +{ + static gchar *last_normalize = NULL; + g_free(last_normalize); + last_normalize = g_utf8_strdown(who, -1); + return last_normalize; +} + +void skype_add_deny(PurpleConnection *gc, const char *who) +{ + Skype *skype = (Skype *)gc->proto_data; + Contact::Ref contact = find_contact(skype, who); + + if (contact) + { + contact->SetBlocked(true); + } +} +void skype_rem_deny(PurpleConnection *gc, const char *who) +{ + Skype *skype = (Skype *)gc->proto_data; + Contact::Ref contact = find_contact(skype, who); + + if (contact) + { + contact->SetBlocked(false); + } +} +void skype_add_permit(PurpleConnection *gc, const char *who) +{ + Skype *skype = (Skype *)gc->proto_data; + Contact::Ref contact = find_contact(skype, who); + + if (contact) + { + contact->SetBuddyStatus(true); + } +} +void +skype_rem_permit(PurpleConnection *gc, const char *who) +{ + Skype *skype = (Skype *)gc->proto_data; + Contact::Ref contact = find_contact(skype, who); + + if (contact) + { + contact->SetBuddyStatus(false); + } +} + +char * +skype_status_text(PurpleBuddy *buddy) +{ + PurpleAccount *account = purple_buddy_get_account(buddy); + PurpleConnection *pc = purple_account_get_connection(account); + Skype *skype = (Skype *)pc->proto_data; + SEString mood; + + Contact::Ref contact = find_contact(skype, buddy->name); + + if (contact) + { + mood = contact->GetProp(Contact::P_MOOD_TEXT); + } + + if (mood.isEmpty()) + { + return NULL; + } + + return g_strdup(mood); +} + +void +skype_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *userinfo, gboolean full) +{ + PurplePresence *presence; + PurpleStatus *status; + const gchar *text; + + 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)); + + text = purple_status_get_attr_string(status, "message"); + if (text) + purple_notify_user_info_add_pair(userinfo, _("Mood"), text); +} + +void +skype_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) +{ + Skype *skype = (Skype *)pc->proto_data; + bool Found = false; + Contact::Ref NewContact; + SEString name = buddy->name; + SEString alias = buddy->alias; + ContactGroup::Ref NewGroup; + SEString groupname = group->name; + + Found = skype->GetContact(name, NewContact); + if (!Found) + return; + + NewContact->GiveDisplayName(alias); + NewContact->SendAuthRequest(""); + NewContact->SetBuddyStatus(true); + + NewGroup = find_group(skype, groupname); + if (!NewGroup) + { + skype->CreateCustomContactGroup(NewGroup); + NewGroup->GiveDisplayName(groupname); + } + NewGroup->AddContact(NewContact); + +} + +void +skype_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) +{ + Skype *skype = (Skype *)pc->proto_data; + bool Found = false; + Contact::Ref RemovableContact; + SEString name = buddy->name; + SEString alias = buddy->alias; + ContactGroup::Ref NewGroup; + SEString groupname = group->name; + + Found = skype->GetContact(name, RemovableContact); + + NewGroup = find_group(skype, groupname); + if (NewGroup) + NewGroup->RemoveContact(RemovableContact); +} + +const char * +skype_list_emblem(PurpleBuddy *buddy) +{ + PurpleConnection *pc = purple_account_get_connection(buddy->account); + Skype *skype = (Skype *)pc->proto_data; + Contact::Ref contact = find_contact(skype, buddy->name); + Contact::AVAILABILITY avail; + bool hasCap = false; + + if (contact) + { + contact->GetPropAvailability(avail); + switch(avail) + { + case Contact::ONLINE_FROM_MOBILE: + case Contact::AWAY_FROM_MOBILE: + case Contact::NOT_AVAILABLE_FROM_MOBILE: + case Contact::DO_NOT_DISTURB_FROM_MOBILE: + case Contact::SKYPE_ME_FROM_MOBILE: + return "mobile"; + break; + default: + break; + } + if (contact->HasCapability(Contact::CAPABILITY_MOBILE_DEVICE, hasCap) + && hasCap == true) + return "mobile"; + + if (contact->HasCapability(Contact::CAPABILITY_VIDEO, hasCap) + && hasCap == true) + return "video"; + } + + return NULL; +} + +gboolean +skype_offline_msg(const PurpleBuddy *buddy) +{ + return TRUE; +} + +// 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->status == status) + return; + + xfer->status = status; + + 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; + } + } +} + +gboolean +skype_can_receive_file(PurpleConnection *pc, const char *who) +{ + return TRUE; +} + +void +skype_xfer_cancel_send(PurpleXfer *xfer) +{ + purple_debug_info("skypekit", "xfer_cancel_send\n"); + Transfer *transfer = (Transfer *) xfer->data; + if (transfer != NULL) + { + transfer->Cancel(); + } +} + +void +skype_xfer_cancel_recv(PurpleXfer *xfer) +{ + purple_debug_info("skypekit", "xfer_cancel_recv\n"); + Transfer *transfer = (Transfer *) xfer->data; + if (transfer != NULL) + { + transfer->Cancel(); + } +} + +void +skype_xfer_init(PurpleXfer *xfer) +{ + purple_debug_info("skypekit", "xfer_init\n"); + Skype *skype = (Skype *)xfer->account->gc->proto_data; + SEFilenameList filenames; + Message::Ref msg; + TRANSFER_SENDFILE_ERROR error; + SEFilename error_filename; + SEString body; + + if (xfer == NULL) + return; + + purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_NOT_STARTED); + + if (xfer->type == PURPLE_XFER_SEND) + { + ConversationRef conv = find_conversation(skype, xfer->who); + + filenames.append(xfer->local_filename); + body += xfer->message; + if (!conv->PostFiles(filenames, body, error, error_filename) || error) + { + purple_debug_error("skypekit", "Failed to post %s (from %s) because %d\n", (const char *)error_filename, (const char *)body, error); + purple_xfer_error(xfer->type, xfer->account, xfer->who, "Error sending file"); + purple_xfer_cancel_local(xfer); + return; + } else { + purple_debug_info("skypekit", "Send file %s\n", xfer->local_filename); + purple_xfer_set_cancel_send_fnc(xfer, skype_xfer_cancel_send); + } + } else if (xfer->type == PURPLE_XFER_RECEIVE) + { + purple_debug_info("skypekit", "incoming file\n"); + bool result; + //transfer->Accept("path", result); + //if (!result) { + //accept failed + //} + Transfer *transfer = (Transfer *) xfer->data; + if (transfer != NULL) + { + transfer->Accept(purple_xfer_get_local_filename(xfer), result); + if (!result) + { + purple_debug_error("skypekit", "accept failed\n"); + } else { + purple_xfer_set_cancel_recv_fnc(xfer, skype_xfer_cancel_recv); + } + } + } +} + +void +skype_xfer_start(PurpleXfer *xfer) +{ + purple_debug_info("skypekit", "xfer_start\n"); +} + +void +skype_xfer_end(PurpleXfer *xfer) +{ + purple_debug_info("skypekit", "xfer_end\n"); +} + +//called when the incoming file request is denied +void +skype_xfer_request_denied(PurpleXfer *xfer) +{ + purple_debug_info("skypekit", "xfer_request_denied\n"); + Transfer *transfer = (Transfer *) xfer->data; + if (transfer != NULL) + { + transfer->Cancel(); + } +} + +PurpleXfer * +skype_xfer_new(PurpleConnection *pc, const char *who) +{ + purple_debug_info("skypekit", "xfer_new\n"); + PurpleXfer *xfer = NULL; + + xfer = purple_xfer_new(pc->account, PURPLE_XFER_SEND, who); + + purple_xfer_set_init_fnc(xfer, skype_xfer_init); + //purple_xfer_set_start_fnc(xfer, skype_xfer_start); + //purple_xfer_set_end_fnc(xfer, skype_xfer_end); + purple_xfer_set_cancel_send_fnc(xfer, skype_xfer_cancel_send); + //purple_xfer_set_cancel_recv_fnc(xfer, skype_xfer_cancel_recv); + + return xfer; +} + +void +skype_send_file(PurpleConnection *pc, const char *who, const char *filename) +{ + purple_debug_info("skypekit", "send_file\n"); + PurpleXfer *xfer = skype_xfer_new(pc, who); + + if (filename) + { + purple_xfer_request_accepted(xfer, filename); + } else { + purple_xfer_request(xfer); + } +} + +void +skype_get_info(PurpleConnection *pc, const gchar *username) +{ + PurpleNotifyUserInfo *user_info; + Skype *skype = (Skype *) pc->proto_data; + MyContact::Ref contact = find_contact(skype, username); + + SEIntList Keys; + SEIntDict Values; + + Keys.append(Contact::P_SKYPENAME); + Keys.append(Contact::P_FULLNAME); + Keys.append(Contact::P_MOOD_TEXT); + Keys.append(Contact::P_BIRTHDAY); + Keys.append(Contact::P_GENDER); + Keys.append(Contact::P_LANGUAGES); + Keys.append(Contact::P_COUNTRY); + Keys.append(Contact::P_GIVEN_AUTHLEVEL); + Keys.append(Contact::P_TIMEZONE); + Keys.append(Contact::P_NROF_AUTHED_BUDDIES); + Keys.append(Contact::P_ABOUT); + Values = contact->GetProps(Keys); + + user_info = purple_notify_user_info_new(); + + purple_notify_user_info_add_section_header(user_info, _("Contact Info")); + purple_notify_user_info_add_pair(user_info, _("Skype Name"), Values.find(Contact::P_SKYPENAME)); + purple_notify_user_info_add_pair(user_info, _("Full Name"), Values.find(Contact::P_FULLNAME)); + purple_notify_user_info_add_pair(user_info, _("Mood Text"), Values.find(Contact::P_MOOD_TEXT)); + + purple_notify_user_info_add_section_break(user_info); + + purple_notify_user_info_add_section_header(user_info, _("Personal Information")); + struct tm birthday_time; + purple_str_to_time(Values.find(Contact::P_BIRTHDAY), TRUE, &birthday_time, NULL, NULL); + purple_notify_user_info_add_pair(user_info, _("Birthday"), purple_date_format_short(&birthday_time)); + purple_notify_user_info_add_pair(user_info, _("Gender"), Values.find(Contact::P_GENDER).toUInt() == 1 ? _("Male") : _("Female")); + purple_notify_user_info_add_pair(user_info, _("Preferred Language"), Values.find(Contact::P_LANGUAGES)); + purple_notify_user_info_add_pair(user_info, _("Country"), Values.find(Contact::P_COUNTRY)); + purple_notify_user_info_add_pair(user_info, _("Authorization Granted"), Values.find(Contact::P_GIVEN_AUTHLEVEL).toUInt() == Contact::AUTHORIZED_BY_ME ? _("Yes") : _("No")); + purple_notify_user_info_add_pair(user_info, _("Blocked"), Values.find(Contact::P_GIVEN_AUTHLEVEL).toUInt() == Contact::BLOCKED_BY_ME ? _("Yes") : _("No")); + + gchar *temp; + purple_notify_user_info_add_pair(user_info, _("Timezone"), temp = g_strdup_printf("UMT %+.1f", ((double)Values.find(Contact::P_TIMEZONE).toUInt()) / 3600 - 24)); + g_free(temp); + + purple_notify_user_info_add_pair(user_info, _("Number of buddies"), Values.find(Contact::P_NROF_AUTHED_BUDDIES)); + + purple_notify_user_info_add_section_break(user_info); + + purple_notify_user_info_add_pair(user_info, NULL, Values.find(Contact::P_ABOUT)); + + purple_notify_userinfo(pc, username, user_info, NULL, NULL); + purple_notify_user_info_destroy(user_info); +} + +void +skype_alias_buddy(PurpleConnection *pc, const char *who, const char *alias) +{ + Skype *skype = (Skype *)pc->proto_data; + SEString displayname = alias; + Contact::Ref contact = find_contact(skype, who); + + contact->GiveDisplayName(displayname); +} + +void +skype_set_buddy_icon(PurpleConnection *pc, PurpleStoredImage *img) +{ + Skype *skype = (Skype *)pc->proto_data; + MyAccount::Ref A; + SEString accountName = pc->account->username; + SEBinary avatarImg; + + avatarImg.set(purple_imgstore_get_data(img), purple_imgstore_get_size(img)); + + if (!skype->GetAccount(accountName, A)) + return; + + A->SetBinProperty(Account::P_AVATAR_IMAGE, avatarImg); +} + +static const char * +skype_contact_status_to_id(int availability) +{ + const char *status; + switch(availability) + { + case Contact::ONLINE: + case Contact::ONLINE_FROM_MOBILE: + status = "ONLINE"; + break; + case Contact::AWAY: + case Contact::AWAY_FROM_MOBILE: + status = "AWAY"; + break; + case Contact::NOT_AVAILABLE: + case Contact::NOT_AVAILABLE_FROM_MOBILE: + status = "NA"; + break; + case Contact::SKYPE_ME: + case Contact::SKYPE_ME_FROM_MOBILE: + status = "SKYPEME"; + break; + case Contact::DO_NOT_DISTURB: + case Contact::DO_NOT_DISTURB_FROM_MOBILE: + status = "DND"; + break; + case Contact::INVISIBLE: + status = "INVISIBLE"; + break; + case Contact::OFFLINE: + case Contact::OFFLINE_BUT_VM_ABLE: + case Contact::OFFLINE_BUT_CF_ABLE: + //case Contact::PENDINGAUTH: + //case Contact::BLOCKED: + default: + status = "OFFLINE"; + break; + } + + return status; +} + +typedef struct { + gpointer me; + int prop; + SEString value; +} SKOnChangeStructHelper; + +gboolean +MyAccountOnChangeTimeout(gpointer data) +{ + SKOnChangeStructHelper *helper = (SKOnChangeStructHelper *) data; + MyAccount *account = (MyAccount *) helper->me; + gint prop = (int)helper->prop; + account->OnChangeThreadSafe(prop, helper->value); + g_free(helper); + return FALSE; +} + +void MyAccount::OnChange(int prop) +{ + SKOnChangeStructHelper *helper = g_new0(SKOnChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->prop = prop; + helper->value = this->GetProp(prop); + + SEStringList DebugStrings = this->getPropDebug(prop, helper->value); + purple_debug_info("skypekit", "Account %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + + purple_timeout_add(1, MyAccountOnChangeTimeout, helper); + return; +} +void MyAccount::OnChangeThreadSafe(int prop, const SEString& value) +{ + if (prop == Account::P_STATUS) + { + Account::STATUS status = (Account::STATUS) value.toUInt(); + size_t progress; + const gchar *status_text; + + switch(status) + { + case Account::LOGGED_OUT: + case Account::LOGGED_OUT_AND_PWD_SAVED: + case Account::LOGGING_OUT: + status_text = "Logged out"; + progress = 0; + break; + case Account::CONNECTING_TO_P2P: + status_text = "Connecting to P2P network"; + progress = 1; + break; + case Account::CONNECTING_TO_SERVER: + status_text = "Connecting to login server"; + progress = 2; + break; + case Account::LOGGING_IN: + status_text = "Waiting for response from server"; + progress = 3; + break; + case Account::INITIALIZING: + status_text = "Response OK, initialising structures"; + progress = 4; + break; + case Account::LOGGED_IN: + status_text = "Logged in"; + progress = 5; + break; + } + + purple_connection_update_progress(this->pc, status_text, progress, 6); + } + + if ((prop == Account::P_STATUS) && (value.toUInt() == Account::LOGGED_IN)) + { + purple_connection_set_state(this->pc, PURPLE_CONNECTED); + purple_debug_info("skypekit", "Login complete.\n"); + + //Download the buddy list + + ContactGroup::Ref SkypeBuddies; + Skype *skype = (Skype *)this->pc->proto_data; + skype->GetHardwiredContactGroup(ContactGroup::ALL_BUDDIES, SkypeBuddies); + + ContactRefs MyContactList; + SkypeBuddies->GetContacts(MyContactList); + + PurpleBuddy *buddy; + PurpleGroup *skype_group = purple_find_group("Skype"); + int ContactCount = MyContactList.size(); + for (int I = 0; I < ContactCount; I++) + { + SEString SkypeName; + MyContactList[I]->GetIdentity(SkypeName); + SEString Alias = MyContactList[I]->GetProp(Contact::P_DISPLAYNAME); + + buddy = purple_find_buddy(this->pa, SkypeName); + if (buddy == NULL) + { + buddy = purple_buddy_new(this->pa, SkypeName, Alias); + if (skype_group == NULL) + { + skype_group = purple_group_new("Skype"); + purple_blist_add_group(skype_group, NULL); + } + purple_blist_add_buddy(buddy, NULL, skype_group, NULL); + } + + SEString Availability = MyContactList[I]->GetProp(Contact::P_AVAILABILITY); + const char *status = skype_contact_status_to_id(Availability.toUInt()); + if (status != NULL) + purple_prpl_got_user_status(this->pa, (const char *)SkypeName, status, NULL); + }; + }; + + if ((prop == Account::P_STATUS) && (value.toUInt() == Account::LOGGED_OUT)) + { + purple_debug_info("skypekit", "Logout complete.\n"); + purple_connection_error(this->pc, _("\nSkype program closed")); + }; +}; + +PurpleXfer *find_skype_xfer(MyTransfer *transfer) +{ + PurpleXfer *xfer; + PurpleXferType purpletype; + SEIntList Keys; + SEIntDict Values; + GList *xfers; + + Keys.append(Transfer::P_TYPE); + Keys.append(Transfer::P_STATUS); + Keys.append(Transfer::P_FILENAME); + Keys.append(Transfer::P_FILEPATH); + Keys.append(Transfer::P_FILESIZE); + Keys.append(Transfer::P_PARTNER_HANDLE); + Values = transfer->GetProps(Keys); + + purpletype = (Values.find(Transfer::P_TYPE).toUInt() == Transfer::INCOMING ? PURPLE_XFER_RECEIVE : PURPLE_XFER_SEND); + + if (transfer->xfer != NULL) + return transfer->xfer; + + gulong transfer_filesize = atoul(Values.find(Transfer::P_FILESIZE)); + + xfers = purple_xfers_get_all(); + for(; xfers; xfers = g_list_next(xfers)) + { + xfer = (PurpleXfer *)xfers->data; + if (xfer->type == purpletype && + xfer->account == transfer->pa && + xfer->size == transfer_filesize && + (!xfer->local_filename || + g_str_equal(xfer->local_filename, Values.find(Transfer::P_FILEPATH))) && + xfer->who && g_str_equal(xfer->who, Values.find(Transfer::P_PARTNER_HANDLE))) + { + purple_debug_info("skypekit", "found the xfer we're looking for\n"); + //as best as we can tell, this is the xfer we're looking for + transfer->xfer = xfer; + xfer->data = transfer->me(); + + return xfer; + } + } + + //Didn't find the xfer, make a new one + xfer = purple_xfer_new(transfer->pa, purpletype, Values.find(Transfer::P_PARTNER_HANDLE)); + + if (xfer) + { + if (!Values.find(Transfer::P_FILENAME).isEmpty()) + purple_xfer_set_filename(xfer, Values.find(Transfer::P_FILENAME)); + if (!Values.find(Transfer::P_FILEPATH).isEmpty()) + purple_xfer_set_local_filename(xfer, Values.find(Transfer::P_FILEPATH)); + purple_debug_info("skypekit", "filesize %lu\n", transfer_filesize); + purple_xfer_set_size(xfer, transfer_filesize); + + purple_xfer_set_init_fnc(xfer, skype_xfer_init); + purple_xfer_set_cancel_recv_fnc(xfer, skype_xfer_cancel_recv); + purple_xfer_set_cancel_send_fnc(xfer, skype_xfer_cancel_send); + + xfer->data = transfer->me(); + transfer->xfer = xfer; + + purple_xfer_add(xfer); + purple_debug_info("skypekit", "had to add a new xfer\n"); + } + + return xfer; +} + +//called when the user accepts an incomming call from the ui +static gboolean +skype_call_accept(Conversation *conversation) +{ + conversation->JoinLiveSession(); + return FALSE; +} + +//called when the user ends an incomming call from the ui +static gboolean +skype_call_end(Conversation *conversation) +{ + conversation->LeaveLiveSession(); + return FALSE; +} + +static void +skype_media_state_changed(PurpleMedia *media, PurpleMediaState state, gchar *session_id, gchar *participant, MyConversation *conversation) +{ + if (state == PURPLE_MEDIA_STATE_END) + { + conversation->media = NULL; + } +} + +static void +skype_stream_info_changed(PurpleMedia *media, PurpleMediaInfoType type, gchar *session_id, gchar *participant, gboolean local, + Conversation *conversation) +{ + if (type == PURPLE_MEDIA_INFO_ACCEPT) { + skype_call_accept(conversation); + } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) { + skype_call_end(conversation); + } +} + +void +skype_handle_incoming_call(PurpleConnection *pc, MyConversation::Ref &conversation, const char *who, const char *display_name) +{ + PurpleAccount *account; + account = purple_connection_get_account(pc); + if (purple_media_manager_get()) + { + PurpleMedia *media; + media = purple_media_manager_create_media(purple_media_manager_get(), account, "fsrtpconference", who, FALSE); + if (media != NULL) + { + purple_media_set_prpl_data(media, conversation->me()); + conversation->media = media; + + purple_media_add_stream(media, "skype-audio1", who, PURPLE_MEDIA_AUDIO, FALSE, "nice", 0, NULL); + purple_media_add_stream(media, "skype-audio2", who, PURPLE_MEDIA_AUDIO, FALSE, "rawudp", 0, NULL); + //g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_NEW, "skype-audio", who); + //g_signal_emit_by_name(media, "state-changed", 0, PURPLE_MEDIA_STATE_NEW, "skype-audio", who); + + g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(skype_call_accept), conversation->me()); + g_signal_connect(G_OBJECT(media), "state-changed", G_CALLBACK(skype_media_state_changed), conversation->me()); + g_signal_connect(G_OBJECT(media), "stream-info", G_CALLBACK(skype_stream_info_changed), conversation->me()); + } else { + purple_debug_info("skype_media", "purple_mmcm returned NULL\n"); + } + } else //if (!purple_media_manager_get()) + { + gchar *temp = g_strdup_printf(_("%s (%s) is calling you."), display_name, who); + purple_request_action(conversation->me(), _("Incoming Call"), temp, + _("Do you want to accept their call?"), + 0, account, who, NULL, conversation->me(), 2, + _("_Accept"), G_CALLBACK(skype_call_accept), + _("_Reject"), G_CALLBACK(skype_call_end)); + g_free(temp); + + } +} + +void +skype_handle_call_got_ended(PurpleConnection *pc, MyConversation::Ref &conversation) +{ + if (purple_media_manager_get()) + { + if (conversation->media) + purple_media_manager_remove_media(purple_media_manager_get(), conversation->media); + } else //if (!purple_media_manager_get()) + { + purple_request_close_with_handle(conversation->me()); + } +} + +gboolean +skype_initiate_media(PurpleAccount *account, const char *who, PurpleMediaSessionType type) +{ + PurpleMedia *media; + PurpleConnection *pc = purple_account_get_connection(account); + Skype *skype = (Skype *)pc->proto_data; + + //Use skype's own audio/video stuff for now + media = purple_media_manager_create_media(purple_media_manager_get(), account, "fsrtpconference", who, TRUE); + MyConversation::Ref conv = find_conversation(skype, who); + + if (media != NULL) + { + conv->JoinLiveSession(); + purple_media_set_prpl_data(media, conv->me()); + conv->media = media; + + g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(skype_call_accept), conv->me()); + g_signal_connect(G_OBJECT(media), "state-changed", G_CALLBACK(skype_media_state_changed), conv->me()); + g_signal_connect(G_OBJECT(media), "stream-info", G_CALLBACK(skype_stream_info_changed), conv->me()); + } else { + purple_debug_info("skype_media", "media is NULL\n"); + } + return TRUE; +} + +PurpleMediaCaps +skype_get_media_caps(PurpleAccount *account, const char *who) +{ + PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE; + Contact::Ref contact; + PurpleConnection *pc = purple_account_get_connection(account); + Skype *skype = (Skype *)pc->proto_data; + bool hasNoAudioCap, hasVideoCap; + + contact = find_contact(skype, who); + contact->HasCapability(Contact::CAPABILITY_VOICE_EVER, hasNoAudioCap); + if (!hasNoAudioCap) + caps = PURPLE_MEDIA_CAPS_AUDIO; + + //TODO: Make video work + //contact->HasCapability(Contact::CAPABILITY_VIDEO, hasVideoCap); + //if (hasVideoCap) + // caps |= PURPLE_MEDIA_CAPS_AUDIO_VIDEO; + + return caps; +} + +typedef struct { + gpointer me; + ConversationRef conversation; + Conversation::LIST_TYPE type; + bool added; +} SKOnConversationListChangeStructHelper; + +gboolean +MySkypeOnConversationListChangeTimeout(gpointer data) +{ + SKOnConversationListChangeStructHelper *helper = (SKOnConversationListChangeStructHelper *)data; + if (helper) + { + MySkype *skype = (MySkype *) helper->me; + if (skype) + skype->OnConversationListChangeThreadSafe(helper->conversation, helper->type, helper->added); + } + g_free(helper); + return FALSE; +} + +void MySkype::OnConversationListChange( + const ConversationRef &conversation, + const Conversation::LIST_TYPE &type, + const bool &added) +{ + SKOnConversationListChangeStructHelper *helper = g_new0(SKOnConversationListChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->conversation = conversation; + helper->type = type; + helper->added = added; + purple_timeout_add(1, MySkypeOnConversationListChangeTimeout, helper); +} + +void MySkype::OnConversationListChangeThreadSafe( + const ConversationRef &conv, + const Conversation::LIST_TYPE &type, + const bool &added) +{ + MyConversation::Ref conversation = conv; + if (type == Conversation::LIVE_CONVERSATIONS) + { + uint LiveStatus; + SEIntList Keys; + SEIntDict Values; + + Keys.append(Conversation::P_DISPLAYNAME); + Keys.append(Conversation::P_LOCAL_LIVESTATUS); + Values = conversation->GetProps(Keys); + LiveStatus = Values.find(Conversation::P_LOCAL_LIVESTATUS).toUInt(); + + if (LiveStatus == Conversation::RINGING_FOR_ME) + { + ParticipantRefs CallerList; + conversation->GetParticipants(CallerList, Conversation::OTHER_CONSUMERS); + SEString PartList = ""; + for (unsigned I = 0; I < CallerList.size(); I++) + { + PartList = PartList + ", " + (const char*)CallerList[I]->GetProp(Participant::P_IDENTITY); + } + + skype_handle_incoming_call(this->pc, conversation, PartList, Values.find(Conversation::P_DISPLAYNAME)); + } else if (LiveStatus == Conversation::NONE) + { + skype_handle_call_got_ended(this->pc, conversation); + } + } +} + +typedef struct { + gpointer me; + Message::Ref message; + bool changesInboxTimestamp; + Message::Ref supersedesHistoryMessage; + Conversation::Ref conversation; +} SKOnMessageStructHelper; + +gboolean +MySkypeOnMessageTimeout(gpointer data) +{ + SKOnMessageStructHelper *helper = (SKOnMessageStructHelper *) data; + if (helper) + { + MySkype *skype = (MySkype *) helper->me; + if (skype) + skype->OnMessageThreadSafe(helper->message, helper->changesInboxTimestamp, helper->supersedesHistoryMessage, helper->conversation); + } + g_free(helper); + return FALSE; +} +void MySkype::OnMessage(const Message::Ref& message, const bool& changesInboxTimestamp, + const Message::Ref& supersedesHistoryMessage, + const Conversation::Ref& conversation) +{ + SKOnMessageStructHelper *helper = g_new0(SKOnMessageStructHelper, 1); + helper->me = (gpointer) this; + helper->message = message; + helper->changesInboxTimestamp = changesInboxTimestamp; + helper->supersedesHistoryMessage = supersedesHistoryMessage; + helper->conversation = conversation; + purple_timeout_add(1, MySkypeOnMessageTimeout, helper); + return; +} +void MySkype::OnMessageThreadSafe(const Message::Ref& message, const bool& changesInboxTimestamp, + const Message::Ref& supersedesHistoryMessage, + const Conversation::Ref& conversation) +{ + SEIntList Keys; + SEIntDict Values; + int MessageType = message->GetProp(Message::P_TYPE).toUInt(); + int ConvType = conversation->GetProp(Conversation::P_TYPE).toUInt(); + + SEStringList DebugStrings = message->getPropDebug(Message::P_TYPE, MessageType); + purple_debug_info("skypekit", "MessageMsg %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + DebugStrings = conversation->getPropDebug(Conversation::P_TYPE, ConvType); + purple_debug_info("skypekit", "MessageConv %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + + if (MessageType == Message::POSTED_EMOTE || MessageType == Message::POSTED_TEXT) + { + Keys.append(Message::P_AUTHOR); + Keys.append(Message::P_BODY_XML); + Keys.append(Message::P_TIMESTAMP); + Keys.append(Message::P_ORIGINALLY_MEANT_FOR); + Values = message->GetProps(Keys); + gchar *message_body; + + if (MessageType == Message::POSTED_EMOTE) + { + const gchar *tempmsg = Values.find(Message::P_BODY_XML); + message_body = g_strconcat("/me ", tempmsg, NULL); + } else { + message_body = g_strdup(Values.find(Message::P_BODY_XML)); + } + + purple_debug_info("skypekit", "Message %s : %s : %s\n", (const char*)Values[0], (const char*)Values[1], (const char*)Values[2]); + + if (ConvType == Conversation::DIALOG) + { + if (!Values.find(Message::P_AUTHOR).equals(this->pa->username)) + { + serv_got_im(this->pc, Values.find(Message::P_AUTHOR), message_body, + PURPLE_MESSAGE_RECV, Values.find(Message::P_TIMESTAMP).toUInt()); + } else { + //Check that we haven't sent the message from here + //serv_got_im(this->pc, Values.find(Message::P_ORIGINALLY_MEANT_FOR), message_body, + // PURPLE_MESSAGE_SEND, Values.find(Message::P_TIMESTAMP).toUInt()); + } + } else if (ConvType == Conversation::CONFERENCE) + { + int ConvId = conversation->getOID(); + serv_got_chat_in(this->pc, ConvId, Values.find(Message::P_AUTHOR), PURPLE_MESSAGE_RECV, + message_body, Values.find(Message::P_TIMESTAMP).toUInt()); + + } + g_free(message_body); + } else if (MessageType == Message::POSTED_FILES) { + // someone sending a file + MyTransfer::Refs transfers; + if (message->GetTransfers(transfers)) { + for(guint i = 0; i < transfers.size(); i++) { + MyTransfer::Ref transfer = transfers[i]; + Keys.append(Transfer::P_TYPE); + Keys.append(Transfer::P_STATUS); + Keys.append(Transfer::P_FILENAME); + Keys.append(Transfer::P_FILESIZE); + Keys.append(Transfer::P_PARTNER_HANDLE); + Values = transfer->GetProps(Keys); + if (Values.find(Transfer::P_STATUS).toUInt() < Transfer::TRANSFERRING) + { + if (Values.find(Transfer::P_TYPE).toUInt() == Transfer::INCOMING) { + purple_debug_info("skypekit", "New incoming file\n"); + PurpleXfer *xfer = purple_xfer_new(this->pa, PURPLE_XFER_RECEIVE, Values.find(Transfer::P_PARTNER_HANDLE)); + if (xfer) + { + gulong filesize = atoul(Values.find(Transfer::P_FILESIZE)); + purple_xfer_set_init_fnc(xfer, skype_xfer_init); + purple_xfer_set_request_denied_fnc(xfer, skype_xfer_request_denied); + purple_xfer_set_filename(xfer, Values.find(Transfer::P_FILENAME)); + purple_debug_info("skypekit", "filesize %lu\n", filesize); + purple_xfer_set_size(xfer, filesize); + + xfer->data = transfer->me(); + transfer->xfer = xfer; + + purple_xfer_request(xfer); + } + } else if (Values.find(Transfer::P_TYPE).toUInt() == Transfer::OUTGOING) { + if (!transfer->xfer) + { + purple_debug_info("skypekit", "no PurpleXfer for this transfer\n"); + //find the current transfer, or create one if we cant find it + transfer->xfer = find_skype_xfer(transfer->me()); + } + } + } + } + } + } else if (MessageType == Message::SPAWNED_CONFERENCE) { + //Turned this im into a chat + // see MyConversation::OnSpawnConference() + } else if (MessageType == Message::ADDED_CONSUMERS) { + //People added to this chat + int ConvId = conversation->getOID(); + PurpleConversation *conv = purple_find_chat(this->pc, ConvId); + Keys.append(Message::P_IDENTITIES); + Values = message->GetProps(Keys); + purple_debug_info("skypekit", "added users %s to chat %d\n", (const char *)Values.find(Message::P_IDENTITIES), ConvId); + purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), Values.find(Message::P_IDENTITIES), NULL, PURPLE_CBFLAGS_NONE, TRUE); + } else if (MessageType == Message::RETIRED_OTHERS) { + // Other user was kicked + int ConvId = conversation->getOID(); + PurpleConversation *conv = purple_find_chat(this->pc, ConvId); + Keys.append(Message::P_IDENTITIES); + Keys.append(Message::P_LEAVEREASON); + Keys.append(Message::P_REASON); + Values = message->GetProps(Keys); + purple_debug_info("skypekit", "removed users %s from chat %d\n", (const char *)Values.find(Message::P_IDENTITIES), ConvId); + purple_conv_chat_remove_user(PURPLE_CONV_CHAT(conv), Values.find(Message::P_IDENTITIES), Values.find(Message::P_REASON)); + } else if (MessageType == Message::RETIRED) { + // User left conversation + int ConvId = conversation->getOID(); + serv_got_chat_left(this->pc, ConvId); + } + + //Mark all messages as read/consumed + conversation->SetConsumedHorizon(time(NULL)); +} + +gboolean +MyConversationOnSpawnConferenceTimeout(gpointer data) +{ + MyConversation *conv = (MyConversation *) data; + conv->OnSpawnConferenceThreadSafe(conv->ref()); + return FALSE; +} +void MyConversation::OnSpawnConference(const ConversationRef& spawned) +{ + MyConversation::Ref conv = spawned; + purple_timeout_add(1, MyConversationOnSpawnConferenceTimeout, conv->me()); +} +//ignore the 'this' keyword here, TODO fix the timeout call +void MyConversation::OnSpawnConferenceThreadSafe(const ConversationRef& spawned) +{ + SEIntList Keys; + SEIntDict Values; + MyConversation::Ref conversation = spawned; + int ConvId = conversation->getOID(); + + Keys.append(Conversation::P_IDENTITY); + Keys.append(Conversation::P_DISPLAYNAME); + Values = conversation->GetProps(Keys); + + purple_debug_info("skypekit", "joined chat %s %d\n", (const char *)Values.find(Conversation::P_IDENTITY), ConvId); + PurpleConversation *conv = serv_got_joined_chat(this->pc, ConvId, Values.find(Conversation::P_IDENTITY)); + purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, Values.find(Conversation::P_DISPLAYNAME)); + + ParticipantRefs participants; + conversation->GetParticipants(participants); + ParticipantRef participant; + while((participant = participants.peek())) + { + SEString who; + participant->GetPropIdentity(who); + purple_debug_info("skypekit", "adding user %s to chat %d\n", (const char *) who, ConvId); + purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), who, NULL, PURPLE_CBFLAGS_NONE, FALSE); + } +} + +gboolean +MyConversationOnChangeTimeout(gpointer data) +{ + SKOnChangeStructHelper *helper = (SKOnChangeStructHelper *) data; + MyConversation *conversation = (MyConversation *) helper->me; + int prop = (int) helper->prop; + conversation->OnChangeThreadSafe(prop, helper->value); + g_free(helper); + return FALSE; +} +void MyConversation::OnChange(int prop) +{ + SKOnChangeStructHelper *helper = g_new0(SKOnChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->prop = prop; + helper->value = this->GetProp(prop); + + SEStringList DebugStrings = this->getPropDebug(prop, helper->value); + purple_debug_info("skypekit", "Conversation %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + + purple_timeout_add(1, MyConversationOnChangeTimeout, helper); + return; +} +void MyConversation::OnChangeThreadSafe(int prop, const SEString &value) +{ +} + +gboolean +MyMessageOnChangeTimeout(gpointer data) +{ + SKOnChangeStructHelper *helper = (SKOnChangeStructHelper *) data; + MyMessage *message = (MyMessage *) helper->me; + int prop = (int) helper->prop; + message->OnChangeThreadSafe(prop, helper->value); + g_free(helper); + return FALSE; +} +void MyMessage::OnChange(int prop) +{ + SKOnChangeStructHelper *helper = g_new0(SKOnChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->prop = prop; + helper->value = this->GetProp(prop); + + SEStringList DebugStrings = this->getPropDebug(prop, helper->value); + purple_debug_info("skypekit", "Message %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + + purple_timeout_add(1, MyMessageOnChangeTimeout, helper); + return; +} +void MyMessage::OnChangeThreadSafe(int prop, const SEString &value) +{ + SEIntList Keys; + SEIntDict Values; + Keys.append(Message::P_ORIGINALLY_MEANT_FOR); + Keys.append(Message::P_BODY_XML); + Keys.append(Message::P_CONVO_ID); + int MsgId = this->getOID(); + + if (prop == Message::P_SENDING_STATUS) + { + Values = this->GetProps(Keys); + + const gchar *tempmsg = Values.find(Message::P_BODY_XML); + const gchar *otherperson = Values.find(Message::P_ORIGINALLY_MEANT_FOR); + gchar *message_number = g_strdup_printf("%du", MsgId); + + if (value.toUInt() == Message::SENT) + { + purple_signal_emit(purple_conversations_get_handle(), "received-im-ack", + this->pa, otherperson, message_number); + } else if (value.toUInt() == Message::SENDING) + { + purple_signal_emit(purple_conversations_get_handle(), "waiting-im-ack", + this->pa, otherperson, tempmsg, message_number); + } + + g_free(message_number); + } +} + +gboolean +MyParticipantOnChangeTimeout(gpointer data) +{ + SKOnChangeStructHelper *helper = (SKOnChangeStructHelper *) data; + MyParticipant *participant = (MyParticipant *) helper->me; + int prop = (int) helper->prop; + participant->OnChangeThreadSafe(prop, helper->value); + g_free(helper); + return FALSE; +} +void MyParticipant::OnChange(int prop) +{ + SKOnChangeStructHelper *helper = g_new0(SKOnChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->prop = prop; + helper->value = this->GetProp(prop); + + SEStringList DebugStrings = this->getPropDebug(prop, helper->value); + purple_debug_info("skypekit", "Participant %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + + purple_timeout_add(1, MyParticipantOnChangeTimeout, helper); + return; +} +void MyParticipant::OnChangeThreadSafe(int prop, const SEString &value) +{ + if (prop == Participant::P_TEXT_STATUS) + { + // typing notifications + int TextStatus = value.toUInt(); + PurpleTypingState typingState; + switch(TextStatus) + { + default: + case Participant::TEXT_NA: + typingState = PURPLE_NOT_TYPING; + break; + case Participant::READING: + typingState = PURPLE_TYPED; + break; + case Participant::WRITING: + case Participant::WRITING_AS_ANGRY: + case Participant::WRITING_AS_CAT: + typingState = PURPLE_TYPING; + break; + } + + SEString Name = this->GetProp(Participant::P_IDENTITY); + purple_debug_info("skypekit", "Got typing notification from %s\n", (const char *) Name); + serv_got_typing(this->pc, (const char *)Name, 10, typingState); + } +} + +gboolean +MyTransferOnChangeTimeout(gpointer data) +{ + SKOnChangeStructHelper *helper = (SKOnChangeStructHelper *) data; + MyTransfer *transfer = (MyTransfer *) helper->me; + int prop = (int) helper->prop; + transfer->OnChangeThreadSafe(prop, helper->value); + g_free(helper); + return FALSE; +} +void MyTransfer::OnChange(int prop) +{ + SKOnChangeStructHelper *helper = g_new0(SKOnChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->prop = prop; + helper->value = this->GetProp(prop); + + SEStringList DebugStrings = this->getPropDebug(prop, helper->value); + purple_debug_info("skypekit", "Transfer %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + + purple_timeout_add(1, MyTransferOnChangeTimeout, helper); + return; +} +void MyTransfer::OnChangeThreadSafe(int prop, const SEString &value) +{ + if (this->xfer == NULL) + { + this->xfer = find_skype_xfer(this); + } + + if (this->xfer != NULL) + { + if(prop == Transfer::P_STATUS) + { + switch(value.toUInt()) + { + case Transfer::NEW: + case Transfer::WAITING_FOR_ACCEPT: + purple_xfer_set_status(this->xfer, PURPLE_XFER_STATUS_NOT_STARTED); + break; + case Transfer::CONNECTING: + purple_xfer_set_status(this->xfer, PURPLE_XFER_STATUS_ACCEPTED); + break; + case Transfer::TRANSFERRING: + case Transfer::TRANSFERRING_OVER_RELAY: + purple_xfer_set_status(this->xfer, PURPLE_XFER_STATUS_STARTED); + break; + case Transfer::COMPLETED: + purple_xfer_set_completed(this->xfer, TRUE); + break; + case Transfer::CANCELLED_BY_REMOTE: + purple_xfer_cancel_remote(this->xfer); + break; + case Transfer::CANCELLED: + purple_xfer_cancel_local(this->xfer); + break; + case Transfer::FAILED: + purple_xfer_error(this->xfer->type, this->pa, this->xfer->who, "Transfer failed"); + break; + case Transfer::PAUSED: + case Transfer::REMOTELY_PAUSED: + case Transfer::PLACEHOLDER: + case Transfer::OFFER_FROM_OTHER_INSTANCE: + default: + break; + } + purple_xfer_update_progress(this->xfer); + } else if (prop == Transfer::P_BYTESTRANSFERRED) + { + if (purple_xfer_get_status(this->xfer) < PURPLE_XFER_STATUS_STARTED) + purple_xfer_set_status(this->xfer, PURPLE_XFER_STATUS_STARTED); + purple_xfer_set_bytes_sent(this->xfer, atol(value)); + purple_xfer_update_progress(this->xfer); + } + } +} + +void +skypekit_auth_allow(gpointer userdata) +{ + ContactRef contact = * (ContactRef *)userdata; + contact->SetBuddyStatus(true); +} +void +skypekit_auth_deny(gpointer userdata) +{ + ContactRef contact = * (ContactRef *)userdata; + contact->SetBlocked(true); +} + +static void +skype_call_user_from_blist(PurpleBlistNode *node, Conversation *conv) +{ + if(PURPLE_BLIST_NODE_IS_BUDDY(node)) + { + conv->JoinLiveSession(); + } +} +static void +skype_hangup_from_blist(PurpleBlistNode *node, Conversation *conv) +{ + if(PURPLE_BLIST_NODE_IS_BUDDY(node)) + { + conv->LeaveLiveSession(); + } +} + +static void +skype_request_auth_from_blist(PurpleBlistNode *node, gpointer data) +{ + PurpleBuddy *buddy; + + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) + return; + + buddy = (PurpleBuddy *)node; + + PurpleConnection *pc = purple_account_get_connection(buddy->account); + Skype *skype = (Skype *)pc->proto_data; + Contact::Ref contact = find_contact(skype, buddy->name); + + contact->SendAuthRequest(""); +} + +static GList * +skype_node_menu(PurpleBlistNode *node) +{ + GList *m = NULL; + PurpleMenuAction *act; + + if(PURPLE_BLIST_NODE_IS_BUDDY(node)) + { + PurpleBuddy *buddy = (PurpleBuddy *)node; + PurpleConnection *pc = purple_account_get_connection(buddy->account); + Skype *skype = (Skype *)pc->proto_data; + + if (!purple_media_manager_get()) + { + MyConversation::Ref conv = find_conversation(skype, buddy->name); + Conversation::LOCAL_LIVESTATUS liveStatus; + conv->GetPropLocalLivestatus(liveStatus); + + if (liveStatus == Conversation::NONE || + liveStatus == Conversation::OTHERS_ARE_LIVE || + liveStatus == Conversation::RECENTLY_LIVE) + { + act = purple_menu_action_new(_("Call..."), + PURPLE_CALLBACK(skype_call_user_from_blist), + conv->me(), NULL); + m = g_list_append(m, act); + } else if (liveStatus != Conversation::OTHERS_ARE_LIVE_FULL) + { + act = purple_menu_action_new(_("End Call..."), + PURPLE_CALLBACK(skype_hangup_from_blist), + conv->me(), NULL); + m = g_list_append(m, act); + + } + } + + if (!PURPLE_BUDDY_IS_ONLINE(buddy)) + { + Contact::Ref contact = find_contact(skype, buddy->name); + Contact::AVAILABILITY avail; + contact->GetPropAvailability(avail); + + if (avail == Contact::PENDINGAUTH) + { + act = purple_menu_action_new(_("Re-request authorization"), + PURPLE_CALLBACK(skype_request_auth_from_blist), + NULL, NULL); + m = g_list_append(m, act); + } + } + } + + return m; +} + + +typedef struct { + gpointer me; + ContactRef contact; +} SKOnContactGroupChangeStructHelper; +gboolean +MyContactGroupOnChangeTimeout(gpointer data) +{ + SKOnContactGroupChangeStructHelper *helper = (SKOnContactGroupChangeStructHelper *) data; + MyContactGroup *contactgroup = (MyContactGroup *) helper->me; + contactgroup->OnChangeThreadSafe(helper->contact); + g_free(helper); + return FALSE; +} +void MyContactGroup::OnChange(const ContactRef& contact) +{ + SKOnContactGroupChangeStructHelper *helper = g_new0(SKOnContactGroupChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->contact = contact; + purple_timeout_add(1, MyContactGroupOnChangeTimeout, helper); + return; +} +void MyContactGroup::OnChangeThreadSafe(const ContactRef& contact) +{ + purple_debug_info("skypekit", "ContactGroup::OnChange\n"); + + TYPE type; + this->GetPropType(type); + if (type == ContactGroup::CONTACTS_WAITING_MY_AUTHORIZATION) + { + SEString ContactSkypeName; + SEString ContactDisplayName; + SEString AuthRequestText; + contact->GetPropSkypename(ContactSkypeName); + contact->GetPropReceivedAuthrequest(AuthRequestText); + contact->GetPropDisplayname(ContactDisplayName); + + purple_account_request_authorization(this->pa, ContactSkypeName, NULL, ContactDisplayName, + AuthRequestText, FALSE, skypekit_auth_allow, skypekit_auth_deny, + &(contact->ref())); + + //contact->IgnoreAuthRequest(); + } +} + +gboolean +MyContactOnChangeTimeout(gpointer data) +{ + SKOnChangeStructHelper *helper = (SKOnChangeStructHelper *) data; + MyContact *contact = (MyContact *) helper->me; + int prop = (int) helper->prop; + contact->OnChangeThreadSafe(prop, helper->value); + g_free(helper); + return FALSE; +} +void MyContact::OnChange(int prop) +{ + SKOnChangeStructHelper *helper = g_new0(SKOnChangeStructHelper, 1); + helper->me = (gpointer) this; + helper->prop = prop; + helper->value = this->GetProp(prop); + + SEStringList DebugStrings = this->getPropDebug(prop, helper->value); + purple_debug_info("skypekit", "Contact %s %s\n", (const char *)DebugStrings[1], (const char *)DebugStrings[2]); + + purple_timeout_add(1, MyContactOnChangeTimeout, helper); + return; +} + +void +skype_auth_allow(gpointer pointer) +{ + Contact *contact = (Contact *)pointer; + //contact->GiveAuthlevel(Contact::AUTHORIZED_BY_ME); + contact->SetBuddyStatus(true); +} + +void +skype_auth_deny(gpointer pointer) +{ + Contact *contact = (Contact *)pointer; + //contact->GiveAuthlevel(Contact::BLOCKED_BY_ME); + contact->SetBlocked(true); +} + +void MyContact::OnChangeThreadSafe(int prop, const SEString &value) +{ + SEString Name; + if (prop == Contact::P_AVAILABILITY) + { + this->GetIdentity(Name); +// SEStringList DebugStrings = this->getPropDebug(prop, value); +// purple_debug_info("skypekit", "User %s is %s\n", (const char *)Name, (const char *)DebugStrings[2]); + const char *status; + + status = skype_contact_status_to_id(value.toUInt()); + if (status != NULL) + purple_prpl_got_user_status(this->pa, (const char *)Name, status, NULL); + } else if (prop == Contact::P_AVATAR_IMAGE) + { + bool hasAvatar = false; + Sid::Binary avatarData; + this->GetIdentity(Name); + this->GetAvatar(hasAvatar, avatarData); + if (hasAvatar) + { + purple_buddy_icons_set_for_user(this->pa, (const char *)Name, (char *)avatarData, avatarData.size(), NULL); + } else { + purple_buddy_icons_set_for_user(this->pa, (const char *)Name, NULL, 0, NULL); + } + } else if (prop == Contact::P_MOOD_TEXT) + { + //moodString = new SEString(value); + } else if (prop == Contact::P_RECEIVED_AUTHREQUEST) + { + SEString DisplayName; + this->GetPropDisplayname(DisplayName); + this->GetIdentity(Name); + + if (purple_account_get_bool(this->pa, "reject_all_auths", FALSE)) + { + skype_auth_deny(this->me()); + } else { + purple_account_request_authorization(this->pa, (const char *)Name, NULL, (const char *)DisplayName, + value, (purple_find_buddy(this->pa, (const char *)Name) != NULL), + skype_auth_allow, skype_auth_deny, this->me()); + } + } +}; + diff --git a/libskypekit/libskypekit.h b/libskypekit/libskypekit.h new file mode 100644 index 0000000..c968c6c --- /dev/null +++ b/libskypekit/libskypekit.h @@ -0,0 +1,361 @@ + +#include "prpl.h" +#include "blist.h" +#include "account.h" +#include "accountopt.h" +#include "connection.h" +#include "server.h" + +#include "mediamanager.h" +#include "request.h" + +#include <glib.h> + +#define SKYPE_APP_TOKEN "AAAgBobH4q2OPAaCX6vWseC82MmHZ1Cpayj61rbYlh0uenHxFByJ/lLu9HSN5nT3TjS91/2RQMASlCmZUCM5zINkR3nQ1240JpB0yNfYfzxXm8EyE9p9gWAGU7spUMvuROxoQR0042VUR4dCRW/kYr3yeYiYOXW0poxxwg+esEbX8W1tqing25kfjUVsij6+T+dxtV8t/B1yGpTiT1okj9FoBvZgnwDoEGEywG5xeJTGLuFtHGALqa7gwvj9rulf7TuM1Q==" + +#define GETTEXT_PACKAGE "skype4pidgin" +#ifdef ENABLE_NLS +# ifdef _WIN32 +# include <win32dep.h> +# endif +# include <glib/gi18n-lib.h> +#else +# define _(a) a +#endif + +#include "debug.h" + +#include "skype-embedded_2.h" +#include "skype-tcp-transport.h" + + +#define atoul(x) strtoul(x, (char **)NULL, 10) + + +class MyAccount : public Account +{ +public: + typedef DRef<MyAccount, Account> Ref; + MyAccount(unsigned int oid, SERootObject* root); + ~MyAccount() {}; + PurpleAccount *pa; + PurpleConnection *pc; + + void OnChange(int prop); + void OnChangeThreadSafe(int prop, const SEString& value); +}; + +MyAccount::MyAccount(unsigned int oid, SERootObject* root) : Account(oid, root) {}; + + +class MyContact : public Contact +{ +public: + typedef DRef<MyContact, Contact> Ref; + MyContact(unsigned int oid, SERootObject* root); + ~MyContact() {}; + PurpleAccount *pa; + PurpleConnection *pc; + MyContact *me() {return this;} + SEString moodString; + + void OnChange(int prop); + void OnChangeThreadSafe(int prop, const SEString& value); +}; + +MyContact::MyContact(unsigned int oid, SERootObject* root) : Contact(oid, root) {}; + +class MyContactGroup : public ContactGroup +{ +public: + typedef DRef<MyContactGroup, ContactGroup> Ref; + MyContactGroup(unsigned int oid, SERootObject* root); + ~MyContactGroup() {}; + PurpleAccount *pa; + PurpleConnection *pc; + + void OnChange(const ContactRef& contact); + void OnChangeThreadSafe(const ContactRef& contact); +}; + +MyContactGroup::MyContactGroup(unsigned int oid, SERootObject* root) : ContactGroup(oid, root) {}; + +class MyParticipant : public Participant +{ +public: + typedef DRef<MyParticipant, Participant> Ref; + MyParticipant(unsigned int oid, SERootObject* root); + ~MyParticipant() {}; + PurpleAccount *pa; + PurpleConnection *pc; + + void OnChange(int prop); + void OnChangeThreadSafe(int prop, const SEString& value); +}; + +MyParticipant::MyParticipant(unsigned int oid, SERootObject* root) : Participant(oid, root) {}; + +class MyTransfer : public Transfer +{ +public: + typedef DRef<MyTransfer, Transfer> Ref; + MyTransfer(unsigned int oid, SERootObject* root); + ~MyTransfer() {}; + PurpleAccount *pa; + PurpleConnection *pc; + PurpleXfer *xfer; + + MyTransfer *me() {return this;} + + void OnChange(int prop); + void OnChangeThreadSafe(int prop, const SEString& value); +}; + +MyTransfer::MyTransfer(unsigned int oid, SERootObject* root) : Transfer(oid, root) {}; + +class MyConversation : public Conversation +{ +public: + typedef DRef<MyConversation, Conversation> Ref; + MyConversation(unsigned int oid, SERootObject* root); + ~MyConversation() {}; + PurpleAccount *pa; + PurpleConnection *pc; + PurpleMedia *media; + + MyConversation *me() {return this;} + + void OnChange(int prop); + void OnChangeThreadSafe(int prop, const SEString& value); + void OnSpawnConference(const ConversationRef& spawned); + void OnSpawnConferenceThreadSafe(const ConversationRef& spawned); +}; + +MyConversation::MyConversation(unsigned int oid, SERootObject* root) : Conversation(oid, root) {}; + +class MyMessage : public Message +{ +public: + typedef DRef<MyMessage, Message> Ref; + MyMessage(unsigned int oid, SERootObject* root); + ~MyMessage() {}; + PurpleAccount *pa; + PurpleConnection *pc; + + void OnChange(int prop); + void OnChangeThreadSafe(int prop, const SEString& value); +}; + +MyMessage::MyMessage(unsigned int oid, SERootObject* root) : Message(oid, root) {}; + +class MySkype : public Skype +{ +public: + MySkype(SETransport* transport) : Skype(transport) {} + ~MySkype() {} + PurpleConnection *pc; + PurpleAccount *pa; + + // Every time an account object is created, we will return instance of MyAccount + Account* newAccount(int oid) {MyAccount *acc = new MyAccount(oid, this); acc->pc=this->pc; acc->pa=this->pa; return acc;} + Contact* newContact(int oid) {MyContact *con = new MyContact(oid, this); con->pc=this->pc; con->pa=this->pa; return con;} + ContactGroup* newContactGroup(int oid) {MyContactGroup *grp = new MyContactGroup(oid, this); grp->pc=this->pc; grp->pa=this->pa; return grp;} + Participant* newParticipant(int oid) {MyParticipant *par = new MyParticipant(oid, this); par->pc=this->pc; par->pa=this->pa; return par;} + Transfer* newTransfer(int oid) {MyTransfer *tra = new MyTransfer(oid, this); tra->xfer=NULL; tra->pc=this->pc; tra->pa=this->pa; return tra;} + Conversation* newConversation(int oid) {MyConversation *con = new MyConversation(oid, this); con->pc=this->pc; con->pa=this->pa; return con;} + Message* newMessage(int oid) {MyMessage *msg = new MyMessage(oid, this); msg->pc=this->pc; msg->pa=this->pa; return msg;} + + virtual void OnMessage( + const Message::Ref& message, + const bool& changesInboxTimestamp, + const Message::Ref& supersedesHistoryMessage, + const Conversation::Ref& conversation); + virtual void OnMessageThreadSafe( + const Message::Ref& message, + const bool& changesInboxTimestamp, + const Message::Ref& supersedesHistoryMessage, + const Conversation::Ref& conversation); + + virtual void OnConversationListChange( + const ConversationRef &conversation, + const Conversation::LIST_TYPE &type, + const bool &added); + virtual void OnConversationListChangeThreadSafe( + const ConversationRef &conversation, + const Conversation::LIST_TYPE &type, + const bool &added); +}; + +extern "C" { + + const char *skype_list_icon(PurpleAccount *account, PurpleBuddy *buddy); + 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); + void skype_set_status(PurpleAccount *account, PurpleStatus *status); + unsigned int skype_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state); + void skype_add_deny(PurpleConnection *gc, const char *who); + void skype_rem_deny(PurpleConnection *gc, const char *who); + char *skype_status_text(PurpleBuddy *buddy); + void skype_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); + void skype_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); + void skype_add_permit(PurpleConnection *gc, const char *who); + void skype_rem_permit(PurpleConnection *gc, const char *who); + const char *skype_list_emblem(PurpleBuddy *buddy); + void skype_alias_buddy(PurpleConnection *gc, const char *who, const char *alias); + void skype_send_file(PurpleConnection *, const char *who, const char *filename); + gboolean skype_offline_msg(const PurpleBuddy *buddy); + gboolean skype_can_receive_file(PurpleConnection *pc, const char *who); + PurpleXfer *skype_xfer_new(PurpleConnection *pc, const char *who); + void skype_alias_buddy(PurpleConnection *pc, const char *who, const char *alias); + void skype_set_buddy_icon(PurpleConnection *pc, PurpleStoredImage *img); + gboolean skype_initiate_media(PurpleAccount *account, const char *who, PurpleMediaSessionType type); + PurpleMediaCaps skype_get_media_caps(PurpleAccount *account, const char *who); + static GList *skype_node_menu(PurpleBlistNode *node); + void skype_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *userinfo, gboolean full); + int skype_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags); + const char *skype_normalize(const PurpleAccount *acct, const char *who); + void skype_set_chat_topic(PurpleConnection *gc, int id, const char *topic); + void skype_get_info(PurpleConnection *gc, const gchar *username); + + PurplePluginProtocolInfo prpl_info = { + /* options */ + (PurpleProtocolOptions) (OPT_PROTO_CHAT_TOPIC | OPT_PROTO_USE_POINTSIZE), + NULL, /* user_splits */ + NULL, /* protocol_options */ + {"jpeg", 0, 0, 256, 256, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */ + skype_list_icon, /* list_icon */ + skype_list_emblem, /* list_emblem */ + skype_status_text, /* status_text */ + skype_tooltip_text, /* tooltip_text */ + skype_status_types, /* status_types */ + skype_node_menu, /* blist_node_menu */ + NULL,/* chat_info */ + NULL,/* chat_info_defaults */ + skype_login, /* login */ + skype_close, /* close */ + skype_send_im, /* send_im */ + NULL, /* set_info */ + skype_send_typing, /* send_typing */ + skype_get_info, /* get_info */ + skype_set_status, /* set_status */ + NULL, /* 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 */ + NULL, /* join_chat */ + NULL, /* reject chat invite */ + NULL, /* get_chat_name */ + NULL, /* chat_invite */ + NULL, /* chat_leave */ + NULL, /* chat_whisper */ + skype_chat_send, /* chat_send */ + NULL, /* 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 */ + NULL, /* get_cb_real_name */ + skype_set_chat_topic,/* set_chat_topic */ + NULL, /* find_blist_chat */ + NULL, /* roomlist_get_list */ + NULL, /* roomlist_cancel */ + NULL, /* roomlist_expand_category */ + skype_can_receive_file, /* can_receive_file */ + skype_send_file, /* send_file */ + skype_xfer_new, /* 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 */ +#if PURPLE_MAJOR_VERSION == 2 && PURPLE_MINOR_VERSION == 1 + (gpointer) +#endif + sizeof(PurplePluginProtocolInfo), /* struct_size */ + NULL, /* get_account_text_table */ + skype_initiate_media,/* initiate_media */ + skype_get_media_caps /* can_do_media */ + }; + + gboolean + plugin_load(PurplePlugin *plugin) + { + return TRUE; + } + + gboolean + plugin_unload(PurplePlugin *plugin) + { + return TRUE; + } + + static PurplePluginInfo info = { + PURPLE_PLUGIN_MAGIC, + /* PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + */ + 2, 1, + PURPLE_PLUGIN_PROTOCOL, /* type */ + NULL, /* ui_requirement */ + 0, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + "prpl-bigbrownchunx-skypekit", /* id */ + "SkypeKit", /* name */ + "0.1", /* version */ + "Allows using Skype IM functions from within Pidgin", /* summary */ + "Allows using Skype IM functions from within Pidgin", /* description */ + "Eion Robb <eion@robbmob.com>", /* author */ + "http://eion.robbmob.com/", /* homepage */ + plugin_load, /* load */ + plugin_unload, /* unload */ + NULL, /* destroy */ + NULL, /* ui_info */ + &prpl_info, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, /* padding */ + NULL, + NULL, + NULL + }; + + static void + plugin_init(PurplePlugin *plugin) + { + PurpleAccountOption *option; + + #if _WIN32 && ENABLE_NLS + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + #endif + + //if (!g_thread_supported ()) + //g_thread_init (NULL); + + option = purple_account_option_string_new(_("Server"), "host", "127.0.0.1"); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_int_new(_("Port"), "port", 8963); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + } + + PURPLE_INIT_PLUGIN(skypekit, plugin_init, info); +}; |