view libpurple/protocols/msn/state.c @ 30844:7b1b7a4e0bb4

Documetn P2P Application ID in a enum and use it instead of 'magic numbers'.
author masca@cpw.pidgin.im
date Mon, 05 Jul 2010 22:00:10 +0000
parents a99b6dcdb60d
children a8cc50c2279f
line wrap: on
line source

/**
 * @file state.c State functions and definitions
 *
 * 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
 */

#include "internal.h"
#include "debug.h"

#include "core.h"

#include "notification.h"
#include "state.h"

static const char *away_text[] =
{
	N_("Available"),
	N_("Available"),
	N_("Busy"),
	N_("Idle"),
	N_("Be Right Back"),
	N_("Away From Computer"),
	N_("On The Phone"),
	N_("Out To Lunch"),
	N_("Available"),
	N_("Available")
};

/*
 * WLM media PSM info build prcedure
 *
 * Result can like:
 *	<CurrentMedia>\0Music\01\0{0} - {1}\0Song Title\0Song Artist\0Song Album\0\0</CurrentMedia>\
 *	<CurrentMedia>\0Games\01\0Playing {0}\0Game Name\0</CurrentMedia>\
 *	<CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>"
 */
static char *
msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr, guint protocol_ver)
{
	xmlnode *dataNode,*psmNode,*mediaNode,*guidNode;
	char *result;
	int length;

	dataNode = xmlnode_new("Data");

	psmNode = xmlnode_new("PSM");
	if(psmstr != NULL){
		xmlnode_insert_data(psmNode, psmstr, -1);
	}
	xmlnode_insert_child(dataNode, psmNode);

	mediaNode = xmlnode_new("CurrentMedia");
	if(mediastr != NULL){
		xmlnode_insert_data(mediaNode, mediastr, -1);
	}
	xmlnode_insert_child(dataNode, mediaNode);

	guidNode = xmlnode_new("MachineGuid");
	if(guidstr != NULL){
		xmlnode_insert_data(guidNode, guidstr, -1);
	}
	xmlnode_insert_child(dataNode, guidNode);

	if (protocol_ver >= 16) {
		/* TODO: What is this for? */
		xmlnode *ddpNode = xmlnode_new("DDP");
		xmlnode_insert_child(dataNode, ddpNode);
	}

	result = xmlnode_to_str(dataNode, &length);
	xmlnode_free(dataNode);
	return result;
}

/* get the CurrentMedia info from the XML node */
char *
msn_get_currentmedia(xmlnode *payloadNode)
{
	xmlnode *currentmediaNode;
	char *currentmedia;

	purple_debug_info("msn", "Get CurrentMedia\n");
	currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia");
	if (currentmediaNode == NULL) {
		purple_debug_info("msn", "No CurrentMedia Node\n");
		return NULL;
	}
	currentmedia = xmlnode_get_data(currentmediaNode);

	return currentmedia;
}

/* Get the PSM info from the XML node */
char *
msn_get_psm(xmlnode *payloadNode)
{
	xmlnode *psmNode;
	char *psm;

	purple_debug_info("msn", "msn get PSM\n");
	psmNode = xmlnode_get_child(payloadNode, "PSM");
	if (psmNode == NULL) {
		purple_debug_info("msn", "No PSM status Node\n");
		return NULL;
	}
	psm = xmlnode_get_data(psmNode);

	return psm;
}

static char *
create_media_string(PurplePresence *presence)
{
	const char *title, *game, *office;
	char *ret;
	PurpleStatus *status = purple_presence_get_status(presence, "tune");
	if (!status || !purple_status_is_active(status))
		return NULL;

	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);
		ret = g_strdup_printf("WMP\\0Music\\01\\0{0}%s%s\\0%s\\0%s\\0%s\\0",
		                      artist ? " - {1}" : "",
		                      album ? " ({2})" : "",
		                      title,
		                      artist ? artist : "",
		                      album ? album : "");
	}
	else if (game && *game)
		ret = g_strdup_printf("\\0Games\\01\\0Playing {0}\\0%s\\0", game);
	else if (office && *office)
		ret = g_strdup_printf("\\0Office\\01\\0Editing {0}\\0%s\\0", office);
	else
		ret = NULL;

	return ret;
}

/* set the MSN's PSM info,Currently Read from the status Line
 * Thanks for Cris Code
 */
