pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
IRC: fill required command parameter counts (part 3)
release-2.x.y
2014-01-16, Tomasz Wasilczyk
6b0e0566af20
IRC: fill required command parameter counts (part 3)
/* MySpaceIM Protocol Plugin - markup
*
* Copyright (C) 2007, Jeff Connelly <jeff2@soc.pidgin.im>
*
* 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
"myspace.h"
typedef
int
(
*
MSIM_XMLNODE_CONVERT
)(
MsimSession
*
,
xmlnode
*
,
gchar
**
,
gchar
**
);
/* Globals */
/* The names in in emoticon_names (for <i n=whatever>) map to corresponding
* entries in emoticon_symbols (for the ASCII representation of the emoticon).
*
* Multiple emoticon symbols in Pidgin can map to one name. List the
* canonical form, as inserted by the "Smile!" dialog, first. For example,
* :) comes before :-), because although both are recognized as 'happy',
* the first is inserted by the smiley button (first symbol in theme).
*
* Note that symbols are case-sensitive in Pidgin -- :-X is not :-x. */
static
struct
MSIM_EMOTICON
{
gchar
*
name
;
gchar
*
symbol
;
}
msim_emoticons
[]
=
{
/* Unfortunately, this list duplicates much of the file
* pidgin/pidgin/pixmaps/emotes/default/22/default.theme.in, because
* that file is part of Pidgin, but we're part of libpurple.
*/
{
"bigsmile"
,
":D"
},
{
"bigsmile"
,
":-D"
},
{
"devil"
,
"}:)"
},
{
"frazzled"
,
":Z"
},
{
"geek"
,
"B)"
},
{
"googles"
,
"%)"
},
{
"growl"
,
":E"
},
{
"laugh"
,
":))"
},
/* Must be before ':)' */
{
"happy"
,
":)"
},
{
"happy"
,
":-)"
},
{
"happi"
,
":)"
},
{
"heart"
,
":X"
},
{
"mohawk"
,
"-:"
},
{
"mad"
,
"X("
},
{
"messed"
,
"X)"
},
{
"nerd"
,
"Q)"
},
{
"oops"
,
":G"
},
{
"pirate"
,
"P)"
},
{
"scared"
,
":O"
},
{
"sidefrown"
,
":{"
},
{
"sinister"
,
":B"
},
{
"smirk"
,
":,"
},
{
"straight"
,
":|"
},
{
"tongue"
,
":P"
},
{
"tongue"
,
":p"
},
{
"tongy"
,
":P"
},
{
"upset"
,
"B|"
},
{
"wink"
,
";-)"
},
{
"wink"
,
";)"
},
{
"winc"
,
";)"
},
{
"worried"
,
":["
},
{
"kiss"
,
":x"
},
{
NULL
,
NULL
}
};
/* Indexes of this array + 1 map HTML font size to scale of normal font size. *
* Based on _point_sizes from libpurple/gtkimhtml.c
* 1 2 3 4 5 6 7 */
static
gdouble
_font_scale
[]
=
{
.85
,
.95
,
1
,
1.2
,
1.44
,
1.728
,
2.0736
};
/* Purple maximum font size. Equivalent to sizeof(_font_scale) / sizeof(_font_scale[0]) */
#define MAX_FONT_SIZE 7
#define POINTS_PER_INCH 72
/* How many pt's in an inch */
/* Text formatting bits for <f s=#> */
#define MSIM_TEXT_BOLD 1
#define MSIM_TEXT_ITALIC 2
#define MSIM_TEXT_UNDERLINE 4
/* Default baseline size of purple's fonts, in points. What is size 3 in points.
* _font_scale specifies scaling factor relative to this point size. Note this
* is only the default; it is configurable in account options. */
#define MSIM_BASE_FONT_POINT_SIZE 8
/* Default display's DPI. 96 is common but it can differ. Also configurable
* in account options. */
#define MSIM_DEFAULT_DPI 96
/* round is part of C99, but sometimes is unavailable before then.
* Based on http://forums.belution.com/en/cpp/000/050/13.shtml
*/
static
double
msim_round
(
double
value
)
{
if
(
value
<
0
)
{
return
-
(
floor
(
-
value
+
0.5
));
}
else
{
return
floor
(
value
+
0.5
);
}
}
/**
* Convert typographical font point size to HTML font size.
* Based on libpurple/gtkimhtml.c
*/
static
guint
msim_point_to_purple_size
(
MsimSession
*
session
,
guint
point
)
{
guint
size
,
this_point
,
base
;
base
=
purple_account_get_int
(
session
->
account
,
"base_font_size"
,
MSIM_BASE_FONT_POINT_SIZE
);
for
(
size
=
0
;
size
<
MAX_FONT_SIZE
;
++
size
)
{
this_point
=
(
guint
)
msim_round
(
base
*
_font_scale
[
size
]);
if
(
this_point
>=
point
)
{
purple_debug_info
(
"msim"
,
"msim_point_to_purple_size: %d pt -> size=%d
\n
"
,
point
,
size
);
return
size
;
}
}
/* No HTML font size was this big; return largest possible. */
return
this_point
;
}
/**
* Convert HTML font size to point size.
*/
static
guint
msim_purple_size_to_point
(
MsimSession
*
session
,
guint
size
)
{
gdouble
scale
;
guint
point
;
guint
base
;
scale
=
_font_scale
[
CLAMP
(
size
,
1
,
MAX_FONT_SIZE
)
-
1
];
base
=
purple_account_get_int
(
session
->
account
,
"base_font_size"
,
MSIM_BASE_FONT_POINT_SIZE
);
point
=
(
guint
)
msim_round
(
scale
*
base
);
purple_debug_info
(
"msim"
,
"msim_purple_size_to_point: size=%d -> %d pt
\n
"
,
size
,
point
);
return
point
;
}
/**
* Convert a msim markup font pixel height to the more usual point size, for incoming messages.
*/
static
guint
msim_height_to_point
(
MsimSession
*
session
,
guint
height
)
{
guint
dpi
;
dpi
=
purple_account_get_int
(
session
->
account
,
"dpi"
,
MSIM_DEFAULT_DPI
);
return
(
guint
)
msim_round
((
POINTS_PER_INCH
*
1.
/
dpi
)
*
height
);
/* See also: libpurple/protocols/bonjour/jabber.c
* _font_size_ichat_to_purple */
}
/**
* Convert point size to msim pixel height font size specification, for outgoing messages.
*/
static
guint
msim_point_to_height
(
MsimSession
*
session
,
guint
point
)
{
guint
dpi
;
dpi
=
purple_account_get_int
(
session
->
account
,
"dpi"
,
MSIM_DEFAULT_DPI
);
return
(
guint
)
msim_round
((
dpi
*
1.
/
POINTS_PER_INCH
)
*
point
);
}
/**
* Convert the msim markup <f> (font) tag into HTML.
*/
static
void
msim_markup_f_to_html
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
const
gchar
*
face
,
*
height_str
,
*
decor_str
;
GString
*
gs_end
,
*
gs_begin
;
guint
decor
,
height
;
face
=
xmlnode_get_attrib
(
root
,
"f"
);
height_str
=
xmlnode_get_attrib
(
root
,
"h"
);
decor_str
=
xmlnode_get_attrib
(
root
,
"s"
);
/* Validate the font face, to avoid constructing invalid HTML later */
if
(
face
!=
NULL
&&
strchr
(
face
,
'\''
)
!=
NULL
)
face
=
NULL
;
height
=
height_str
!=
NULL
?
atol
(
height_str
)
:
12
;
decor
=
decor_str
!=
NULL
?
atol
(
decor_str
)
:
0
;
/*
* The HTML we're constructing here is a bit redudant. Ideally we
* would use only the font-family and font-size CSS span, but Pidgin
* doesn't support it (it's included for other UIs). For Pidgin we
* wrap the whole thing in an ugly font tag, and Pidgin will happily
* ignore the <span>.
*/
gs_begin
=
g_string_new
(
""
);
if
(
height
&&
!
face
)
{
guint
point_size
=
msim_height_to_point
(
session
,
height
);
g_string_printf
(
gs_begin
,
"<font size='%d'><span style='font-size: %dpt'>"
,
msim_point_to_purple_size
(
session
,
point_size
),
point_size
);
}
else
if
(
height
&&
face
)
{
guint
point_size
=
msim_height_to_point
(
session
,
height
);
g_string_printf
(
gs_begin
,
"<font face='%s' size='%d'><span style='font-family: %s; font-size: %dpt'>"
,
face
,
msim_point_to_purple_size
(
session
,
point_size
),
face
,
point_size
);
}
else
{
g_string_printf
(
gs_begin
,
"<font><span>"
);
}
gs_end
=
g_string_new
(
"</span></font>"
);
if
(
decor
&
MSIM_TEXT_BOLD
)
{
g_string_append
(
gs_begin
,
"<b>"
);
g_string_prepend
(
gs_end
,
"</b>"
);
}
if
(
decor
&
MSIM_TEXT_ITALIC
)
{
g_string_append
(
gs_begin
,
"<i>"
);
g_string_append
(
gs_end
,
"</i>"
);
}
if
(
decor
&
MSIM_TEXT_UNDERLINE
)
{
g_string_append
(
gs_begin
,
"<u>"
);
g_string_append
(
gs_end
,
"</u>"
);
}
*
begin
=
g_string_free
(
gs_begin
,
FALSE
);
*
end
=
g_string_free
(
gs_end
,
FALSE
);
}
/**
* Convert a msim markup color to a color suitable for libpurple.
*
* @param msim Either a color name, or an rgb(x,y,z) code.
*
* @return A new string, either a color name or #rrggbb code. Must g_free().
*/
static
char
*
msim_color_to_purple
(
const
char
*
msim
)
{
guint
red
,
green
,
blue
;
if
(
!
msim
)
{
return
g_strdup
(
"black"
);
}
if
(
sscanf
(
msim
,
"rgb(%d,%d,%d)"
,
&
red
,
&
green
,
&
blue
)
!=
3
)
{
/* Color name. */
return
g_strdup
(
msim
);
}
/* TODO: rgba (alpha). */
return
g_strdup_printf
(
"#%.2x%.2x%.2x"
,
red
,
green
,
blue
);
}
/**
* Convert the msim markup <a> (anchor) tag into HTML.
*/
static
void
msim_markup_a_to_html
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
const
gchar
*
href
;
href
=
xmlnode_get_attrib
(
root
,
"h"
);
if
(
!
href
)
{
href
=
""
;
}
*
begin
=
g_strdup_printf
(
"<a href=
\"
%s
\"
>%s"
,
href
,
href
);
*
end
=
g_strdup
(
"</a>"
);
}
/**
* Convert the msim markup <p> (paragraph) tag into HTML.
*/
static
void
msim_markup_p_to_html
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
/* Just pass through unchanged.
*
* Note: attributes currently aren't passed, if there are any. */
*
begin
=
g_strdup
(
"<p>"
);
*
end
=
g_strdup
(
"</p>"
);
}
/**
* Convert the msim markup <c> tag (text color) into HTML.
*/
static
void
msim_markup_c_to_html
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
const
gchar
*
color
;
gchar
*
purple_color
;
color
=
xmlnode_get_attrib
(
root
,
"v"
);
if
(
!
color
)
{
purple_debug_info
(
"msim"
,
"msim_markup_c_to_html: <c> tag w/o v attr
\n
"
);
*
begin
=
g_strdup
(
""
);
*
end
=
g_strdup
(
""
);
/* TODO: log as unrecognized */
return
;
}
purple_color
=
msim_color_to_purple
(
color
);
#ifdef USE_CSS_FORMATTING
*
begin
=
g_strdup_printf
(
"<span style='color: %s'>"
,
purple_color
);
*
end
=
g_strdup
(
"</span>"
);
#else
*
begin
=
g_strdup_printf
(
"<font color='%s'>"
,
purple_color
);
*
end
=
g_strdup
(
"</font>"
);
#endif
g_free
(
purple_color
);
}
/**
* Convert the msim markup <b> tag (background color) into HTML.
*/
static
void
msim_markup_b_to_html
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
const
gchar
*
color
;
gchar
*
purple_color
;
color
=
xmlnode_get_attrib
(
root
,
"v"
);
if
(
!
color
)
{
*
begin
=
g_strdup
(
""
);
*
end
=
g_strdup
(
""
);
purple_debug_info
(
"msim"
,
"msim_markup_b_to_html: <b> w/o v attr
\n
"
);
/* TODO: log as unrecognized. */
return
;
}
purple_color
=
msim_color_to_purple
(
color
);
#ifdef USE_CSS_FORMATTING
*
begin
=
g_strdup_printf
(
"<span style='background-color: %s'>"
,
purple_color
);
*
end
=
g_strdup
(
"</span>"
);
#else
*
begin
=
g_strdup_printf
(
"<body bgcolor='%s'>"
,
purple_color
);
*
end
=
g_strdup
(
"</body>"
);
#endif
g_free
(
purple_color
);
}
/**
* Convert the msim markup <i> tag (emoticon image) into HTML.
*/
static
void
msim_markup_i_to_html
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
const
gchar
*
name
;
guint
i
;
struct
MSIM_EMOTICON
*
emote
;
name
=
xmlnode_get_attrib
(
root
,
"n"
);
if
(
!
name
)
{
purple_debug_info
(
"msim"
,
"msim_markup_i_to_html: <i> w/o n
\n
"
);
*
begin
=
g_strdup
(
""
);
*
end
=
g_strdup
(
""
);
/* TODO: log as unrecognized */
return
;
}
/* Find and use canonical form of smiley symbol. */
for
(
i
=
0
;
(
emote
=
&
msim_emoticons
[
i
])
&&
emote
->
name
!=
NULL
;
++
i
)
{
if
(
g_str_equal
(
name
,
emote
->
name
))
{
*
begin
=
g_strdup
(
emote
->
symbol
);
*
end
=
g_strdup
(
""
);
return
;
}
}
/* Couldn't find it, sorry. Try to degrade gracefully. */
*
begin
=
g_strdup_printf
(
"**%s**"
,
name
);
*
end
=
g_strdup
(
""
);
}
/**
* Convert an individual msim markup tag to HTML.
*/
static
int
msim_markup_tag_to_html
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
g_return_val_if_fail
(
root
!=
NULL
,
0
);
if
(
g_str_equal
(
root
->
name
,
"f"
))
{
msim_markup_f_to_html
(
session
,
root
,
begin
,
end
);
}
else
if
(
g_str_equal
(
root
->
name
,
"a"
))
{
msim_markup_a_to_html
(
session
,
root
,
begin
,
end
);
}
else
if
(
g_str_equal
(
root
->
name
,
"p"
))
{
msim_markup_p_to_html
(
session
,
root
,
begin
,
end
);
}
else
if
(
g_str_equal
(
root
->
name
,
"c"
))
{
msim_markup_c_to_html
(
session
,
root
,
begin
,
end
);
}
else
if
(
g_str_equal
(
root
->
name
,
"b"
))
{
msim_markup_b_to_html
(
session
,
root
,
begin
,
end
);
}
else
if
(
g_str_equal
(
root
->
name
,
"i"
))
{
msim_markup_i_to_html
(
session
,
root
,
begin
,
end
);
}
else
{
purple_debug_info
(
"msim"
,
"msim_markup_tag_to_html: "
"unknown tag name=%s, ignoring
\n
"
,
root
->
name
?
root
->
name
:
"(NULL)"
);
*
begin
=
g_strdup
(
""
);
*
end
=
g_strdup
(
""
);
}
return
0
;
}
/**
* Convert an individual HTML tag to msim markup.
*/
static
int
html_tag_to_msim_markup
(
MsimSession
*
session
,
xmlnode
*
root
,
gchar
**
begin
,
gchar
**
end
)
{
int
ret
=
0
;
if
(
!
purple_utf8_strcasecmp
(
root
->
name
,
"root"
)
||
!
purple_utf8_strcasecmp
(
root
->
name
,
"html"
))
{
*
begin
=
g_strdup
(
""
);
*
end
=
g_strdup
(
""
);
/* TODO: Coalesce nested tags into one <f> tag!
* Currently, the 's' value will be overwritten when b/i/u is nested
* within another one, and only the inner-most formatting will be
* applied to the text. */
}
else
if
(
!
purple_utf8_strcasecmp
(
root
->
name
,
"b"
))
{
if
(
root
->
child
->
type
==
XMLNODE_TYPE_DATA
)
{
*
begin
=
g_strdup_printf
(
"<f s='%d'>"
,
MSIM_TEXT_BOLD
);
*
end
=
g_strdup
(
"</f>"
);
}
else
{
if
(
!
purple_utf8_strcasecmp
(
root
->
child
->
name
,
"i"
))
{
ret
++
;
if
(
root
->
child
->
child
->
type
==
XMLNODE_TYPE_DATA
)
{
*
begin
=
g_strdup_printf
(
"<f s='%d'>"
,
(
MSIM_TEXT_BOLD
+
MSIM_TEXT_ITALIC
));
*
end
=
g_strdup
(
"</f>"
);
}
else
{
if
(
!
purple_utf8_strcasecmp
(
root
->
child
->
child
->
name
,
"u"
))
{
ret
++
;
*
begin
=
g_strdup_printf
(
"<f s='%d'>"
,
(
MSIM_TEXT_BOLD
+
MSIM_TEXT_ITALIC
+
MSIM_TEXT_UNDERLINE
));
*
end
=
g_strdup
(
"</f>"
);
}
}
}
else
if
(
!
purple_utf8_strcasecmp
(
root
->
child
->
name
,
"u"
))
{
ret
++
;
*
begin
=
g_strdup_printf
(
"<f s='%d'>"
,
(
MSIM_TEXT_BOLD
+
MSIM_TEXT_UNDERLINE
));
*
end
=
g_strdup
(
"</f>"
);
}
}
}
else
if
(
!
purple_utf8_strcasecmp
(
root
->
name
,
"i"
))
{
if
(
root
->
child
->
type
==
XMLNODE_TYPE_DATA
)
{
*
begin
=
g_strdup_printf
(
"<f s='%d'>"
,
MSIM_TEXT_ITALIC
);
*
end
=
g_strdup
(
"</f>"
);
}
else
{
if
(
!
purple_utf8_strcasecmp
(
root
->
child
->
name
,
"u"
))
{
ret
++
;
*
begin
=
g_strdup_printf
(
"<f s='%d'>"
,
(
MSIM_TEXT_ITALIC
+
MSIM_TEXT_UNDERLINE
));
*
end
=
g_strdup
(
"</f>"
);
}
}
}
else
if
(
!
purple_utf8_strcasecmp
(
root
->
name
,
"u"
))
{
*
begin
=
g_strdup_printf
(
"<f s='%d'>"
,
MSIM_TEXT_UNDERLINE
);
*
end
=
g_strdup
(
"</f>"
);
}
else
if
(
!
purple_utf8_strcasecmp
(
root
->
name
,
"a"
))
{
const
gchar
*
href
;
gchar
*
link_text
;
href
=
xmlnode_get_attrib
(
root
,
"href"
);
if
(
!
href
)
{
href
=
xmlnode_get_attrib
(
root
,
"HREF"
);
}
link_text
=
xmlnode_get_data
(
root
);
if
(
href
)
{
if
(
g_str_equal
(
link_text
,
href
))
{
/* Purple gives us: <a href="URL">URL</a>
* Translate to <a h='URL' />
* Displayed as text of URL with link to URL
*/
*
begin
=
g_strdup_printf
(
"<a h='%s' />"
,
href
);
}
else
{
/* But if we get: <a href="URL">text</a>
* Translate to: text: <a h='URL' />
*
* Because official client only supports self-closed <a>
* tags; you can't change the link text.
*/
*
begin
=
g_strdup_printf
(
"%s: <a h='%s' />"
,
link_text
,
href
);
}
}
else
{
*
begin
=
g_strdup
(
"<a />"
);
}
/* Sorry, kid. MySpace doesn't support you within <a> tags. */
xmlnode_free
(
root
->
child
);
g_free
(
link_text
);
root
->
child
=
NULL
;
*
end
=
g_strdup
(
""
);
}
else
if
(
!
purple_utf8_strcasecmp
(
root
->
name
,
"font"
))
{
GString
*
tmpbegin
,
*
tmpend
;
const
gchar
*
size
;
const
gchar
*
face
;
const
gchar
*
color
;
size
=
xmlnode_get_attrib
(
root
,
"size"
);
face
=
xmlnode_get_attrib
(
root
,
"face"
);
color
=
xmlnode_get_attrib
(
root
,
"color"
);
tmpbegin
=
g_string_new
(
"<f"
);
tmpend
=
g_string_new
(
"</f>"
);
if
(
face
!=
NULL
)
g_string_append_printf
(
tmpbegin
,
" f='%s'"
,
face
);
if
(
size
!=
NULL
)
g_string_append_printf
(
tmpbegin
,
" h='%d'"
,
msim_point_to_height
(
session
,
msim_purple_size_to_point
(
session
,
atoi
(
size
))));
/* Close the <f> tag */
g_string_append
(
tmpbegin
,
">"
);
if
(
color
!=
NULL
)
{
g_string_append_printf
(
tmpbegin
,
"<c v='%s'>"
,
color
);
g_string_prepend
(
tmpend
,
"</c>"
);
}
*
begin
=
g_string_free
(
tmpbegin
,
FALSE
);
*
end
=
g_string_free
(
tmpend
,
FALSE
);
}
else
if
(
!
purple_utf8_strcasecmp
(
root
->
name
,
"body"
))
{
const
gchar
*
bgcolor
;
bgcolor
=
xmlnode_get_attrib
(
root
,
"bgcolor"
);
if
(
bgcolor
!=
NULL
)
{
*
begin
=
g_strdup_printf
(
"<b v='%s'>"
,
bgcolor
);
*
end
=
g_strdup
(
"</b>"
);
}
}
else
{
gchar
*
err
;
#ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS
*
begin
=
g_strdup_printf
(
"[%s]"
,
root
->
name
);
*
end
=
g_strdup_printf
(
"[/%s]"
,
root
->
name
);
#else
*
begin
=
g_strdup
(
""
);
*
end
=
g_strdup
(
""
);
#endif
err
=
g_strdup_printf
(
"html_tag_to_msim_markup: unrecognized "
"HTML tag %s was sent by the IM client; ignoring"
,
root
->
name
?
root
->
name
:
"(NULL)"
);
msim_unrecognized
(
NULL
,
NULL
,
err
);
g_free
(
err
);
}
return
ret
;
}
/**
* Convert an xmlnode of msim markup or HTML to an HTML string or msim markup.
*
* @param f Function to convert tags.
*
* @return An HTML string. Caller frees.
*/
static
void
msim_convert_xmlnode
(
MsimSession
*
session
,
GString
*
out
,
xmlnode
*
root
,
MSIM_XMLNODE_CONVERT
f
,
int
nodes_processed
)
{
xmlnode
*
node
;
gchar
*
begin
,
*
end
,
*
tmp
;
int
descended
=
nodes_processed
;
if
(
!
root
||
!
root
->
name
)
return
;
purple_debug_info
(
"msim"
,
"msim_convert_xmlnode: got root=%s
\n
"
,
root
->
name
);
begin
=
end
=
NULL
;
if
(
descended
==
0
)
/* We've not formatted this yet.. :) */
descended
=
f
(
session
,
root
,
&
begin
,
&
end
);
/* Get the value that our format function has already descended for us */
g_string_append
(
out
,
begin
);
g_free
(
begin
);
/* Loop over all child nodes. */
for
(
node
=
root
->
child
;
node
!=
NULL
;
node
=
node
->
next
)
{
switch
(
node
->
type
)
{
case
XMLNODE_TYPE_ATTRIB
:
/* Attributes handled above. */
break
;
case
XMLNODE_TYPE_TAG
:
/* A tag or tag with attributes. Recursively descend. */
msim_convert_xmlnode
(
session
,
out
,
node
,
f
,
descended
);
purple_debug_info
(
"msim"
,
" ** node name=%s
\n
"
,
node
->
name
?
node
->
name
:
"(NULL)"
);
break
;
case
XMLNODE_TYPE_DATA
:
/* Literal text. */
/*
* TODO: Why is it necessary to escape here? I thought
* node->data was already escaped?
*/
tmp
=
g_markup_escape_text
(
node
->
data
,
node
->
data_sz
);
g_string_append
(
out
,
tmp
);
g_free
(
tmp
);
break
;
default
:
purple_debug_warning
(
"msim"
,
"msim_convert_xmlnode: unknown node type
\n
"
);
}
}
/* TODO: Note that msim counts each piece of text enclosed by <f> as
* a paragraph and will display each on its own line. You actually have
* to _nest_ <f> tags to intersperse different text in one paragraph!
* Comment out this line below to see. */
g_string_append
(
out
,
end
);
g_free
(
end
);
}
/**
* Convert XML to something based on MSIM_XMLNODE_CONVERT.
*/
static
gchar
*
msim_convert_xml
(
MsimSession
*
session
,
const
gchar
*
raw
,
MSIM_XMLNODE_CONVERT
f
)
{
xmlnode
*
root
;
GString
*
str
;
gchar
*
enclosed_raw
;
g_return_val_if_fail
(
raw
!=
NULL
,
NULL
);
/* Enclose text in one root tag, to try to make it valid XML for parsing. */
enclosed_raw
=
g_strconcat
(
"<root>"
,
raw
,
"</root>"
,
NULL
);
root
=
xmlnode_from_str
(
enclosed_raw
,
-1
);
if
(
!
root
)
{
purple_debug_warning
(
"msim"
,
"msim_markup_to_html: couldn't parse "
"%s as XML, returning raw: %s
\n
"
,
enclosed_raw
,
raw
);
/* TODO: msim_unrecognized */
g_free
(
enclosed_raw
);
return
g_strdup
(
raw
);
}
g_free
(
enclosed_raw
);
str
=
g_string_new
(
NULL
);
msim_convert_xmlnode
(
session
,
str
,
root
,
f
,
0
);
xmlnode_free
(
root
);
purple_debug_info
(
"msim"
,
"msim_markup_to_html: returning %s
\n
"
,
str
->
str
);
return
g_string_free
(
str
,
FALSE
);
}
/**
* Convert plaintext smileys to <i> markup tags.
*
* @param before Original text with ASCII smileys. Will be freed.
* @return A new string with <i> tags, if applicable. Must be g_free()'d.
*/
static
gchar
*
msim_convert_smileys_to_markup
(
gchar
*
before
)
{
gchar
*
old
,
*
new
,
*
replacement
;
guint
i
;
struct
MSIM_EMOTICON
*
emote
;
old
=
before
;
new
=
NULL
;
for
(
i
=
0
;
(
emote
=
&
msim_emoticons
[
i
])
&&
emote
->
name
!=
NULL
;
++
i
)
{
gchar
*
name
,
*
symbol
;
name
=
emote
->
name
;
symbol
=
emote
->
symbol
;
replacement
=
g_strdup_printf
(
"<i n=
\"
%s
\"
/>"
,
name
);
purple_debug_info
(
"msim"
,
"msim_convert_smileys_to_markup: %s->%s
\n
"
,
symbol
?
symbol
:
"(NULL)"
,
replacement
?
replacement
:
"(NULL)"
);
new
=
purple_strreplace
(
old
,
symbol
,
replacement
);
g_free
(
replacement
);
g_free
(
old
);
old
=
new
;
}
return
new
;
}
/**
* High-level function to convert MySpaceIM markup to Purple (HTML) markup.
*
* @return Purple markup string, must be g_free()'d. */
gchar
*
msim_markup_to_html
(
MsimSession
*
session
,
const
gchar
*
raw
)
{
return
msim_convert_xml
(
session
,
raw
,
msim_markup_tag_to_html
);
}
/**
* High-level function to convert Purple (HTML) to MySpaceIM markup.
*
* TODO: consider using purple_markup_html_to_xhtml() to make valid XML.
*
* @return HTML markup string, must be g_free()'d. */
gchar
*
html_to_msim_markup
(
MsimSession
*
session
,
const
gchar
*
raw
)
{
gchar
*
markup
;
markup
=
msim_convert_xml
(
session
,
raw
,
html_tag_to_msim_markup
);
if
(
purple_account_get_bool
(
session
->
account
,
"emoticons"
,
TRUE
))
{
/* Frees markup and allocates a new one. */
markup
=
msim_convert_smileys_to_markup
(
markup
);
}
return
markup
;
}