pidgin/pidgin

Fix coverity 1255966 and 1255964

2016-12-10, Gary Kramlich
20f33f108f99
Fix coverity 1255966 and 1255964
/**
* @file msn.c The MSN protocol plugin
*
* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#define PHOTO_SUPPORT 1
#include "internal.h"
#include "debug.h"
#include "http.h"
#include "request.h"
#include "accountopt.h"
#include "contact.h"
#include "msg.h"
#include "page.h"
#include "plugins.h"
#include "prefs.h"
#include "session.h"
#include "smiley.h"
#include "smiley-custom.h"
#include "smiley-parser.h"
#include "state.h"
#include "util.h"
#include "cmds.h"
#include "core.h"
#include "protocol.h"
#include "msnutils.h"
#include "version.h"
#include "error.h"
#include "msg.h"
#include "switchboard.h"
#include "notification.h"
#include "slplink.h"
#if PHOTO_SUPPORT
#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024)
#include "image-store.h"
#endif
typedef struct
{
PurpleConnection *gc;
const char *passport;
} MsnMobileData;
typedef struct
{
PurpleConnection *gc;
char *name;
} MsnGetInfoData;
typedef struct
{
MsnGetInfoData *info_data;
char *stripped;
char *url_buffer;
PurpleNotifyUserInfo *user_info;
char *photo_url_text;
} MsnGetInfoStepTwoData;
typedef struct
{
PurpleConnection *gc;
const char *who;
char *msg;
PurpleMessageFlags flags;
time_t when;
} MsnIMData;
typedef struct
{
char *smile;
PurpleSmiley *ps;
MsnObject *obj;
} MsnEmoticon;
static PurpleProtocol *my_protocol = NULL;
static GSList *cmds = NULL;
static const char *
msn_normalize(const PurpleAccount *account, const char *str)
{
static char buf[BUF_LEN];
char *tmp;
g_return_val_if_fail(str != NULL, NULL);
tmp = g_strchomp(g_utf8_strdown(str, -1));
g_snprintf(buf, sizeof(buf), "%s%s", tmp,
(strchr(tmp, '@') ? "" : "@hotmail.com"));
g_free(tmp);
return buf;
}
static gboolean
msn_send_attention(PurpleConnection *gc, const char *username, guint type)
{
MsnMessage *msg;
MsnSession *session;
MsnSwitchBoard *swboard;
msg = msn_message_new_nudge();
session = purple_connection_get_protocol_data(gc);
swboard = msn_session_get_swboard(session, username, MSN_SB_FLAG_IM);
msn_switchboard_send_msg(swboard, msg, TRUE);
msn_message_unref(msg);
return TRUE;
}
static GList *
msn_attention_types(PurpleAccount *account)
{
static GList *list = NULL;
if (!list) {
list = g_list_append(list, purple_attention_type_new("Nudge", _("Nudge"),
_("%s has nudged you!"), _("Nudging %s...")));
}
return list;
}
static GHashTable *
msn_get_account_text_table(PurpleAccount *unused)
{
GHashTable *table;
table = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(table, "login_label", (gpointer)_("Email Address..."));
return table;
}
static PurpleCmdRet
msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data)
{
PurpleAccount *account = purple_conversation_get_account(conv);
PurpleConnection *gc = purple_account_get_connection(account);
const gchar *username;
username = purple_conversation_get_name(conv);
purple_protocol_send_attention(gc, username, MSN_NUDGE);
return PURPLE_CMD_RET_OK;
}
struct public_alias_closure
{
PurpleAccount *account;
gpointer success_cb;
gpointer failure_cb;
};
static gboolean
set_public_alias_length_error(gpointer data)
{
struct public_alias_closure *closure = data;
PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
failure_cb(closure->account, _("Your new MSN friendly name is too long."));
g_object_unref(closure->account);
g_free(closure);
return FALSE;
}
static void
prp_success_cb(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
const char *type, *friendlyname;
struct public_alias_closure *closure;
g_return_if_fail(cmd->param_count >= 3);
type = cmd->params[1];
g_return_if_fail(!strcmp(type, "MFN"));
closure = cmd->trans->data;
friendlyname = purple_url_decode(cmd->params[2]);
msn_update_contact(cmdproc->session, "Me", MSN_UPDATE_DISPLAY, friendlyname);
purple_connection_set_display_name(
purple_account_get_connection(closure->account),
friendlyname);
purple_account_set_string(closure->account, "display-name", friendlyname);
if (closure->success_cb) {
PurpleSetPublicAliasSuccessCallback success_cb = closure->success_cb;
success_cb(closure->account, friendlyname);
}
}
static void
prp_error_cb(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
{
struct public_alias_closure *closure = trans->data;
PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
gboolean debug;
const char *error_text;
error_text = msn_error_get_text(error, &debug);
failure_cb(closure->account, error_text);
}
static void
prp_timeout_cb(MsnCmdProc *cmdproc, MsnTransaction *trans)
{
struct public_alias_closure *closure = trans->data;
PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
failure_cb(closure->account, _("Connection Timeout"));
}
void
msn_set_public_alias(PurpleConnection *pc, const char *alias,
PurpleSetPublicAliasSuccessCallback success_cb,
PurpleSetPublicAliasFailureCallback failure_cb)
{
MsnCmdProc *cmdproc;
MsnSession *session;
MsnTransaction *trans;
PurpleAccount *account;
char real_alias[BUDDY_ALIAS_MAXLEN + 1];
struct public_alias_closure *closure;
session = purple_connection_get_protocol_data(pc);
cmdproc = session->notification->cmdproc;
account = purple_connection_get_account(pc);
if (alias && *alias) {
if (!msn_encode_spaces(alias, real_alias, BUDDY_ALIAS_MAXLEN + 1)) {
if (failure_cb) {
struct public_alias_closure *closure =
g_new0(struct public_alias_closure, 1);
closure->account = g_object_ref(account);
closure->failure_cb = failure_cb;
purple_timeout_add(0, set_public_alias_length_error, closure);
} else {
purple_notify_error(pc, NULL, _("Your new MSN "
"friendly name is too long."), NULL,
purple_request_cpar_from_connection(pc));
}
return;
}
if (real_alias[0] == '\0')
g_strlcpy(real_alias, purple_account_get_username(account), sizeof(real_alias));
} else
g_strlcpy(real_alias, purple_account_get_username(account), sizeof(real_alias));
closure = g_new0(struct public_alias_closure, 1);
closure->account = account;
closure->success_cb = success_cb;
closure->failure_cb = failure_cb;
trans = msn_transaction_new(cmdproc, "PRP", "MFN %s", real_alias);
msn_transaction_set_data(trans, closure);
msn_transaction_set_data_free(trans, g_free);
msn_transaction_add_cb(trans, "PRP", prp_success_cb);
if (failure_cb) {
msn_transaction_set_error_cb(trans, prp_error_cb);
msn_transaction_set_timeout_cb(trans, prp_timeout_cb);
}
msn_cmdproc_send_trans(cmdproc, trans);
}
static gboolean
get_public_alias_cb(gpointer data)
{
struct public_alias_closure *closure = data;
PurpleGetPublicAliasSuccessCallback success_cb = closure->success_cb;
const char *alias;
alias = purple_account_get_string(closure->account, "display-name",
purple_account_get_username(closure->account));
success_cb(closure->account, alias);
g_object_unref(closure->account);
g_free(closure);
return FALSE;
}
static void
msn_get_public_alias(PurpleConnection *pc,
PurpleGetPublicAliasSuccessCallback success_cb,
PurpleGetPublicAliasFailureCallback failure_cb)
{
struct public_alias_closure *closure = g_new0(struct public_alias_closure, 1);
PurpleAccount *account = purple_connection_get_account(pc);
closure->account = g_object_ref(account);
closure->success_cb = success_cb;
purple_timeout_add(0, get_public_alias_cb, closure);
}
static void
msn_act_id(PurpleConnection *gc, const char *entry)
{
msn_set_public_alias(gc, entry, NULL, NULL);
}
static void
msn_set_prp(PurpleConnection *gc, const char *type, const char *entry)
{
MsnCmdProc *cmdproc;
MsnSession *session;
MsnTransaction *trans;
session = purple_connection_get_protocol_data(gc);
cmdproc = session->notification->cmdproc;
if (entry == NULL || *entry == '\0')
{
trans = msn_transaction_new(cmdproc, "PRP", "%s", type);
}
else
{
trans = msn_transaction_new(cmdproc, "PRP", "%s %s", type,
purple_url_encode(entry));
}
msn_cmdproc_send_trans(cmdproc, trans);
}
static void
msn_set_home_phone_cb(PurpleConnection *gc, const char *entry)
{
msn_set_prp(gc, "PHH", entry);
}
static void
msn_set_work_phone_cb(PurpleConnection *gc, const char *entry)
{
msn_set_prp(gc, "PHW", entry);
}
static void
msn_set_mobile_phone_cb(PurpleConnection *gc, const char *entry)
{
msn_set_prp(gc, "PHM", entry);
}
static void
enable_msn_pages_cb(PurpleConnection *gc)
{
msn_set_prp(gc, "MOB", "Y");
}
static void
disable_msn_pages_cb(PurpleConnection *gc)
{
msn_set_prp(gc, "MOB", "N");
}
static void
send_to_mobile(PurpleConnection *gc, const char *who, const char *entry)
{
MsnTransaction *trans;
MsnSession *session;
MsnCmdProc *cmdproc;
MsnPage *page;
MsnMessage *msg;
MsnUser *user;
char *payload = NULL;
const char *mobile_number = NULL;
gsize payload_len;
session = purple_connection_get_protocol_data(gc);
cmdproc = session->notification->cmdproc;
page = msn_page_new();
msn_page_set_body(page, entry);
payload = msn_page_gen_payload(page, &payload_len);
if ((user = msn_userlist_find_user(session->userlist, who)) &&
(mobile_number = msn_user_get_mobile_phone(user)) &&
mobile_number[0] == '+') {
/* if msn_user_get_mobile_phone() has a + in front, it's a number
that from the buddy's contact card */
trans = msn_transaction_new(cmdproc, "PGD", "tel:%s 1 %" G_GSIZE_FORMAT,
mobile_number, payload_len);
} else {
/* otherwise we send to whatever phone number the buddy registered
with msn */
trans = msn_transaction_new(cmdproc, "PGD", "%s 1 %" G_GSIZE_FORMAT,
who, payload_len);
}
msn_transaction_set_payload(trans, payload, payload_len);
g_free(payload);
msg = msn_message_new_plain(entry);
msn_transaction_set_data(trans, msg);
msn_page_destroy(page);
msn_cmdproc_send_trans(cmdproc, trans);
}
static void
send_to_mobile_cb(MsnMobileData *data, const char *entry)
{
send_to_mobile(data->gc, data->passport, entry);
g_free(data);
}
static void
close_mobile_page_cb(MsnMobileData *data, const char *entry)
{
g_free(data);
}
/* -- */
static void
msn_show_set_friendly_name(PurpleProtocolAction *action)
{
PurpleConnection *gc;
PurpleAccount *account;
char *tmp;
gc = action->connection;
account = purple_connection_get_account(gc);
tmp = g_strdup_printf(_("Set friendly name for %s."),
purple_account_get_username(account));
purple_request_input(gc, _("Set Friendly Name"), tmp,
_("This is the name that other MSN buddies will "
"see you as."),
purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_act_id),
_("Cancel"), NULL,
purple_request_cpar_from_connection(gc),
gc);
g_free(tmp);
}
typedef struct MsnLocationData {
PurpleAccount *account;
MsnSession *session;
PurpleRequestFieldGroup *group;
} MsnLocationData;
static void
update_endpoint_cb(MsnLocationData *data, PurpleRequestFields *fields)
{
PurpleAccount *account;
MsnSession *session;
const char *old_name;
const char *name;
GList *others;
session = data->session;
account = data->account;
/* Update the current location's name */
old_name = purple_account_get_string(account, "endpoint-name", NULL);
name = purple_request_fields_get_string(fields, "endpoint-name");
if (!g_str_equal(old_name, name)) {
purple_account_set_string(account, "endpoint-name", name);
msn_notification_send_uux_private_endpointdata(session);
}
/* Sign out other locations */
for (others = purple_request_field_group_get_fields(data->group);
others;
others = g_list_next(others)) {
PurpleRequestField *field = others->data;
if (purple_request_field_get_field_type(field) != PURPLE_REQUEST_FIELD_BOOLEAN)
continue;
if (purple_request_field_bool_get_value(field)) {
const char *id = purple_request_field_get_id(field);
char *user;
purple_debug_info("msn", "Disconnecting Endpoint %s\n", id);
user = g_strdup_printf("%s;%s", purple_account_get_username(account), id);
msn_notification_send_uun(session, user, MSN_UNIFIED_NOTIFICATION_MPOP, "goawyplzthxbye");
g_free(user);
}
}
g_free(data);
}
static void
msn_show_locations(PurpleProtocolAction *action)
{
PurpleConnection *pc;
PurpleAccount *account;
MsnSession *session;
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
gboolean have_other_endpoints;
GSList *l;
MsnLocationData *data;
pc = action->connection;
account = purple_connection_get_account(pc);
session = purple_connection_get_protocol_data(pc);
fields = purple_request_fields_new();
group = purple_request_field_group_new(_("This Location"));
purple_request_fields_add_group(fields, group);
field = purple_request_field_label_new("endpoint-label", _("This is the name that identifies this location"));
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("endpoint-name",
_("Name"),
purple_account_get_string(account, "endpoint-name", NULL),
FALSE);
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
group = purple_request_field_group_new(_("Other Locations"));
purple_request_fields_add_group(fields, group);
have_other_endpoints = FALSE;
for (l = session->user->endpoints; l; l = l->next) {
MsnUserEndpoint *ep = l->data;
if (ep->id[0] != '\0' && strncasecmp(ep->id + 1, session->guid, 36) == 0)
/* Don't add myself to the list */
continue;
if (!have_other_endpoints) {
/* We do in fact have an endpoint other than ourselves... let's
add a label */
field = purple_request_field_label_new("others-label",
_("You can sign out from other locations here"));
purple_request_field_group_add_field(group, field);
}
have_other_endpoints = TRUE;
field = purple_request_field_bool_new(ep->id, ep->name, FALSE);
purple_request_field_group_add_field(group, field);
}
if (!have_other_endpoints) {
/* TODO: Due to limitations in our current request field API, the
following string will show up with a trailing colon. This should
be fixed either by adding an "include_colon" boolean, or creating
a separate purple_request_field_label_new_without_colon function,
or by never automatically adding the colon and requiring that
callers add the colon themselves. */
field = purple_request_field_label_new("others-label", _("You are not signed in from any other locations."));
purple_request_field_group_add_field(group, field);
}
data = g_new0(MsnLocationData, 1);
data->account = account;
data->session = session;
data->group = group;
purple_request_fields(pc, NULL, NULL, NULL, fields, _("OK"),
G_CALLBACK(update_endpoint_cb), _("Cancel"), G_CALLBACK(g_free),
purple_request_cpar_from_connection(pc), data);
}
static void
enable_mpop_cb(PurpleConnection *pc)
{
MsnSession *session = purple_connection_get_protocol_data(pc);
purple_debug_info("msn", "Enabling MPOP\n");
session->enable_mpop = TRUE;
msn_annotate_contact(session, "Me", "MSN.IM.MPOP", "1", NULL);
purple_protocol_got_account_actions(purple_connection_get_account(pc));
}
static void
disable_mpop_cb(PurpleConnection *pc)
{
PurpleAccount *account = purple_connection_get_account(pc);
MsnSession *session = purple_connection_get_protocol_data(pc);
GSList *l;
purple_debug_info("msn", "Disabling MPOP\n");
session->enable_mpop = FALSE;
msn_annotate_contact(session, "Me", "MSN.IM.MPOP", "0", NULL);
for (l = session->user->endpoints; l; l = l->next) {
MsnUserEndpoint *ep = l->data;
char *user;
if (ep->id[0] != '\0' && strncasecmp(ep->id + 1, session->guid, 36) == 0)
/* Don't kick myself */
continue;
purple_debug_info("msn", "Disconnecting Endpoint %s\n", ep->id);
user = g_strdup_printf("%s;%s", purple_account_get_username(account), ep->id);
msn_notification_send_uun(session, user, MSN_UNIFIED_NOTIFICATION_MPOP, "goawyplzthxbye");
g_free(user);
}
purple_protocol_got_account_actions(account);
}
static void
msn_show_set_mpop(PurpleProtocolAction *action)
{
PurpleConnection *pc;
pc = action->connection;
purple_request_action(pc, NULL, _("Allow multiple logins?"),
_("Do you want to allow or disallow connecting from "
"multiple locations simultaneously?"),
PURPLE_DEFAULT_ACTION_NONE,
purple_request_cpar_from_connection(pc),
pc, 3,
_("Allow"), G_CALLBACK(enable_mpop_cb),
_("Disallow"), G_CALLBACK(disable_mpop_cb),
_("Cancel"), NULL);
}
static void
msn_show_set_home_phone(PurpleProtocolAction *action)
{
PurpleConnection *gc;
MsnSession *session;
gc = action->connection;
session = purple_connection_get_protocol_data(gc);
purple_request_input(gc, NULL, _("Set your home phone number."), NULL,
msn_user_get_home_phone(session->user), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_set_home_phone_cb),
_("Cancel"), NULL,
purple_request_cpar_from_connection(gc),
gc);
}
static void
msn_show_set_work_phone(PurpleProtocolAction *action)
{
PurpleConnection *gc;
MsnSession *session;
gc = action->connection;
session = purple_connection_get_protocol_data(gc);
purple_request_input(gc, NULL, _("Set your work phone number."), NULL,
msn_user_get_work_phone(session->user), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_set_work_phone_cb),
_("Cancel"), NULL,
purple_request_cpar_from_connection(gc),
gc);
}
static void
msn_show_set_mobile_phone(PurpleProtocolAction *action)
{
PurpleConnection *gc;
MsnSession *session;
gc = action->connection;
session = purple_connection_get_protocol_data(gc);
purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL,
msn_user_get_mobile_phone(session->user), FALSE, FALSE, NULL,
_("OK"), G_CALLBACK(msn_set_mobile_phone_cb),
_("Cancel"), NULL,
purple_request_cpar_from_connection(gc),
gc);
}
static void
msn_show_set_mobile_pages(PurpleProtocolAction *action)
{
PurpleConnection *gc;
gc = action->connection;
purple_request_action(gc, NULL, _("Allow MSN Mobile pages?"),
_("Do you want to allow or disallow people on "
"your buddy list to send you MSN Mobile pages "
"to your cell phone or other mobile device?"),
PURPLE_DEFAULT_ACTION_NONE,
purple_request_cpar_from_connection(gc),
gc, 3,
_("Allow"), G_CALLBACK(enable_msn_pages_cb),
_("Disallow"), G_CALLBACK(disable_msn_pages_cb),
_("Cancel"), NULL);
}
/* QuLogic: Disabled until confirmed correct. */
#if 0
static void
msn_show_blocked_text(PurpleProtocolAction *action)
{
PurpleConnection *pc = action->connection;
MsnSession *session;
char *title;
session = purple_connection_get_protocol_data(pc);
title = g_strdup_printf(_("Blocked Text for %s"), session->account->username);
if (session->blocked_text == NULL) {
purple_notify_formatted(pc, title, title, NULL, _("No text is blocked for this account."), NULL, NULL);
} else {
char *blocked_text;
blocked_text = g_strdup_printf(_("MSN servers are currently blocking the following regular expressions:<br/>%s"),
session->blocked_text);
purple_notify_formatted(pc, title, title, NULL, blocked_text, NULL, NULL);
g_free(blocked_text);
}
g_free(title);
}
#endif
static void
msn_show_hotmail_inbox(PurpleProtocolAction *action)
{
PurpleConnection *gc;
MsnSession *session;
gc = action->connection;
session = purple_connection_get_protocol_data(gc);
if (!session->passport_info.email_enabled) {
purple_notify_error(gc, NULL, _("This account does not have "
"email enabled."), NULL,
purple_request_cpar_from_connection(gc));
return;
}
/** apparently the correct value is 777, use 750 as a failsafe */
if ((session->passport_info.mail_url == NULL)
|| (time (NULL) - session->passport_info.mail_timestamp >= 750)) {
MsnTransaction *trans;
MsnCmdProc *cmdproc;
cmdproc = session->notification->cmdproc;
trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
msn_transaction_set_data(trans, GUINT_TO_POINTER(TRUE));
msn_cmdproc_send_trans(cmdproc, trans);
} else
purple_notify_uri(gc, session->passport_info.mail_url);
}
static void
show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored)
{
PurpleBuddy *buddy;
PurpleConnection *gc;
MsnMobileData *data;
PurpleAccount *account;
const char *name;
g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
account = purple_buddy_get_account(buddy);
gc = purple_account_get_connection(account);
name = purple_buddy_get_name(buddy);
data = g_new0(MsnMobileData, 1);
data->gc = gc;
data->passport = name;
purple_request_input(gc, NULL, _("Send a mobile message."), NULL,
NULL, TRUE, FALSE, NULL,
_("Page"), G_CALLBACK(send_to_mobile_cb),
_("Close"), G_CALLBACK(close_mobile_page_cb),
purple_request_cpar_from_connection(gc),
data);
}
static gboolean
msn_offline_message(const PurpleBuddy *buddy) {
return TRUE;
}
void
msn_send_privacy(PurpleConnection *gc)
{
PurpleAccount *account;
MsnSession *session;
MsnCmdProc *cmdproc;
MsnTransaction *trans;
account = purple_connection_get_account(gc);
session = purple_connection_get_protocol_data(gc);
cmdproc = session->notification->cmdproc;
if (purple_account_get_privacy_type(account) == PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL ||
purple_account_get_privacy_type(account) == PURPLE_ACCOUNT_PRIVACY_DENY_USERS)
trans = msn_transaction_new(cmdproc, "BLP", "%s", "AL");
else
trans = msn_transaction_new(cmdproc, "BLP", "%s", "BL");
msn_cmdproc_send_trans(cmdproc, trans);
}
static void
initiate_chat_cb(PurpleBlistNode *node, gpointer data)
{
PurpleBuddy *buddy;
PurpleConnection *gc;
PurpleAccount *account;
MsnSession *session;
MsnSwitchBoard *swboard;
const char *alias;
g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
account = purple_buddy_get_account(buddy);
gc = purple_account_get_connection(account);
session = purple_connection_get_protocol_data(gc);
swboard = msn_switchboard_new(session);
msn_switchboard_request(swboard);
msn_switchboard_request_add_user(swboard, purple_buddy_get_name(buddy));
/* TODO: This might move somewhere else, after USR might be */
swboard->chat_id = msn_switchboard_get_chat_id();
swboard->conv = PURPLE_CONVERSATION(purple_serv_got_joined_chat(gc,
swboard->chat_id, "MSN Chat"));
swboard->flag = MSN_SB_FLAG_IM;
/* Local alias > Display name > Username */
if ((alias = purple_account_get_private_alias(account)) == NULL)
if ((alias = purple_connection_get_display_name(gc)) == NULL)
alias = purple_account_get_username(account);
purple_chat_conversation_add_user(PURPLE_CHAT_CONVERSATION(swboard->conv),
alias, NULL, PURPLE_CHAT_USER_NONE, TRUE);
}
static void
t_msn_xfer_init(PurpleXfer *xfer)
{
msn_request_ft(xfer);
}
static void
t_msn_xfer_cancel_send(PurpleXfer *xfer)
{
MsnSlpLink *slplink = purple_xfer_get_protocol_data(xfer);
msn_slplink_unref(slplink);
}
static PurpleXfer*
msn_new_xfer(PurpleConnection *gc, const char *who)
{
MsnSession *session;
PurpleXfer *xfer;
session = purple_connection_get_protocol_data(gc);
xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_TYPE_SEND, who);
g_return_val_if_fail(xfer != NULL, NULL);
purple_xfer_set_protocol_data(xfer, msn_slplink_ref(msn_session_get_slplink(session, who)));
purple_xfer_set_init_fnc(xfer, t_msn_xfer_init);
purple_xfer_set_cancel_send_fnc(xfer, t_msn_xfer_cancel_send);
return xfer;
}
static void
msn_send_file(PurpleConnection *gc, const char *who, const char *file)
{
PurpleXfer *xfer = msn_new_xfer(gc, who);
if (file)
purple_xfer_request_accepted(xfer, file);
else
purple_xfer_request(xfer);
}
static gboolean
msn_can_receive_file(PurpleConnection *gc, const char *who)
{
PurpleAccount *account;
gchar *normal;
gboolean ret;
account = purple_connection_get_account(gc);
normal = g_strdup(msn_normalize(account, purple_account_get_username(account)));
ret = strcmp(normal, msn_normalize(account, who));
g_free(normal);
if (ret) {
MsnSession *session = purple_connection_get_protocol_data(gc);
if (session) {
MsnUser *user = msn_userlist_find_user(session->userlist, who);
if (user) {
/* Include these too: MSN_CAP_MOBILE_ON|MSN_CAP_WEB_WATCH ? */
if ((user->clientid & MSN_CAP_VIA_WEBIM) ||
user->networkid == MSN_NETWORK_YAHOO)
ret = FALSE;
else
ret = TRUE;
}
} else
ret = FALSE;
}
return ret;
}
/**************************************************************************
* Protocol Plugin ops
**************************************************************************/
static const char *
msn_list_icon(PurpleAccount *a, PurpleBuddy *b)
{
return "msn";
}
static const char *
msn_list_emblems(PurpleBuddy *b)
{
MsnUser *user = purple_buddy_get_protocol_data(b);
if (user != NULL) {
if (user->clientid & MSN_CAP_BOT)
return "bot";
if (user->clientid & MSN_CAP_VIA_MOBILE)
return "mobile";
#if 0
/* XXX: Since we don't support this, there's no point in showing it just yet */
if (user->clientid & MSN_CAP_SCHANNEL)
return "secure";
#endif
if (user->clientid & MSN_CAP_VIA_WEBIM)
return "external";
if (user->networkid == MSN_NETWORK_YAHOO)
return "yahoo";
}
return NULL;
}
/*
* Set the User status text
*/
static char *
msn_status_text(PurpleBuddy *buddy)
{
PurplePresence *presence;
PurpleStatus *status;
const char *msg;
presence = purple_buddy_get_presence(buddy);
status = purple_presence_get_active_status(presence);
/* Official client says media takes precedence over message */
/* I say message take precedence over media! Plus jabber agrees
too */
msg = purple_status_get_attr_string(status, "message");
if (msg && *msg)
return g_markup_escape_text(msg, -1);
if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
const char *title, *game, *office;
char *media, *esc;
status = purple_presence_get_status(presence, "tune");
title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
game = purple_status_get_attr_string(status, "game");
office = purple_status_get_attr_string(status, "office");
if (title && *title) {
const char *artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
const char *album = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
media = purple_util_format_song_info(title, artist, album, NULL);
return media;
}
else if (game && *game)
media = g_strdup_printf("Playing %s", game);
else if (office && *office)
media = g_strdup_printf("Editing %s", office);
else
return NULL;
esc = g_markup_escape_text(media, -1);
g_free(media);
return esc;
}
return NULL;
}
static void
msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
{
MsnUser *user;
PurplePresence *presence = purple_buddy_get_presence(buddy);
PurpleStatus *status = purple_presence_get_active_status(presence);
user = purple_buddy_get_protocol_data(buddy);
if (purple_presence_is_online(presence))
{
const char *psm, *name;
const char *mediatype = NULL;
char *currentmedia = NULL;
psm = purple_status_get_attr_string(status, "message");
if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
PurpleStatus *tune = purple_presence_get_status(presence, "tune");
const char *title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
const char *game = purple_status_get_attr_string(tune, "game");
const char *office = purple_status_get_attr_string(tune, "office");
if (title && *title) {
const char *artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
const char *album = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
mediatype = _("Now Listening");
currentmedia = purple_util_format_song_info(title, artist, album, NULL);
} else if (game && *game) {
mediatype = _("Playing a game");
currentmedia = g_strdup(game);
} else if (office && *office) {
mediatype = _("Working");
currentmedia = g_strdup(office);
}
}
if (!purple_status_is_available(status)) {
name = purple_status_get_name(status);
} else {
name = NULL;
}
if (name != NULL && *name) {
char *tmp2;
tmp2 = g_markup_escape_text(name, -1);
if (purple_presence_is_idle(presence)) {
char *idle;
char *tmp3;
/* Never know what those translations might end up like... */
idle = g_markup_escape_text(_("Idle"), -1);
tmp3 = g_strdup_printf("%s/%s", tmp2, idle);
g_free(idle);
g_free(tmp2);
tmp2 = tmp3;
}
if (psm != NULL && *psm) {
purple_notify_user_info_add_pair_plaintext(user_info, tmp2, psm);
} else {
purple_notify_user_info_add_pair_html(user_info, _("Status"), tmp2);
}
g_free(tmp2);
} else {
if (psm != NULL && *psm) {
if (purple_presence_is_idle(presence)) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), psm);
} else {
purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), psm);
}
} else {
if (purple_presence_is_idle(presence)) {
purple_notify_user_info_add_pair_plaintext(user_info,
_("Status"), _("Idle"));
} else {
purple_notify_user_info_add_pair_plaintext(user_info,
_("Status"), purple_status_get_name(status));
}
}
}
if (currentmedia) {
purple_notify_user_info_add_pair_html(user_info, mediatype, currentmedia);
g_free(currentmedia);
}
}
/* XXX: This is being shown in non-full tooltips because the
* XXX: blocked icon overlay isn't always accurate for MSN.
* XXX: This can die as soon as purple_privacy_check() knows that
* XXX: this protocol always honors both the allow and deny lists. */
/* While the above comment may be strictly correct (the privacy API needs
* rewriteing), purple_privacy_check() is going to be more accurate at
* indicating whether a particular buddy is going to be able to message
* you, which is the important information that this is trying to convey.
*/
if (full && user)
{
const char *phone;
purple_notify_user_info_add_pair_plaintext(user_info, _("Has you"),
((user->list_op & (1 << MSN_LIST_RL)) ? _("Yes") : _("No")));
purple_notify_user_info_add_pair_plaintext(user_info, _("Blocked"),
((user->list_op & (1 << MSN_LIST_BL)) ? _("Yes") : _("No")));
phone = msn_user_get_home_phone(user);
if (phone != NULL) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Home Phone Number"), phone);
}
phone = msn_user_get_work_phone(user);
if (phone != NULL) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Work Phone Number"), phone);
}
phone = msn_user_get_mobile_phone(user);
if (phone != NULL) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Mobile Phone Number"), phone);
}
}
}
static GList *
msn_status_types(PurpleAccount *account)
{
PurpleStatusType *status;
GList *types = NULL;
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(
PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
NULL, NULL, TRUE, TRUE, FALSE);
types = g_list_append(types, status);
status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
NULL, NULL, TRUE, TRUE, FALSE);
types = g_list_append(types, status);
status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
"mobile", NULL, FALSE, FALSE, TRUE);
types = g_list_append(types, status);
status = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
"tune", NULL, FALSE, TRUE, TRUE,
PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(G_TYPE_STRING),
PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(G_TYPE_STRING),
PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(G_TYPE_STRING),
"game", _("Game Title"), purple_value_new(G_TYPE_STRING),
"office", _("Office Title"), purple_value_new(G_TYPE_STRING),
NULL);
types = g_list_append(types, status);
return types;
}
static GList *
msn_get_actions(PurpleConnection *gc)
{
MsnSession *session;
GList *m = NULL;
PurpleProtocolAction *act;
session = purple_connection_get_protocol_data(gc);
act = purple_protocol_action_new(_("Set Friendly Name..."),
msn_show_set_friendly_name);
m = g_list_append(m, act);
m = g_list_append(m, NULL);
if (session->enable_mpop)
{
act = purple_protocol_action_new(_("View Locations..."),
msn_show_locations);
m = g_list_append(m, act);
m = g_list_append(m, NULL);
}
act = purple_protocol_action_new(_("Set Home Phone Number..."),
msn_show_set_home_phone);
m = g_list_append(m, act);
act = purple_protocol_action_new(_("Set Work Phone Number..."),
msn_show_set_work_phone);
m = g_list_append(m, act);
act = purple_protocol_action_new(_("Set Mobile Phone Number..."),
msn_show_set_mobile_phone);
m = g_list_append(m, act);
m = g_list_append(m, NULL);
#if 0
act = purple_protocol_action_new(_("Enable/Disable Mobile Devices..."),
msn_show_set_mobile_support);
m = g_list_append(m, act);
#endif
act = purple_protocol_action_new(_("Allow/Disallow Multiple Logins..."),
msn_show_set_mpop);
m = g_list_append(m, act);
act = purple_protocol_action_new(_("Allow/Disallow Mobile Pages..."),
msn_show_set_mobile_pages);
m = g_list_append(m, act);
/* QuLogic: Disabled until confirmed correct. */
#if 0
m = g_list_append(m, NULL);
act = purple_protocol_action_new(_("View Blocked Text..."),
msn_show_blocked_text);
m = g_list_append(m, act);
#endif
m = g_list_append(m, NULL);
act = purple_protocol_action_new(_("Open Hotmail Inbox"),
msn_show_hotmail_inbox);
m = g_list_append(m, act);
return m;
}
static GList *
msn_buddy_menu(PurpleBuddy *buddy)
{
MsnUser *user;
GList *m = NULL;
PurpleMenuAction *act;
g_return_val_if_fail(buddy != NULL, NULL);
user = purple_buddy_get_protocol_data(buddy);
if (user != NULL)
{
if (user->mobile)
{
act = purple_menu_action_new(_("Send to Mobile"),
PURPLE_CALLBACK(show_send_to_mobile_cb),
NULL, NULL);
m = g_list_append(m, act);
}
}
if (g_ascii_strcasecmp(purple_buddy_get_name(buddy),
purple_account_get_username(purple_buddy_get_account(buddy))))
{
act = purple_menu_action_new(_("Initiate _Chat"),
PURPLE_CALLBACK(initiate_chat_cb),
NULL, NULL);
m = g_list_append(m, act);
}
return m;
}
static GList *
msn_blist_node_menu(PurpleBlistNode *node)
{
if(PURPLE_IS_BUDDY(node))
{
return msn_buddy_menu((PurpleBuddy *) node);
}
else
{
return NULL;
}
}
static void
msn_login(PurpleAccount *account)
{
PurpleConnection *gc;
MsnSession *session;
const char *username;
const char *host;
gboolean http_method = FALSE;
int port;
gc = purple_account_get_connection(account);
http_method = purple_account_get_bool(account, "http_method", FALSE);
if (http_method)
host = purple_account_get_string(account, "http_method_server", MSN_HTTPCONN_SERVER);
else
host = purple_account_get_string(account, "server", MSN_SERVER);
port = purple_account_get_int(account, "port", MSN_PORT);
session = msn_session_new(account);
purple_connection_set_protocol_data(gc, session);
purple_connection_set_flags(gc,
PURPLE_CONNECTION_FLAG_HTML |
PURPLE_CONNECTION_FLAG_FORMATTING_WBFO |
PURPLE_CONNECTION_FLAG_NO_BGCOLOR |
PURPLE_CONNECTION_FLAG_NO_FONTSIZE |
PURPLE_CONNECTION_FLAG_NO_URLDESC |
PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY |
PURPLE_CONNECTION_FLAG_NO_IMAGES);
msn_session_set_login_step(session, MSN_LOGIN_STEP_START);
/* Hmm, I don't like this. */
/* XXX shx: Me neither */
username = msn_normalize(account, purple_account_get_username(account));
if (strcmp(username, purple_account_get_username(account)))
purple_account_set_username(account, username);
username = purple_account_get_string(account, "display-name", NULL);
purple_connection_set_display_name(gc, username);
if (purple_account_get_string(account, "endpoint-name", NULL) == NULL) {
GHashTable *ui_info = purple_core_get_ui_info();
const gchar *ui_name = ui_info ? g_hash_table_lookup(ui_info, "name") : NULL;
purple_account_set_string(account, "endpoint-name",
ui_name && *ui_name ? ui_name : PACKAGE_NAME);
}
if (!msn_session_connect(session, host, port, http_method))
purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
static void
msn_close(PurpleConnection *gc)
{
MsnSession *session;
session = purple_connection_get_protocol_data(gc);
g_return_if_fail(session != NULL);
msn_session_destroy(session);
purple_connection_set_protocol_data(gc, NULL);
}
static gboolean
msn_send_me_im(gpointer data)
{
MsnIMData *imdata = data;
purple_serv_got_im(imdata->gc, imdata->who, imdata->msg, imdata->flags, imdata->when);
g_object_unref(imdata->gc);
g_free(imdata->msg);
g_free(imdata);
return FALSE;
}
static GString*
msn_msg_emoticon_add(GString *current, MsnEmoticon *emoticon)
{
MsnObject *obj;
char *strobj;
if (emoticon == NULL)
return current;
obj = emoticon->obj;
if (!obj)
return current;
strobj = msn_object_to_string(obj);
if (current)
g_string_append_printf(current, "\t%s\t%s", emoticon->smile, strobj);
else {
current = g_string_new("");
g_string_printf(current, "%s\t%s", emoticon->smile, strobj);
}
g_free(strobj);
return current;
}
static void
msn_send_emoticons(MsnSwitchBoard *swboard, GString *body)
{
MsnMessage *msg;
g_return_if_fail(body != NULL);
msg = msn_message_new(MSN_MSG_SLP);
msn_message_set_content_type(msg, "text/x-mms-emoticon");
msn_message_set_flag(msg, 'N');
msn_message_set_bin_data(msg, body->str, body->len);
msn_switchboard_send_msg(swboard, msg, TRUE);
msn_message_unref(msg);
}
static void msn_emoticon_destroy(MsnEmoticon *emoticon)
{
if (emoticon->obj)
msn_object_destroy(emoticon->obj, FALSE);
g_free(emoticon->smile);
g_free(emoticon);
}
static GSList* msn_msg_grab_emoticons(const char *msg, const char *username)
{
GSList *list = NULL;
GList *smileys, *it;
MsnEmoticon *emoticon;
smileys = purple_smiley_parser_find(purple_smiley_custom_get_list(),
msg, FALSE);
for (it = smileys; it; it = g_list_next(it)) {
PurpleSmiley *smiley = it->data;
PurpleImage *img;
img = purple_smiley_get_image(smiley);
emoticon = g_new0(MsnEmoticon, 1);
emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley));
emoticon->ps = smiley;
/* TODO: we are leaking file location, consider using
* purple_image_get_friendly_filename. */
emoticon->obj = msn_object_new_from_image(img,
purple_image_get_path(purple_smiley_get_image(smiley)),
username, MSN_OBJECT_EMOTICON);
list = g_slist_prepend(list, emoticon);
}
g_list_free(smileys);
return list;
}
void
msn_send_im_message(MsnSession *session, MsnMessage *msg)
{
MsnEmoticon *smile;
GSList *smileys;
GString *emoticons = NULL;
const char *username = purple_account_get_username(session->account);
MsnSwitchBoard *swboard = msn_session_get_swboard(session, msg->remote_user, MSN_SB_FLAG_IM);
smileys = msn_msg_grab_emoticons(msg->body, username);
while (smileys) {
smile = (MsnEmoticon *)smileys->data;
emoticons = msn_msg_emoticon_add(emoticons, smile);
msn_emoticon_destroy(smile);
smileys = g_slist_delete_link(smileys, smileys);
}
if (emoticons) {
msn_send_emoticons(swboard, emoticons);
g_string_free(emoticons, TRUE);
}
msn_switchboard_send_msg(swboard, msg, TRUE);
}
static int
msn_send_im(PurpleConnection *gc, PurpleMessage *pmsg)
{
PurpleAccount *account;
MsnSession *session;
MsnSwitchBoard *swboard;
MsnMessage *msg;
char *msgformat;
char *msgtext;
size_t msglen;
const char *username;
const gchar *rcpt = purple_message_get_recipient(pmsg);
PurpleMessageFlags flags = purple_message_get_flags(pmsg);
PurpleBuddy *buddy = purple_blist_find_buddy(purple_connection_get_account(gc), rcpt);
const gchar *cont = purple_message_get_contents(pmsg);
account = purple_connection_get_account(gc);
username = purple_account_get_username(account);
session = purple_connection_get_protocol_data(gc);
swboard = msn_session_find_swboard(session, rcpt);
if (!strncmp("tel:+", rcpt, 5)) {
char *text = purple_markup_strip_html(cont);
send_to_mobile(gc, rcpt, text);
g_free(text);
return 1;
}
if (buddy) {
PurplePresence *p = purple_buddy_get_presence(buddy);
if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
char *text = purple_markup_strip_html(cont);
send_to_mobile(gc, rcpt, text);
g_free(text);
return 1;
}
}
msn_import_html(cont, &msgformat, &msgtext);
msglen = strlen(msgtext);
if (msglen == 0) {
/* Stuff like <hr> will be ignored. Don't send an empty message
if that's all there is. */
g_free(msgtext);
g_free(msgformat);
return 0;
}
if (msglen + strlen(msgformat) + strlen(VERSION) > 1564)
{
g_free(msgformat);
g_free(msgtext);
return -E2BIG;
}
msg = msn_message_new_plain(msgtext);
msg->remote_user = g_strdup(rcpt);
msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
g_free(msgformat);
g_free(msgtext);
if (g_ascii_strcasecmp(rcpt, username))
{
if (flags & PURPLE_MESSAGE_AUTO_RESP) {
msn_message_set_flag(msg, 'U');
}
if (msn_user_is_yahoo(account, rcpt) || !(msn_user_is_online(account, rcpt) || swboard != NULL)) {
/*we send the online and offline Message to Yahoo User via UBM*/
purple_debug_info("msn", "send to offline or Yahoo user\n");
msn_notification_send_uum(session, msg);
} else {
purple_debug_info("msn", "send via switchboard\n");
msn_send_im_message(session, msg);
}
}
else
{
char *body_str, *body_enc, *pre, *post;
const char *format;
MsnIMData *imdata = g_new0(MsnIMData, 1);
/*
* In MSN, you can't send messages to yourself, so
* we'll fake like we received it ;)
*/
body_str = msn_message_to_string(msg);
body_enc = g_markup_escape_text(body_str, -1);
g_free(body_str);
format = msn_message_get_header_value(msg, "X-MMS-IM-Format");
msn_parse_format(format, &pre, &post);
body_str = g_strdup_printf("%s%s%s", pre ? pre : "",
body_enc ? body_enc : "", post ? post : "");
g_free(body_enc);
g_free(pre);
g_free(post);
purple_serv_got_typing_stopped(gc, rcpt);
imdata->gc = g_object_ref(gc);
imdata->who = rcpt;
imdata->msg = body_str;
imdata->flags = flags & ~PURPLE_MESSAGE_SEND;
imdata->when = time(NULL);
purple_timeout_add(0, msn_send_me_im, imdata);
}
msn_message_unref(msg);
return 1;
}
static unsigned int
msn_send_typing(PurpleConnection *gc, const char *who, PurpleIMTypingState state)
{
PurpleAccount *account;
MsnSession *session;
MsnSwitchBoard *swboard;
MsnMessage *msg;
account = purple_connection_get_account(gc);
session = purple_connection_get_protocol_data(gc);
/*
* TODO: I feel like this should be "if (state != PURPLE_IM_TYPING)"
* but this is how it was before, and I don't want to break
* anything. --KingAnt
*/
if (state == PURPLE_IM_NOT_TYPING)
return 0;
if (!g_ascii_strcasecmp(who, purple_account_get_username(account)))
{
/* We'll just fake it, since we're sending to ourself. */
purple_serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, PURPLE_IM_TYPING);
return MSN_TYPING_SEND_TIMEOUT;
}
swboard = msn_session_find_swboard(session, who);
if (swboard == NULL || !msn_switchboard_can_send(swboard))
return 0;
swboard->flag |= MSN_SB_FLAG_IM;
msg = msn_message_new(MSN_MSG_TYPING);
msn_message_set_content_type(msg, "text/x-msmsgscontrol");
msn_message_set_flag(msg, 'U');
msn_message_set_header(msg, "TypingUser",
purple_account_get_username(account));
msn_message_set_bin_data(msg, "\r\n", 2);
msn_switchboard_send_msg(swboard, msg, FALSE);
msn_message_unref(msg);
return MSN_TYPING_SEND_TIMEOUT;
}
static void
msn_set_status(PurpleAccount *account, PurpleStatus *status)
{
PurpleConnection *gc;
MsnSession *session;
gc = purple_account_get_connection(account);
if (gc != NULL)
{
session = purple_connection_get_protocol_data(gc);
msn_change_status(session);
}
}
static void
msn_set_idle(PurpleConnection *gc, int idle)
{
MsnSession *session;
session = purple_connection_get_protocol_data(gc);
msn_change_status(session);
}
/*
* Actually adds a buddy once we have the response from FQY
*/
static void
add_pending_buddy(MsnSession *session,
const char *who,
MsnNetwork network,
MsnUser *user)
{
char *group;
MsnUserList *userlist;
MsnUser *user2;
g_return_if_fail(user != NULL);
if (network == MSN_NETWORK_UNKNOWN) {
purple_debug_error("msn", "Network in FQY response was unknown. "
"Assuming %s is a passport user and adding anyway.\n", who);
network = MSN_NETWORK_PASSPORT;
}
group = msn_user_remove_pending_group(user);
userlist = session->userlist;
user2 = msn_userlist_find_user(userlist, who);
if (user2 != NULL) {
/* User already in userlist, so just update it. */
msn_user_unref(user);
user = user2;
} else {
msn_userlist_add_user(userlist, user);
msn_user_unref(user);
}
msn_user_set_network(user, network);
msn_userlist_add_buddy(userlist, who, group);
g_free(group);
}
static void
msn_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
{
PurpleAccount *account;
const char *bname, *gname;
MsnSession *session;
MsnUserList *userlist;
MsnUser *user;
account = purple_connection_get_account(pc);
session = purple_connection_get_protocol_data(pc);
bname = purple_buddy_get_name(buddy);
if (!session->logged_in)
{
purple_debug_error("msn", "msn_add_buddy called before connected\n");
return;
}
/* XXX - Would group ever be NULL here? I don't think so...
* shx: Yes it should; MSN handles non-grouped buddies, and this is only
* internal.
* KingAnt: But PurpleBuddys must always exist inside PurpleGroups, so
* won't group always be non-NULL here?
*/
bname = msn_normalize(account, bname);
gname = group ? purple_group_get_name(group) : NULL;
purple_debug_info("msn", "Add user:%s to group:%s\n",
bname, gname ? gname : "(null)");
if (!msn_email_is_valid(bname)) {
gchar *buf;
buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid. Usernames must be valid email addresses."), bname);
if (!purple_conversation_present_error(bname, account, buf)) {
purple_notify_error(pc, NULL, _("Unable to Add"), buf,
purple_request_cpar_from_connection(pc));
}
g_free(buf);
/* Remove from local list */
purple_blist_remove_buddy(buddy);
return;
}
/* Make sure name is normalized */
purple_buddy_set_name(buddy, bname);
userlist = session->userlist;
user = msn_userlist_find_user(userlist, bname);
if (user && user->authorized) {
message = NULL;
}
if ((user != NULL) && (user->networkid != MSN_NETWORK_UNKNOWN)) {
/* We already know this buddy and their network. This function knows
what to do with users already in the list and stuff... */
msn_user_set_invite_message(user, message);
msn_userlist_add_buddy(userlist, bname, gname);
} else {
char **tokens;
char *fqy;
/* We need to check the network for this buddy first */
user = msn_user_new(userlist, bname, NULL);
msn_user_set_invite_message(user, message);
msn_user_set_pending_group(user, gname);
msn_user_set_network(user, MSN_NETWORK_UNKNOWN);
/* Should probably re-use the msn_add_contact_xml function here */
tokens = g_strsplit(bname, "@", 2);
fqy = g_strdup_printf("<ml><d n=\"%s\"><c n=\"%s\"/></d></ml>",
tokens[1],
tokens[0]);
/* TODO: I think user will leak if we disconnect before receiving
a response to this FQY request */
msn_notification_send_fqy(session, fqy, strlen(fqy),
(MsnFqyCb)add_pending_buddy, user);
g_free(fqy);
g_strfreev(tokens);
}
}
static void
msn_rem_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
{
MsnSession *session;
MsnUserList *userlist;
session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
if (!session->logged_in)
return;
/* XXX - Does buddy->name need to be msn_normalize'd here? --KingAnt */
msn_userlist_rem_buddy(userlist, purple_buddy_get_name(buddy));
}
static void
msn_add_permit(PurpleConnection *gc, const char *who)
{
MsnSession *session;
MsnUserList *userlist;
MsnUser *user;
session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
user = msn_userlist_find_user(userlist, who);
if (!session->logged_in)
return;
if (user != NULL && user->list_op & MSN_LIST_BL_OP) {
msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
/* delete contact from Block list and add it to Allow in the callback */
msn_del_contact_from_list(session, NULL, who, MSN_LIST_BL);
} else {
/* just add the contact to Allow list */
msn_add_contact_to_list(session, NULL, who, MSN_LIST_AL);
}
msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
}
static void
msn_add_deny(PurpleConnection *gc, const char *who)
{
MsnSession *session;
MsnUserList *userlist;
MsnUser *user;
session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
user = msn_userlist_find_user(userlist, who);
if (!session->logged_in)
return;
if (user != NULL && user->list_op & MSN_LIST_AL_OP) {
msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
/* delete contact from Allow list and add it to Block in the callback */
msn_del_contact_from_list(session, NULL, who, MSN_LIST_AL);
} else {
/* just add the contact to Block list */
msn_add_contact_to_list(session, NULL, who, MSN_LIST_BL);
}
msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
}
static void
msn_rem_permit(PurpleConnection *gc, const char *who)
{
MsnSession *session;
MsnUserList *userlist;
MsnUser *user;
session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
if (!session->logged_in)
return;
user = msn_userlist_find_user(userlist, who);
msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
msn_del_contact_from_list(session, NULL, who, MSN_LIST_AL);
if (user != NULL && user->list_op & MSN_LIST_RL_OP)
msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
}
static void
msn_rem_deny(PurpleConnection *gc, const char *who)
{
MsnSession *session;
MsnUserList *userlist;
MsnUser *user;
session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
if (!session->logged_in)
return;
user = msn_userlist_find_user(userlist, who);
msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
msn_del_contact_from_list(session, NULL, who, MSN_LIST_BL);
if (user != NULL && user->list_op & MSN_LIST_RL_OP)
msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
}
static void
msn_set_permit_deny(PurpleConnection *gc)
{
msn_send_privacy(gc);
}
static void
msn_chat_invite(PurpleConnection *gc, int id, const char *msg,
const char *who)
{
MsnSession *session;
MsnSwitchBoard *swboard;
session = purple_connection_get_protocol_data(gc);
swboard = msn_session_find_swboard_with_id(session, id);
if (swboard == NULL)
{
/* if we have no switchboard, everyone else left the chat already */
swboard = msn_switchboard_new(session);
msn_switchboard_request(swboard);
swboard->chat_id = id;
swboard->conv = PURPLE_CONVERSATION(purple_conversations_find_chat(gc, id));
}
swboard->flag |= MSN_SB_FLAG_IM;
msn_switchboard_request_add_user(swboard, who);
}
static void
msn_chat_leave(PurpleConnection *gc, int id)
{
MsnSession *session;
MsnSwitchBoard *swboard;
PurpleConversation *conv;
session = purple_connection_get_protocol_data(gc);
swboard = msn_session_find_swboard_with_id(session, id);
/* if swboard is NULL we were the only person left anyway */
if (swboard == NULL)
return;
conv = swboard->conv;
msn_switchboard_release(swboard, MSN_SB_FLAG_IM);
/* If other switchboards managed to associate themselves with this
* conv, make sure they know it's gone! */
if (conv != NULL)
{
while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL)
swboard->conv = NULL;
}
}
static int
msn_chat_send(PurpleConnection *gc, int id, PurpleMessage *pmsg)
{
PurpleAccount *account;
MsnSession *session;
const char *username;
MsnSwitchBoard *swboard;
MsnMessage *msg;
char *msgformat;
char *msgtext;
size_t msglen;
account = purple_connection_get_account(gc);
session = purple_connection_get_protocol_data(gc);
username = purple_account_get_username(account);
swboard = msn_session_find_swboard_with_id(session, id);
if (swboard == NULL)
return -EINVAL;
if (!swboard->ready)
return 0;
swboard->flag |= MSN_SB_FLAG_IM;
msn_import_html(purple_message_get_contents(pmsg), &msgformat, &msgtext);
msglen = strlen(msgtext);
if ((msglen == 0) || (msglen + strlen(msgformat) + strlen(VERSION) > 1564))
{
g_free(msgformat);
g_free(msgtext);
return -E2BIG;
}
msg = msn_message_new_plain(msgtext);
msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
msn_switchboard_send_msg(swboard, msg, FALSE);
msn_message_unref(msg);
g_free(msgformat);
g_free(msgtext);
purple_serv_got_chat_in(gc, id, username, purple_message_get_flags(pmsg),
purple_message_get_contents(pmsg), time(NULL));
return 0;
}
static void
msn_keepalive(PurpleConnection *gc)
{
MsnSession *session;
MsnTransaction *trans;
session = purple_connection_get_protocol_data(gc);
if (!session->http_method)
{
MsnCmdProc *cmdproc;
cmdproc = session->notification->cmdproc;
trans = msn_transaction_new(cmdproc, "PNG", NULL);
msn_transaction_set_saveable(trans, FALSE);
msn_cmdproc_send_trans(cmdproc, trans);
}
}
static void msn_alias_buddy(PurpleConnection *pc, const char *name, const char *alias)
{
MsnSession *session;
session = purple_connection_get_protocol_data(pc);
msn_update_contact(session, name, MSN_UPDATE_ALIAS, alias);
}
static void
msn_group_buddy(PurpleConnection *gc, const char *who,
const char *old_group_name, const char *new_group_name)
{
MsnSession *session;
MsnUserList *userlist;
session = purple_connection_get_protocol_data(gc);
userlist = session->userlist;
msn_userlist_move_buddy(userlist, who, old_group_name, new_group_name);
}
static void
msn_rename_group(PurpleConnection *gc, const char *old_name,
PurpleGroup *group, GList *moved_buddies)
{
MsnSession *session;
const char *gname;
session = purple_connection_get_protocol_data(gc);
g_return_if_fail(session != NULL);
g_return_if_fail(session->userlist != NULL);
gname = purple_group_get_name(group);
if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL)
{
msn_contact_rename_group(session, old_name, gname);
}
else
{
/* not found */
msn_add_group(session, NULL, gname);
}
}
static void
msn_convo_closed(PurpleConnection *gc, const char *who)
{
MsnSession *session;
MsnSwitchBoard *swboard;
PurpleConversation *conv;
session = purple_connection_get_protocol_data(gc);
swboard = msn_session_find_swboard(session, who);
/*
* Don't perform an assertion here. If swboard is NULL, then the
* switchboard was either closed by the other party, or the person
* is talking to himself.
*/
if (swboard == NULL)
return;
conv = swboard->conv;
/* If we release the switchboard here, it may still have messages
pending ACK which would result in incorrect unsent message errors.
Just let it timeout... This is *so* going to screw with people who
use dumb clients that report "User has closed the conversation window" */
/* msn_switchboard_release(swboard, MSN_SB_FLAG_IM); */
swboard->conv = NULL;
/* If other switchboards managed to associate themselves with this
* conv, make sure they know it's gone! */
if (conv != NULL)
{
while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL)
swboard->conv = NULL;
}
}
static void
msn_set_buddy_icon(PurpleConnection *gc, PurpleImage *img)
{
MsnSession *session;
MsnUser *user;
session = purple_connection_get_protocol_data(gc);
user = session->user;
msn_user_set_buddy_icon(user, img);
msn_change_status(session);
}
static void
msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
{
MsnSession *session;
const char *gname;
session = purple_connection_get_protocol_data(gc);
gname = purple_group_get_name(group);
purple_debug_info("msn", "Remove group %s\n", gname);
/*we can't delete the default group*/
if(!strcmp(gname, MSN_INDIVIDUALS_GROUP_NAME)||
!strcmp(gname, MSN_NON_IM_GROUP_NAME))
{
purple_debug_info("msn", "This group can't be removed, returning.\n");
return ;
}
msn_del_group(session, gname);
}
/**
* Extract info text from info_data and add it to user_info
*/
static gboolean
msn_tooltip_extract_info_text(PurpleNotifyUserInfo *user_info, MsnGetInfoData *info_data)
{
PurpleBuddy *b;
b = purple_blist_find_buddy(purple_connection_get_account(info_data->gc),
info_data->name);
if (b)
{
char *tmp;
const char *alias;
alias = purple_buddy_get_local_alias(b);
if (alias && alias[0])
{
purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), alias);
}
if ((alias = purple_buddy_get_server_alias(b)) != NULL)
{
char *nicktext = g_markup_escape_text(alias, -1);
tmp = g_strdup_printf("<font sml=\"msn\">%s</font>", nicktext);
purple_notify_user_info_add_pair_html(user_info, _("Nickname"), tmp);
g_free(tmp);
g_free(nicktext);
}
/* Add the tooltip information */
msn_tooltip_text(b, user_info, TRUE);
return TRUE;
}
return FALSE;
}
#if PHOTO_SUPPORT
static char *
msn_get_photo_url(const char *url_text)
{
char *p, *q;
if ((p = strstr(url_text, PHOTO_URL)) != NULL)
{
p += strlen(PHOTO_URL);
}
if (p && (strncmp(p, "http://", strlen("http://")) == 0) && ((q = strchr(p, '"')) != NULL))
return g_strndup(p, q - p);
return NULL;
}
static void msn_got_photo(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
gpointer _info2_data);
#endif
#if 0
static char *msn_info_date_reformat(const char *field, size_t len)
{
char *tmp = g_strndup(field, len);
time_t t = purple_str_to_time(tmp, FALSE, NULL, NULL, NULL);
g_free(tmp);
return g_strdup(purple_date_format_short(localtime(&t)));
}
#endif
#define MSN_GOT_INFO_GET_FIELD(a, b) \
found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \
"\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, NULL); \
if (found) \
sect_info = TRUE;
#define MSN_GOT_INFO_GET_FIELD_NO_SEARCH(a, b) \
found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \
"\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, msn_info_strip_search_link); \
if (found) \
sect_info = TRUE;
static char *
msn_info_strip_search_link(const char *field, size_t len)
{
const char *c;
if ((c = strstr(field, " (http://")) == NULL)
return g_strndup(field, len);
return g_strndup(field, c - field);
}
static void
msn_got_info(PurpleHttpConnection *http_conn,
PurpleHttpResponse *response, gpointer _info_data)
{
MsnGetInfoData *info_data = _info_data;
MsnSession *session;
PurpleNotifyUserInfo *user_info;
char *stripped, *p, *q, *tmp;
char *user_url = NULL;
gboolean found;
gboolean has_tooltip_text = FALSE;
gboolean has_info = FALSE;
gboolean sect_info = FALSE;
gboolean has_contact_info = FALSE;
char *url_buffer;
int stripped_len;
const gchar *got_data;
size_t got_len;
#if PHOTO_SUPPORT
char *photo_url_text = NULL;
MsnGetInfoStepTwoData *info2_data = NULL;
#endif
session = purple_connection_get_protocol_data(info_data->gc);
user_info = purple_notify_user_info_new();
has_tooltip_text = msn_tooltip_extract_info_text(user_info, info_data);
if (!purple_http_response_is_successful(response))
{
purple_notify_user_info_add_pair_html(user_info,
_("Error retrieving profile"), NULL);
purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
purple_notify_user_info_destroy(user_info);
g_free(info_data->name);
g_free(info_data);
return;
}
got_data = purple_http_response_get_data(response, &got_len);
purple_debug_info("msn", "In msn_got_info,url_text:{%s}\n", got_data);
url_buffer = g_strdup(got_data);
/* If they have a homepage link, MSN masks it such that we need to
* fetch the url out before purple_markup_strip_html() nukes it */
/* I don't think this works with the new spaces profiles - Stu 3/2/06 */
if ((p = strstr(url_buffer,
"Take a look at my </font><A class=viewDesc title=\"")) != NULL)
{
p += 50;
if ((q = strchr(p, '"')) != NULL)
user_url = g_strndup(p, q - p);
}
/*
* purple_markup_strip_html() doesn't strip out character entities like &nbsp;
* and &#183;
*/
while ((p = strstr(url_buffer, "&nbsp;")) != NULL)
{
*p = ' '; /* Turn &nbsp;'s into ordinary blanks */
p += 1;
memmove(p, p + 5, strlen(p + 5));
url_buffer[strlen(url_buffer) - 5] = '\0';
}
while ((p = strstr(url_buffer, "&#183;")) != NULL)
{
memmove(p, p + 6, strlen(p + 6));
url_buffer[strlen(url_buffer) - 6] = '\0';
}
/* Nuke the nasty \r's that just get in the way */
purple_str_strip_char(url_buffer, '\r');
/* MSN always puts in &#39; for apostrophes...replace them */
while ((p = strstr(url_buffer, "&#39;")) != NULL)
{
*p = '\'';
memmove(p + 1, p + 5, strlen(p + 5));
url_buffer[strlen(url_buffer) - 4] = '\0';
}
/* Nuke the html, it's easier than trying to parse the horrid stuff */
stripped = purple_markup_strip_html(url_buffer);
stripped_len = strlen(stripped);
purple_debug_misc("msn", "stripped = %p\n", stripped);
purple_debug_misc("msn", "url_buffer = %p\n", url_buffer);
/* General section header */
if (has_tooltip_text)
purple_notify_user_info_add_section_break(user_info);
purple_notify_user_info_add_section_header(user_info, _("General"));
/* Extract their Name and put it in */
MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
/* General */
MSN_GOT_INFO_GET_FIELD("Nickname", _("Nickname"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Age", _("Age"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Gender", _("Gender"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Occupation", _("Occupation"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Location", _("Location"));
/* Extract their Interests and put it in */
found = purple_markup_extract_info_field(stripped, stripped_len, user_info,
"\nInterests\t", 0, " (/default.aspx?page=searchresults", 0,
"Undisclosed", _("Hobbies and Interests") /* _("Interests") */,
0, NULL, NULL);
if (found)
sect_info = TRUE;
MSN_GOT_INFO_GET_FIELD("More about me", _("A Little About Me"));
if (sect_info)
{
has_info = TRUE;
sect_info = FALSE;
}
else
{
/* Remove the section header */
purple_notify_user_info_remove_last_item(user_info);
if (has_tooltip_text)
purple_notify_user_info_remove_last_item(user_info);
}
/* Social */
purple_notify_user_info_add_section_break(user_info);
purple_notify_user_info_add_section_header(user_info, _("Social"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Marital status", _("Marital Status"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Interested in", _("Interests"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Pets", _("Pets"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Hometown", _("Hometown"));
MSN_GOT_INFO_GET_FIELD("Places lived", _("Places Lived"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Fashion", _("Fashion"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Humor", _("Humor"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Music", _("Music"));
MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Favorite quote", _("Favorite Quote"));
if (sect_info)
{
has_info = TRUE;
sect_info = FALSE;
}
else
{
/* Remove the section header */
purple_notify_user_info_remove_last_item(user_info);
purple_notify_user_info_remove_last_item(user_info);
}
/* Contact Info */
/* Personal */
purple_notify_user_info_add_section_break(user_info);
purple_notify_user_info_add_section_header(user_info, _("Contact Info"));
purple_notify_user_info_add_section_header(user_info, _("Personal"));
MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
MSN_GOT_INFO_GET_FIELD("Significant other", _("Significant Other"));
MSN_GOT_INFO_GET_FIELD("Home phone", _("Home Phone"));
MSN_GOT_INFO_GET_FIELD("Home phone 2", _("Home Phone 2"));
MSN_GOT_INFO_GET_FIELD("Home address", _("Home Address"));
MSN_GOT_INFO_GET_FIELD("Personal Mobile", _("Personal Mobile"));
MSN_GOT_INFO_GET_FIELD("Home fax", _("Home Fax"));
MSN_GOT_INFO_GET_FIELD("Personal email", _("Personal Email"));
MSN_GOT_INFO_GET_FIELD("Personal IM", _("Personal IM"));
MSN_GOT_INFO_GET_FIELD("Birthday", _("Birthday"));
MSN_GOT_INFO_GET_FIELD("Anniversary", _("Anniversary"));
MSN_GOT_INFO_GET_FIELD("Notes", _("Notes"));
if (sect_info)
{
has_info = TRUE;
sect_info = FALSE;
has_contact_info = TRUE;
}
else
{
/* Remove the section header */
purple_notify_user_info_remove_last_item(user_info);
}
/* Business */
purple_notify_user_info_add_section_header(user_info, _("Work"));
MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
MSN_GOT_INFO_GET_FIELD("Job title", _("Job Title"));
MSN_GOT_INFO_GET_FIELD("Company", _("Company"));
MSN_GOT_INFO_GET_FIELD("Department", _("Department"));
MSN_GOT_INFO_GET_FIELD("Profession", _("Profession"));
MSN_GOT_INFO_GET_FIELD("Work phone 1", _("Work Phone"));
MSN_GOT_INFO_GET_FIELD("Work phone 2", _("Work Phone 2"));
MSN_GOT_INFO_GET_FIELD("Work address", _("Work Address"));
MSN_GOT_INFO_GET_FIELD("Work mobile", _("Work Mobile"));
MSN_GOT_INFO_GET_FIELD("Work pager", _("Work Pager"));
MSN_GOT_INFO_GET_FIELD("Work fax", _("Work Fax"));
MSN_GOT_INFO_GET_FIELD("Work email", _("Work Email"));
MSN_GOT_INFO_GET_FIELD("Work IM", _("Work IM"));
MSN_GOT_INFO_GET_FIELD("Start date", _("Start Date"));
MSN_GOT_INFO_GET_FIELD("Notes", _("Notes"));
if (sect_info)
{
has_info = TRUE;
has_contact_info = TRUE;
#if 0
/* it's true, but we don't need these assignments */
sect_info = FALSE;
#endif
}
else
{
/* Remove the section header */
purple_notify_user_info_remove_last_item(user_info);
}
if (!has_contact_info)
{
/* Remove the Contact Info section header */
purple_notify_user_info_remove_last_item(user_info);
}
#if 0 /* these probably don't show up any more */
/*
* The fields, 'A Little About Me', 'Favorite Things', 'Hobbies
* and Interests', 'Favorite Quote', and 'My Homepage' may or may
* not appear, in any combination. However, they do appear in
* certain order, so we can successively search to pin down the
* distinct values.
*/
/* Check if they have A Little About Me */
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" A Little About Me \n\n", 0, "Favorite Things", '\n', NULL,
_("A Little About Me"), 0, NULL, NULL);
if (!found)
{
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" A Little About Me \n\n", 0, "Hobbies and Interests", '\n',
NULL, _("A Little About Me"), 0, NULL, NULL);
}
if (!found)
{
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" A Little About Me \n\n", 0, "Favorite Quote", '\n', NULL,
_("A Little About Me"), 0, NULL, NULL);
}
if (!found)
{
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" A Little About Me \n\n", 0, "My Homepage \n\nTake a look",
'\n',
NULL, _("A Little About Me"), 0, NULL, NULL);
}
if (!found)
{
purple_markup_extract_info_field(stripped, stripped_len, s,
" A Little About Me \n\n", 0, "last updated", '\n', NULL,
_("A Little About Me"), 0, NULL, NULL);
}
if (found)
has_info = TRUE;
/* Check if they have Favorite Things */
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" Favorite Things \n\n", 0, "Hobbies and Interests", '\n', NULL,
_("Favorite Things"), 0, NULL, NULL);
if (!found)
{
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" Favorite Things \n\n", 0, "Favorite Quote", '\n', NULL,
_("Favorite Things"), 0, NULL, NULL);
}
if (!found)
{
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" Favorite Things \n\n", 0, "My Homepage \n\nTake a look", '\n',
NULL, _("Favorite Things"), 0, NULL, NULL);
}
if (!found)
{
purple_markup_extract_info_field(stripped, stripped_len, s,
" Favorite Things \n\n", 0, "last updated", '\n', NULL,
_("Favorite Things"), 0, NULL, NULL);
}
if (found)
has_info = TRUE;
/* Check if they have Hobbies and Interests */
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" Hobbies and Interests \n\n", 0, "Favorite Quote", '\n', NULL,
_("Hobbies and Interests"), 0, NULL, NULL);
if (!found)
{
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" Hobbies and Interests \n\n", 0, "My Homepage \n\nTake a look",
'\n', NULL, _("Hobbies and Interests"), 0, NULL, NULL);
}
if (!found)
{
purple_markup_extract_info_field(stripped, stripped_len, s,
" Hobbies and Interests \n\n", 0, "last updated", '\n', NULL,
_("Hobbies and Interests"), 0, NULL, NULL);
}
if (found)
has_info = TRUE;
/* Check if they have Favorite Quote */
found = purple_markup_extract_info_field(stripped, stripped_len, s,
"Favorite Quote \n\n", 0, "My Homepage \n\nTake a look", '\n', NULL,
_("Favorite Quote"), 0, NULL, NULL);
if (!found)
{
purple_markup_extract_info_field(stripped, stripped_len, s,
"Favorite Quote \n\n", 0, "last updated", '\n', NULL,
_("Favorite Quote"), 0, NULL, NULL);
}
if (found)
has_info = TRUE;
/* Extract the last updated date and put it in */
found = purple_markup_extract_info_field(stripped, stripped_len, s,
" last updated:", 1, "\n", 0, NULL, _("Last Updated"), 0,
NULL, msn_info_date_reformat);
if (found)
has_info = TRUE;
#endif
/* If we were able to fetch a homepage url earlier, stick it in there */
if (user_url != NULL)
{
tmp = g_strdup_printf("<a href=\"%s\">%s</a>", user_url, user_url);
purple_notify_user_info_add_pair_html(user_info, _("Homepage"), tmp);
g_free(tmp);
g_free(user_url);
has_info = TRUE;
}
if (!has_info)
{
/* MSN doesn't actually distinguish between "unknown member" and
* a known member with an empty profile. Try to explain this fact.
* Note that if we have a nonempty tooltip_text, we know the user
* exists.
*/
/* This doesn't work with the new spaces profiles - Stu 3/2/06
char *p = strstr(url_buffer, "Unknown Member </TITLE>");
* This might not work for long either ... */
/* Nope, it failed some time before 5/2/07 :(
char *p = strstr(url_buffer, "form id=\"SpacesSearch\" name=\"SpacesSearch\"");
* Let's see how long this one holds out for ... */
char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http&#58;&#47;&#47;spaces.live.com&#47;profile.aspx&#63;cid&#61;0\"");
PurpleBuddy *b = purple_blist_find_buddy
(purple_connection_get_account(info_data->gc), info_data->name);
purple_notify_user_info_add_pair_html(user_info,
_("Error retrieving profile"), NULL);
purple_notify_user_info_add_pair_plaintext(user_info, NULL,
((p && b) ? _("The user has not created a public profile.") :
(p ? _("MSN reported not being able to find the user's profile. "
"This either means that the user does not exist, "
"or that the user exists "
"but has not created a public profile.") :
_("Could not find " /* This should never happen */
"any information in the user's profile. "
"The user most likely does not exist."))));
}
/* put a link to the actual profile URL */
purple_notify_user_info_add_section_break(user_info);
tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>",
PROFILE_URL, info_data->name, _("View web profile"));
purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
g_free(tmp);
#if PHOTO_SUPPORT
/* Find the URL to the photo; must be before the marshalling [Bug 994207] */
photo_url_text = msn_get_photo_url(got_data);
purple_debug_info("msn", "photo url:{%s}\n", photo_url_text ? photo_url_text : "(null)");
/* Marshall the existing state */
info2_data = g_new0(MsnGetInfoStepTwoData, 1);
info2_data->info_data = info_data;
info2_data->stripped = stripped;
info2_data->url_buffer = url_buffer;
info2_data->user_info = user_info;
info2_data->photo_url_text = photo_url_text;
/* Try to put the photo in there too, if there's one */
if (photo_url_text)
{
PurpleHttpRequest *req;
req = purple_http_request_new(photo_url_text);
purple_http_request_set_max_len(req, MAX_HTTP_BUDDYICON_BYTES);
purple_http_connection_set_add(session->http_reqs,
purple_http_request(info_data->gc, req, msn_got_photo,
info2_data));
purple_http_request_unref(req);
}
else
{
/* Finish the Get Info and show the user something */
msn_got_photo(NULL, NULL, info2_data);
}
}
static void
msn_got_photo(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
gpointer _info2_data)
{
MsnGetInfoStepTwoData *info2_data = _info2_data;
/* Unmarshall the saved state */
MsnGetInfoData *info_data = info2_data->info_data;
char *stripped = info2_data->stripped;
char *url_buffer = info2_data->url_buffer;
PurpleNotifyUserInfo *user_info = info2_data->user_info;
char *photo_url_text = info2_data->photo_url_text;
/* Try to put the photo in there too, if there's one and is readable */
if (response && purple_http_response_is_successful(response))
{
PurpleImage *img;
char buf[1024];
const gchar *photo_data;
size_t len;
guint img_id;
photo_data = purple_http_response_get_data(response, &len);
purple_debug_info("msn", "%s is %" G_GSIZE_FORMAT " bytes\n", photo_url_text, len);
img = purple_image_new_from_data(g_memdup(photo_data, len), len);
img_id = purple_image_store_add_temporary(img);
g_object_unref(img);
g_snprintf(buf, sizeof(buf), "<img id=\""
PURPLE_IMAGE_STORE_PROTOCOL "%u\"><br>", img_id);
purple_notify_user_info_prepend_pair_html(user_info, NULL, buf);
}
/* We continue here from msn_got_info, as if nothing has happened */
#endif
purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
g_free(stripped);
g_free(url_buffer);
purple_notify_user_info_destroy(user_info);
g_free(info_data->name);
g_free(info_data);
#if PHOTO_SUPPORT
g_free(photo_url_text);
g_free(info2_data);
#endif
}
static void
msn_get_info(PurpleConnection *gc, const char *name)
{
MsnSession *session = purple_connection_get_protocol_data(gc);
MsnGetInfoData *data;
data = g_new0(MsnGetInfoData, 1);
data->gc = gc;
data->name = g_strdup(name);
purple_http_connection_set_add(session->http_reqs,
purple_http_get_printf(gc, msn_got_info, data, "%s%s",
PROFILE_URL, name));
}
static PurpleAccount *find_acct(const char *protocol, const char *acct_id)
{
PurpleAccount *acct = NULL;
/* If we have a specific acct, use it */
if (acct_id) {
acct = purple_accounts_find(acct_id, protocol);
if (acct && !purple_account_is_connected(acct))
acct = NULL;
} else { /* Otherwise find an active account for the protocol */
GList *l = purple_accounts_get_all();
while (l) {
if (!strcmp(protocol, purple_account_get_protocol_id(l->data))
&& purple_account_is_connected(l->data)) {
acct = l->data;
break;
}
l = l->next;
}
}
return acct;
}
static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *params)
{
char *acct_id = g_hash_table_lookup(params, "account");
PurpleAccount *acct;
if (g_ascii_strcasecmp(proto, "msnim"))
return FALSE;
acct = find_acct("prpl-msn", acct_id);
if (!acct)
return FALSE;
/* msnim:chat?contact=user@domain.tld */
if (!g_ascii_strcasecmp(cmd, "Chat")) {
char *sname = g_hash_table_lookup(params, "contact");
if (sname) {
PurpleIMConversation *im = purple_conversations_find_im_with_account(
sname, acct);
if (im == NULL)
im = purple_im_conversation_new(acct, sname);
purple_conversation_present(PURPLE_CONVERSATION(im));
}
/*else
**If pidgindialogs_im() was in the core, we could use it here.
* It is all purple_request_* based, but I'm not sure it really belongs in the core
pidgindialogs_im();*/
return TRUE;
}
/* msnim:add?contact=user@domain.tld */
else if (!g_ascii_strcasecmp(cmd, "Add")) {
char *name = g_hash_table_lookup(params, "contact");
purple_blist_request_add_buddy(acct, name, NULL, NULL);
return TRUE;
}
return FALSE;
}
static gssize
msn_get_max_message_size(PurpleConversation *conv)
{
/* XXX: pidgin-otr says 1409. Verify and document it. */
return 1525 - strlen(VERSION);
}
static void
msn_protocol_init(PurpleProtocol *protocol)
{
PurpleAccountOption *option;
protocol->id = "prpl-msn";
protocol->name = "MSN";
protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_INVITE_MESSAGE;
protocol->icon_spec = purple_buddy_icon_spec_new("png,gif",
0, 0, 96, 96, 0,
PURPLE_ICON_SCALE_SEND);
option = purple_account_option_string_new(_("Server"), "server",
MSN_SERVER);
protocol->account_options = g_list_append(protocol->account_options,
option);
option = purple_account_option_int_new(_("Port"), "port", MSN_PORT);
protocol->account_options = g_list_append(protocol->account_options,
option);
option = purple_account_option_bool_new(_("Use HTTP Method"),
"http_method", FALSE);
protocol->account_options = g_list_append(protocol->account_options,
option);
option = purple_account_option_string_new(_("HTTP Method Server"),
"http_method_server", MSN_HTTPCONN_SERVER);
protocol->account_options = g_list_append(protocol->account_options,
option);
option = purple_account_option_bool_new(_("Show custom smileys"),
"custom_smileys", TRUE);
protocol->account_options = g_list_append(protocol->account_options,
option);
option = purple_account_option_bool_new(_("Allow direct connections"),
"direct_connect", TRUE);
protocol->account_options = g_list_append(protocol->account_options,
option);
option = purple_account_option_bool_new(_("Allow connecting from multiple locations"),
"mpop", TRUE);
protocol->account_options = g_list_append(protocol->account_options,
option);
}
static void
msn_protocol_class_init(PurpleProtocolClass *klass)
{
klass->login = msn_login;
klass->close = msn_close;
klass->status_types = msn_status_types;
klass->list_icon = msn_list_icon;
}
static void
msn_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
{
client_iface->get_actions = msn_get_actions;
client_iface->list_emblem = msn_list_emblems;
client_iface->status_text = msn_status_text;
client_iface->tooltip_text = msn_tooltip_text;
client_iface->blist_node_menu = msn_blist_node_menu;
client_iface->convo_closed = msn_convo_closed;
client_iface->normalize = msn_normalize;
client_iface->offline_message = msn_offline_message;
client_iface->get_account_text_table = msn_get_account_text_table;
client_iface->get_max_message_size = msn_get_max_message_size;
}
static void
msn_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
{
server_iface->get_info = msn_get_info;
server_iface->set_status = msn_set_status;
server_iface->set_idle = msn_set_idle;
server_iface->add_buddy = msn_add_buddy;
server_iface->remove_buddy = msn_rem_buddy;
server_iface->keepalive = msn_keepalive;
server_iface->alias_buddy = msn_alias_buddy;
server_iface->group_buddy = msn_group_buddy;
server_iface->rename_group = msn_rename_group;
server_iface->set_buddy_icon = msn_set_buddy_icon;
server_iface->remove_group = msn_remove_group;
server_iface->set_public_alias = msn_set_public_alias;
server_iface->get_public_alias = msn_get_public_alias;
}
static void
msn_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
{
im_iface->send = msn_send_im;
im_iface->send_typing = msn_send_typing;
}
static void
msn_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
{
chat_iface->invite = msn_chat_invite;
chat_iface->leave = msn_chat_leave;
chat_iface->send = msn_chat_send;
}
static void
msn_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
{
privacy_iface->add_permit = msn_add_permit;
privacy_iface->add_deny = msn_add_deny;
privacy_iface->rem_permit = msn_rem_permit;
privacy_iface->rem_deny = msn_rem_deny;
privacy_iface->set_permit_deny = msn_set_permit_deny;
}
static void
msn_protocol_attention_iface_init(PurpleProtocolAttentionIface *attention_iface)
{
attention_iface->send = msn_send_attention;
attention_iface->get_types = msn_attention_types;
}
static void
msn_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface)
{
xfer_iface->can_receive = msn_can_receive_file;
xfer_iface->send = msn_send_file;
xfer_iface->new_xfer = msn_new_xfer;
}
PURPLE_DEFINE_TYPE_EXTENDED(
MsnProtocol, msn_protocol, PURPLE_TYPE_PROTOCOL, 0,
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
msn_protocol_client_iface_init)
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
msn_protocol_server_iface_init)
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
msn_protocol_im_iface_init)
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
msn_protocol_chat_iface_init)
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
msn_protocol_privacy_iface_init)
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION_IFACE,
msn_protocol_attention_iface_init)
PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE,
msn_protocol_xfer_iface_init)
);
static PurplePluginInfo *
plugin_query(GError **error)
{
return purple_plugin_info_new(
"id", "prpl-msn",
"name", "MSN Protocol",
"version", DISPLAY_VERSION,
"category", N_("Protocol"),
"summary", N_("Windows Live Messenger Protocol Plugin"),
"description", N_("Windows Live Messenger Protocol Plugin"),
"website", PURPLE_WEBSITE,
"abi-version", PURPLE_ABI_VERSION,
"flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
NULL
);
}
static gboolean
plugin_load(PurplePlugin *plugin, GError **error)
{
PurpleCmdId id;
msn_protocol_register_type(plugin);
my_protocol = purple_protocols_add(MSN_TYPE_PROTOCOL, error);
if (!my_protocol)
return FALSE;
id = purple_cmd_register("nudge", "", PURPLE_CMD_P_PROTOCOL,
PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PROTOCOL_ONLY,
"prpl-msn", msn_cmd_nudge,
_("nudge: nudge a user to get their attention"), NULL);
cmds = g_slist_prepend(cmds, GUINT_TO_POINTER(id));
purple_prefs_remove("/plugins/prpl/msn");
msn_notification_init();
msn_switchboard_init();
purple_signal_connect(purple_get_core(), "uri-handler", my_protocol,
PURPLE_CALLBACK(msn_uri_handler), NULL);
return TRUE;
}
static gboolean
plugin_unload(PurplePlugin *plugin, GError **error)
{
msn_notification_end();
msn_switchboard_end();
while (cmds) {
PurpleCmdId id = GPOINTER_TO_UINT(cmds->data);
purple_cmd_unregister(id);
cmds = g_slist_delete_link(cmds, cmds);
}
if (!purple_protocols_remove(my_protocol, error))
return FALSE;
return TRUE;
}
PURPLE_PLUGIN_INIT(msn, plugin_query, plugin_load, plugin_unload);