static void
msn_set_psm(MsnSession *session)
{
	PurpleAccount *account;
	PurplePresence *presence;
	PurpleStatus *status;
	char *payload;
	const char *statusline;
	gchar *statusline_stripped, *media = NULL;

	g_return_if_fail(session != NULL);
	g_return_if_fail(session->notification != NULL);

	account = session->account;

	/* Get the PSM string from Purple's Status Line */
	presence = purple_account_get_presence(account);
	status = purple_presence_get_active_status(presence);
	statusline = purple_status_get_attr_string(status, "message");

	/* MSN expects plain text, not HTML */
	statusline_stripped = purple_markup_strip_html(statusline);
	media = create_media_string(presence);
	g_free(session->psm);
	session->psm = msn_build_psm(statusline_stripped, media, session->protocol_ver >= 16 ? session->guid : NULL, session->protocol_ver);

	payload = session->psm;

	msn_notification_send_uux(session, payload);

	g_free(statusline_stripped);
	g_free(media);
}

void
msn_change_status(MsnSession *session)
{
	PurpleAccount *account;
	MsnCmdProc *cmdproc;
	MsnTransaction *trans;
	MsnUser *user;
	MsnObject *msnobj;
	const char *state_text;
	GHashTable *ui_info = purple_core_get_ui_info();
	MsnClientCaps caps = MSN_CLIENT_ID;

	g_return_if_fail(session != NULL);
	g_return_if_fail(session->notification != NULL);

	/* set client caps based on what the UI tells us it is... */
	if (ui_info) {
		const gchar *client_type = g_hash_table_lookup(ui_info, "client_type");
		if (client_type) {
			if (strcmp(client_type, "phone") == 0 ||
				strcmp(client_type, "handheld") == 0) {
				caps |= MSN_CLIENT_CAP_WIN_MOBILE;
			} else if (strcmp(client_type, "web") == 0) {
				caps |= MSN_CLIENT_CAP_WEBMSGR;
			} else if (strcmp(client_type, "bot") == 0) {
				caps |= MSN_CLIENT_CAP_BOT;
			}
			/* MSN doesn't a "console" type... 
			 What, they have no ncurses UI? :-) */
		}
	}

	account = session->account;
	cmdproc = session->notification->cmdproc;
	user = session->user;
	state_text = msn_state_get_text(msn_state_from_account(account));

	/* If we're not logged in yet, don't send the status to the server,
	 * it will be sent when login completes
	 */
	if (!session->logged_in)
		return;

	msn_set_psm(session);

	msnobj = msn_user_get_object(user);

	if (msnobj == NULL)
	{
		if (session->protocol_ver >= 16)
			trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u 0", state_text, caps, MSN_CLIENT_ID_EXT_CAPS);
		else
			trans = msn_transaction_new(cmdproc, "CHG", "%s %u", state_text, caps);
	}
	else
	{
		char *msnobj_str;

		msnobj_str = msn_object_to_string(msnobj);

		if (session->protocol_ver >= 16)
			trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u %s", state_text,
							 caps, MSN_CLIENT_ID_EXT_CAPS, purple_url_encode(msnobj_str));
		else
			trans = msn_transaction_new(cmdproc, "CHG", "%s %u %s", state_text,
							 caps, purple_url_encode(msnobj_str));

		g_free(msnobj_str);
	}

	msn_cmdproc_send_trans(cmdproc, trans);
}

const char *
msn_away_get_text(MsnAwayType type)
{
	g_return_val_if_fail(type <= MSN_HIDDEN, NULL);

	return _(away_text[type]);
}

const char *
msn_state_get_text(MsnAwayType state)
{
	static char *status_text[] =
	{ "NLN", "NLN", "BSY", "IDL", "BRB", "AWY", "PHN", "LUN", "HDN", "HDN" };

	return status_text[state];
}

MsnAwayType
msn_state_from_account(PurpleAccount *account)
{
	MsnAwayType msnstatus;
	PurplePresence *presence;
	PurpleStatus *status;
	const char *status_id;

	presence = purple_account_get_presence(account);
	status = purple_presence_get_active_status(presence);
	status_id = purple_status_get_id(status);

	if (!strcmp(status_id, "away"))
		msnstatus = MSN_AWAY;
	else if (!strcmp(status_id, "brb"))
		msnstatus = MSN_BRB;
	else if (!strcmp(status_id, "busy"))
		msnstatus = MSN_BUSY;
	else if (!strcmp(status_id, "phone"))
		msnstatus = MSN_PHONE;
	else if (!strcmp(status_id, "lunch"))
		msnstatus = MSN_LUNCH;
	else if (!strcmp(status_id, "invisible"))
		msnstatus = MSN_HIDDEN;
	else
		msnstatus = MSN_ONLINE;

	if ((msnstatus == MSN_ONLINE) && purple_presence_is_idle(presence))
		msnstatus = MSN_IDLE;

	return msnstatus;
}