view libpurple/protocols/msn/state.c @ 20946:2c039a40babc

More leak fixes.
author Daniel Atallah <daniel.atallah@gmail.com>
date Mon, 15 Oct 2007 03:07:16 +0000
parents 5bef3197383a
children 5913725cbcd6 4495a311b959
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 "msn.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")
};

/* Local Function Prototype*/
static char *msn_build_psm(const char *psmstr,const char *mediastr,
						   const char *guidstr);

/*
 * 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)
{
	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,strlen(psmstr));
	}
	xmlnode_insert_child(dataNode,psmNode);

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

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

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

/* parse CurrentMedia string */
char *
msn_parse_currentmedia(const char *cmedia)
{
	char **cmedia_array;
	GString *buffer = NULL;
	int strings;

	if ((cmedia == NULL) || (*cmedia == '\0')) {
		purple_debug_info("msn", "No currentmedia string\n");
		return NULL;
	}

	purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia);

	cmedia_array = g_strsplit(cmedia, "\\0", 0);

	strings = 0;
	/* Yes, we want to skip the first element here, as it is empty due to
	 * the cmedia string starting with \0 -- see the examples below. */
	while (cmedia_array[++strings] != NULL);

	/* The cmedia_array[2] field contains a 1 if enabled. */
	if ((strings > 3) && (!strcmp(cmedia_array[2], "1"))) {
		char *inptr = cmedia_array[3];

		buffer = g_string_new(NULL);

		while (*inptr != '\0') {
			if ((*inptr == '{') && ((*(inptr + 1) != '\0') && (*(inptr+2) == '}'))) {
				char *tmpptr;
				int tmp;

				errno = 0;
				tmp = strtol(inptr + 1, &tmpptr, 10);

				if (errno == 0 && tmpptr != inptr + 1 &&
				    tmp + 4 < strings) {
					/* Replace {?} tag with appropriate text only when successful.
					 * Skip otherwise. */
					buffer = g_string_append(buffer, cmedia_array[tmp + 4]);
				}
				inptr += 3; /* Skip to the next char after '}' */
			} else {
				buffer = g_string_append_c(buffer, *inptr++);
			}
		}
		purple_debug_info("msn", "Parsed currentmedia string, result: \"%s\"\n",
		                  buffer->str);
	} else {
		purple_debug_info("msn", "Current media marked disabled, not parsing.\n");
	}

	g_strfreev(cmedia_array);
	return buffer ? g_string_free(buffer, FALSE) : NULL;
}

/* get the CurrentMedia info from the XML string */
char *
msn_get_currentmedia(char *xml_str, gsize len)
{
	xmlnode *payloadNode, *currentmediaNode;
	char *currentmedia;
	
	purple_debug_info("msn","msn get CurrentMedia\n");
	payloadNode = xmlnode_from_str(xml_str, len);
	if (!payloadNode){
		purple_debug_error("msn","PSM XML parse Error!\n");
		return NULL;
	}
	currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia");
	if (currentmediaNode == NULL){
		purple_debug_info("msn","No CurrentMedia Node");
		xmlnode_free(payloadNode);
		return NULL;
	}
	currentmedia = xmlnode_get_data(currentmediaNode);

	xmlnode_free(payloadNode);

	return currentmedia;
}

/*get the PSM info from the XML string*/
char *
msn_get_psm(char *xml_str, gsize len)
{
	xmlnode *payloadNode, *psmNode;
	char *psm;
	
	purple_debug_info("MSNP14","msn get PSM\n");
	payloadNode = xmlnode_from_str(xml_str, len);
	if (!payloadNode){
		purple_debug_error("MSNP14","PSM XML parse Error!\n");
		return NULL;
	}
	psmNode = xmlnode_get_child(payloadNode, "PSM");
	if (psmNode == NULL){
		purple_debug_info("MSNP14","No PSM status Node");
		xmlnode_free(payloadNode);
		return NULL;
	}
	psm = xmlnode_get_data(psmNode);

	xmlnode_free(payloadNode);

	return psm;
}

/* set the MSN's PSM info,Currently Read from the status Line 
 * Thanks for Cris Code
 */
void
msn_set_psm(MsnSession *session)
{
	PurpleAccount *account = session->account;
	PurplePresence *presence;
	PurpleStatus *status;
	MsnCmdProc *cmdproc;
	MsnTransaction *trans;
	char *payload;
	const char *statusline;
	gchar *unescapedstatusline;

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

	cmdproc = session->notification->cmdproc;
	/*prepare PSM info*/
	if(session->psm){
		g_free(session->psm);
	}
	/*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");
	unescapedstatusline = purple_unescape_html(statusline);
	session->psm = msn_build_psm(unescapedstatusline, NULL, NULL);
	g_free(unescapedstatusline);
	payload = session->psm;

	purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload);
	trans = msn_transaction_new(cmdproc, "UUX","%d",strlen(payload));
	msn_transaction_set_payload(trans, payload, strlen(payload));
	msn_cmdproc_send_trans(cmdproc, trans);
}

void
msn_change_status(MsnSession *session)
{
	PurpleAccount *account;
	MsnCmdProc *cmdproc;
	MsnUser *user;
	MsnObject *msnobj;
	const char *state_text;

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

	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;

	msnobj = msn_user_get_object(user);

	if (msnobj == NULL)
	{
		msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text,
						 MSN_CLIENT_ID);
	}
	else
	{
		char *msnobj_str;

		msnobj_str = msn_object_to_string(msnobj);

		msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text,
						 MSN_CLIENT_ID, purple_url_encode(msnobj_str));

		g_free(msnobj_str);
	}
	msn_set_psm(session);
}

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;
}