4 * Some code copyright 2003 Tim Ringenbach <omarvo@hotmail.com>
5 * (marv on irc.freenode.net)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #endif /* HAVE_CONFIG_H */
36 yahoo_account_use_http_proxy(PurpleConnection
*pc
)
38 PurpleAccount
*account
= purple_connection_get_account(pc
);
39 PurpleProxyInfo
*ppi
= NULL
;
40 PurpleProxyType type
= PURPLE_PROXY_NONE
;
42 gboolean proxy_ssl
= purple_account_get_bool(account
, "proxy_ssl", FALSE
);
45 ppi
= purple_proxy_get_setup(account
);
47 ppi
= purple_proxy_get_setup(NULL
);
49 ppi
= purple_proxy_get_setup(account
);
51 type
= purple_proxy_info_get_proxy_type(ppi
);
53 return (type
== PURPLE_PROXY_HTTP
|| type
== PURPLE_PROXY_USE_ENVVAR
);
57 * Returns cookies formatted as a null terminated string for the given connection.
58 * Must g_free return value.
60 * TODO:will work, but must test for strict correctness
62 gchar
* yahoo_get_cookies(PurpleConnection
*gc
)
69 YahooData
*yd
= purple_connection_get_protocol_data(gc
);
70 GSList
*cookies
= yd
->cookies
;
77 t2
= g_strrstr(cur
, ";expires=");
79 t2
= g_strrstr(cur
, "; expires=");
83 ans
= g_strdup_printf("%c=%s", cur
[0], cur
+2);
85 ans
= g_strdup_printf("%s; %c=%s", t1
, cur
[0], cur
+2);
89 t3
= strstr(t2
+1, ";");
95 ans
= g_strdup_printf("%c=%s%s", cur
[0], cur
+2, t3
);
97 ans
= g_strdup_printf("%s; %c=%s%s", t1
, cur
[0], cur
+2, t3
);
106 ans
= g_strdup_printf("%c=%s", cur
[0], cur
+2);
108 ans
= g_strdup_printf("%s; %c=%s", t1
, cur
[0], cur
+2);
117 tmp
= g_slist_next(tmp
);
122 char *yahoo_string_encode(PurpleConnection
*gc
, const char *str
, gboolean utf8
)
125 const char *to_codeset
;
126 GError
*error
= NULL
;
128 if (utf8
) /* FIXME: maybe don't use utf8 if it'll fit in latin1 */
129 return g_strdup(str
);
131 to_codeset
= purple_account_get_string(purple_connection_get_account(gc
), "local_charset", "ISO-8859-1");
132 ret
= g_convert_with_fallback(str
, -1, to_codeset
, "UTF-8", "?", NULL
, NULL
, &error
);
135 purple_debug_error("yahoo", "Could not convert %s from UTF-8 to "
136 "%s: %d - %s\n", str
? str
: "(null)", to_codeset
,
138 error
->message
? error
->message
: "(null)");
141 purple_debug_error("yahoo", "Could not convert %s from UTF-8 to "
142 "%s: unkown error\n", str
? str
: "(null)", to_codeset
);
151 * Decode some text received from the server.
153 * @param gc The gc handle.
154 * @param str The null terminated string to decode.
155 * @param utf8 Did the server tell us it was supposed to be utf8?
156 * @return The decoded, utf-8 string, which must be g_free()'d.
158 char *yahoo_string_decode(PurpleConnection
*gc
, const char *str
, gboolean utf8
)
161 const char *from_codeset
;
162 GError
*error
= NULL
;
165 if (g_utf8_validate(str
, -1, NULL
))
166 return g_strdup(str
);
167 purple_debug_warning("yahoo", "Server told us a string was supposed "
168 "to be UTF-8, but it was not. Will try another encoding.\n");
171 from_codeset
= purple_account_get_string(purple_connection_get_account(gc
), "local_charset", "ISO-8859-1");
173 ret
= g_convert_with_fallback(str
, -1, "UTF-8", from_codeset
, NULL
, NULL
, NULL
, &error
);
176 purple_debug_error("yahoo", "Could not convert %s from %s to "
177 "UTF-8: %d - %s\n", str
? str
: "(null)", from_codeset
,
178 error
->code
, error
->message
? error
->message
: "(null)");
181 purple_debug_error("yahoo", "Could not convert %s from %s to "
182 "UTF-8: unkown error\n", str
? str
: "(null)",
191 char *yahoo_convert_to_numeric(const char *str
)
193 GString
*gstr
= NULL
;
194 const unsigned char *p
;
196 gstr
= g_string_sized_new(strlen(str
) * 6 + 1);
198 for (p
= (unsigned char *)str
; *p
; p
++) {
199 g_string_append_printf(gstr
, "&#%u;", *p
);
202 return g_string_free(gstr
, FALSE
);
206 * The values in this hash table should probably be lowercase, since that's
207 * what xhtml expects. Also because yahoo_codes_to_html() does
208 * case-sensitive comparisons.
210 * I found these on some website but i don't know that they actually
211 * work (or are supposed to work). I didn't implement them yet.
216 * [0;38m ---light black
217 * [1;39m ---dark blue
222 * [1;30m ---light blue
226 * (shift+comma)lyellow(shift+period) ---light yellow
227 * (shift+comma)lgreen(shift+period) ---light green
228 * [2;30m <--white out
231 static GHashTable
*esc_codes_ht
= NULL
;
232 static GHashTable
*tags_ht
= NULL
;
234 void yahoo_init_colorht()
236 if (esc_codes_ht
!= NULL
)
237 /* Hash table has already been initialized */
240 /* Key is the escape code string. Value is the HTML that should be
241 * inserted in place of the escape code. */
242 esc_codes_ht
= g_hash_table_new(g_str_hash
, g_str_equal
);
244 /* Key is the name of the HTML tag, for example "font" or "/font"
245 * value is the HTML that should be inserted in place of the old tag */
246 tags_ht
= g_hash_table_new(g_str_hash
, g_str_equal
);
248 /* the numbers in comments are what gyach uses, but i think they're incorrect */
249 #ifdef USE_CSS_FORMATTING
250 g_hash_table_insert(esc_codes_ht
, "30", "<span style=\"color: #000000\">"); /* black */
251 g_hash_table_insert(esc_codes_ht
, "31", "<span style=\"color: #0000FF\">"); /* blue */
252 g_hash_table_insert(esc_codes_ht
, "32", "<span style=\"color: #008080\">"); /* cyan */ /* 00b2b2 */
253 g_hash_table_insert(esc_codes_ht
, "33", "<span style=\"color: #808080\">"); /* gray */ /* 808080 */
254 g_hash_table_insert(esc_codes_ht
, "34", "<span style=\"color: #008000\">"); /* green */ /* 00c200 */
255 g_hash_table_insert(esc_codes_ht
, "35", "<span style=\"color: #FF0080\">"); /* pink */ /* ffafaf */
256 g_hash_table_insert(esc_codes_ht
, "36", "<span style=\"color: #800080\">"); /* purple */ /* b200b2 */
257 g_hash_table_insert(esc_codes_ht
, "37", "<span style=\"color: #FF8000\">"); /* orange */ /* ffff00 */
258 g_hash_table_insert(esc_codes_ht
, "38", "<span style=\"color: #FF0000\">"); /* red */
259 g_hash_table_insert(esc_codes_ht
, "39", "<span style=\"color: #808000\">"); /* olive */ /* 546b50 */
261 g_hash_table_insert(esc_codes_ht
, "30", "<font color=\"#000000\">"); /* black */
262 g_hash_table_insert(esc_codes_ht
, "31", "<font color=\"#0000FF\">"); /* blue */
263 g_hash_table_insert(esc_codes_ht
, "32", "<font color=\"#008080\">"); /* cyan */ /* 00b2b2 */
264 g_hash_table_insert(esc_codes_ht
, "33", "<font color=\"#808080\">"); /* gray */ /* 808080 */
265 g_hash_table_insert(esc_codes_ht
, "34", "<font color=\"#008000\">"); /* green */ /* 00c200 */
266 g_hash_table_insert(esc_codes_ht
, "35", "<font color=\"#FF0080\">"); /* pink */ /* ffafaf */
267 g_hash_table_insert(esc_codes_ht
, "36", "<font color=\"#800080\">"); /* purple */ /* b200b2 */
268 g_hash_table_insert(esc_codes_ht
, "37", "<font color=\"#FF8000\">"); /* orange */ /* ffff00 */
269 g_hash_table_insert(esc_codes_ht
, "38", "<font color=\"#FF0000\">"); /* red */
270 g_hash_table_insert(esc_codes_ht
, "39", "<font color=\"#808000\">"); /* olive */ /* 546b50 */
271 #endif /* !USE_CSS_FORMATTING */
273 g_hash_table_insert(esc_codes_ht
, "1", "<b>");
274 g_hash_table_insert(esc_codes_ht
, "x1", "</b>");
275 g_hash_table_insert(esc_codes_ht
, "2", "<i>");
276 g_hash_table_insert(esc_codes_ht
, "x2", "</i>");
277 g_hash_table_insert(esc_codes_ht
, "4", "<u>");
278 g_hash_table_insert(esc_codes_ht
, "x4", "</u>");
280 /* these just tell us the text they surround is supposed
281 * to be a link. purple figures that out on its own so we
284 g_hash_table_insert(esc_codes_ht
, "l", ""); /* link start */
285 g_hash_table_insert(esc_codes_ht
, "xl", ""); /* link end */
287 #ifdef USE_CSS_FORMATTING
288 g_hash_table_insert(tags_ht
, "black", "<span style=\"color: #000000\">");
289 g_hash_table_insert(tags_ht
, "blue", "<span style=\"color: #0000FF\">");
290 g_hash_table_insert(tags_ht
, "cyan", "<span style=\"color: #008284\">");
291 g_hash_table_insert(tags_ht
, "gray", "<span style=\"color: #848284\">");
292 g_hash_table_insert(tags_ht
, "green", "<span style=\"color: #008200\">");
293 g_hash_table_insert(tags_ht
, "pink", "<span style=\"color: #FF0084\">");
294 g_hash_table_insert(tags_ht
, "purple", "<span style=\"color: #840084\">");
295 g_hash_table_insert(tags_ht
, "orange", "<span style=\"color: #FF8000\">");
296 g_hash_table_insert(tags_ht
, "red", "<span style=\"color: #FF0000\">");
297 g_hash_table_insert(tags_ht
, "yellow", "<span style=\"color: #848200\">");
299 g_hash_table_insert(tags_ht
, "/black", "</span>");
300 g_hash_table_insert(tags_ht
, "/blue", "</span>");
301 g_hash_table_insert(tags_ht
, "/cyan", "</span>");
302 g_hash_table_insert(tags_ht
, "/gray", "</span>");
303 g_hash_table_insert(tags_ht
, "/green", "</span>");
304 g_hash_table_insert(tags_ht
, "/pink", "</span>");
305 g_hash_table_insert(tags_ht
, "/purple", "</span>");
306 g_hash_table_insert(tags_ht
, "/orange", "</span>");
307 g_hash_table_insert(tags_ht
, "/red", "</span>");
308 g_hash_table_insert(tags_ht
, "/yellow", "</span>");
310 g_hash_table_insert(tags_ht
, "black", "<font color=\"#000000\">");
311 g_hash_table_insert(tags_ht
, "blue", "<font color=\"#0000FF\">");
312 g_hash_table_insert(tags_ht
, "cyan", "<font color=\"#008284\">");
313 g_hash_table_insert(tags_ht
, "gray", "<font color=\"#848284\">");
314 g_hash_table_insert(tags_ht
, "green", "<font color=\"#008200\">");
315 g_hash_table_insert(tags_ht
, "pink", "<font color=\"#FF0084\">");
316 g_hash_table_insert(tags_ht
, "purple", "<font color=\"#840084\">");
317 g_hash_table_insert(tags_ht
, "orange", "<font color=\"#FF8000\">");
318 g_hash_table_insert(tags_ht
, "red", "<font color=\"#FF0000\">");
319 g_hash_table_insert(tags_ht
, "yellow", "<font color=\"#848200\">");
321 g_hash_table_insert(tags_ht
, "/black", "</font>");
322 g_hash_table_insert(tags_ht
, "/blue", "</font>");
323 g_hash_table_insert(tags_ht
, "/cyan", "</font>");
324 g_hash_table_insert(tags_ht
, "/gray", "</font>");
325 g_hash_table_insert(tags_ht
, "/green", "</font>");
326 g_hash_table_insert(tags_ht
, "/pink", "</font>");
327 g_hash_table_insert(tags_ht
, "/purple", "</font>");
328 g_hash_table_insert(tags_ht
, "/orange", "</font>");
329 g_hash_table_insert(tags_ht
, "/red", "</font>");
330 g_hash_table_insert(tags_ht
, "/yellow", "</font>");
331 #endif /* !USE_CSS_FORMATTING */
333 /* We don't support these tags, so discard them */
334 g_hash_table_insert(tags_ht
, "alt", "");
335 g_hash_table_insert(tags_ht
, "fade", "");
336 g_hash_table_insert(tags_ht
, "snd", "");
337 g_hash_table_insert(tags_ht
, "/alt", "");
338 g_hash_table_insert(tags_ht
, "/fade", "");
340 /* Official clients don't seem to send b, i or u tags. They use
341 * the escape codes listed above. Official clients definitely send
342 * font tags, though. I wonder if we can remove the opening and
343 * closing b, i and u tags from here? */
344 g_hash_table_insert(tags_ht
, "b", "<b>");
345 g_hash_table_insert(tags_ht
, "i", "<i>");
346 g_hash_table_insert(tags_ht
, "u", "<u>");
347 g_hash_table_insert(tags_ht
, "font", "<font>");
349 g_hash_table_insert(tags_ht
, "/b", "</b>");
350 g_hash_table_insert(tags_ht
, "/i", "</i>");
351 g_hash_table_insert(tags_ht
, "/u", "</u>");
352 g_hash_table_insert(tags_ht
, "/font", "</font>");
355 void yahoo_dest_colorht()
357 if (esc_codes_ht
== NULL
)
358 /* Hash table has already been destroyed */
361 g_hash_table_destroy(esc_codes_ht
);
363 g_hash_table_destroy(tags_ht
);
367 #ifndef USE_CSS_FORMATTING
368 static int point_to_html(int x
)
384 #endif /* !USE_CSS_FORMATTING */
386 static void append_attrs_datalist_foreach_cb(GQuark key_id
, gpointer data
, gpointer user_data
)
392 key
= g_quark_to_string(key_id
);
396 purple_xmlnode_set_attrib(cur
, key
, value
);
400 * @param cur A pointer to the position in the XML tree that we're
401 * currently building. This will be modified when opening a tag
402 * or closing an existing tag.
404 static void yahoo_codes_to_html_add_tag(PurpleXmlNode
**cur
, const char *tag
, gboolean is_closing_tag
, const gchar
*tag_name
, gboolean is_font_tag
)
406 if (is_closing_tag
) {
408 GSList
*dangling_tags
= NULL
;
410 /* Move up the DOM until we find the opening tag */
411 for (tmp
= *cur
; tmp
!= NULL
; tmp
= purple_xmlnode_get_parent(tmp
)) {
412 /* Add one to tag_name when doing this comparison because it starts with a / */
413 if (g_str_equal(tmp
->name
, tag_name
+ 1))
416 dangling_tags
= g_slist_prepend(dangling_tags
, tmp
);
419 /* This is a closing tag with no opening tag. Useless. */
420 purple_debug_error("yahoo", "Ignoring unmatched tag %s", tag
);
421 g_slist_free(dangling_tags
);
425 /* Move our current position up, now that we've closed a tag */
426 *cur
= purple_xmlnode_get_parent(tmp
);
428 /* Re-open any tags that were nested below the tag we just closed */
429 while (dangling_tags
!= NULL
) {
430 tmp
= dangling_tags
->data
;
431 dangling_tags
= g_slist_delete_link(dangling_tags
, dangling_tags
);
433 /* Create a copy of this tag+attributes (but not child tags or
434 * data) at our new location */
435 *cur
= purple_xmlnode_new_child(*cur
, tmp
->name
);
436 for (tmp
= tmp
->child
; tmp
!= NULL
; tmp
= tmp
->next
)
437 if (tmp
->type
== PURPLE_XMLNODE_TYPE_ATTRIB
)
438 purple_xmlnode_set_attrib_full(*cur
, tmp
->name
,
439 tmp
->xmlns
, tmp
->prefix
, tmp
->data
);
445 char *fontsize
= NULL
;
447 if (!purple_markup_find_tag(tag_name
, tag
, &start
, &end
, &attributes
))
448 g_return_if_reached();
449 *cur
= purple_xmlnode_new_child(*cur
, tag_name
);
452 /* Special case for the font size attribute */
453 fontsize
= g_strdup(g_datalist_get_data(&attributes
, "size"));
454 if (fontsize
!= NULL
)
455 g_datalist_remove_data(&attributes
, "size");
458 /* Add all font tag attributes */
459 g_datalist_foreach(&attributes
, append_attrs_datalist_foreach_cb
, *cur
);
460 g_datalist_clear(&attributes
);
462 if (fontsize
!= NULL
) {
463 #ifdef USE_CSS_FORMATTING
465 * The Yahoo font size value is given in pt, even though the HTML
466 * standard for <font size="x"> treats the size as a number on a
467 * scale between 1 and 7. So we insert the font size as a CSS
468 * style on a span tag.
470 gchar
*tmp
= g_strdup_printf("font-size: %spt", fontsize
);
471 *cur
= purple_xmlnode_new_child(*cur
, "span");
472 purple_xmlnode_set_attrib(*cur
, "style", tmp
);
476 * The Yahoo font size value is given in pt, even though the HTML
477 * standard for <font size="x"> treats the size as a number on a
478 * scale between 1 and 7. So we convert it to an appropriate
479 * value. This loses precision, which is why CSS formatting is
480 * preferred. The "absz" attribute remains here for backward
481 * compatibility with UIs that might use it, but it is totally
482 * not standard at all.
486 size
= strtol(fontsize
, NULL
, 10);
487 htmlsize
= point_to_html(size
);
488 sprintf(tmp
, "%u", htmlsize
);
489 purple_xmlnode_set_attrib(*cur
, "size", tmp
);
490 purple_xmlnode_set_attrib(*cur
, "absz", fontsize
);
491 #endif /* !USE_CSS_FORMATTING */
498 * Similar to purple_markup_get_tag_name(), but works with closing tags.
500 * @return The lowercase name of the tag. If this is a closing tag then
501 * this value starts with a forward slash. The caller must free
502 * this string with g_free.
504 static gchar
*yahoo_markup_get_tag_name(const char *tag
, gboolean
*is_closing_tag
)
508 *is_closing_tag
= (tag
[1] == '/');
510 len
= strcspn(tag
+ 1, "> ");
512 len
= strcspn(tag
+ 1, "> /");
514 return g_utf8_strdown(tag
+ 1, len
);
518 * Yahoo! messages generally aren't well-formed. Their markup is
519 * more of a flow from start to finish rather than a hierarchy from
520 * outer to inner. They tend to open tags and close them only when
523 * Example: <font size="8">size 8 <font size="16">size 16 <font size="8">size 8 again
525 * But we want to send well-formed HTML to the core, so we step through
526 * the input string and build an PurpleXmlNode tree containing sanitized HTML.
528 char *yahoo_codes_to_html(const char *x
)
531 PurpleXmlNode
*html
, *cur
;
532 GString
*cdata
= g_string_new(NULL
);
534 gboolean no_more_gt_brackets
= FALSE
;
536 gchar
*xmlstr1
, *xmlstr2
, *esc
;
539 html
= purple_xmlnode_new("html");
542 for (i
= 0; i
< x_len
; i
++) {
543 if ((x
[i
] == 0x1b) && (x
[i
+1] == '[')) {
544 /* This escape sequence signifies the beginning of some
545 * text formatting code */
548 while (j
++ < x_len
) {
552 /* Keep looking for the end of this sequence */
555 /* We've reached the end of the formatting sequence, yay */
557 /* Append any character data that belongs in the current node */
558 if (cdata
->len
> 0) {
559 purple_xmlnode_insert_data(cur
, cdata
->str
, cdata
->len
);
560 g_string_truncate(cdata
, 0);
563 code
= g_strndup(x
+ i
+ 2, j
- i
- 2);
564 if (code
[0] == '#') {
565 #ifdef USE_CSS_FORMATTING
566 gchar
*tmp
= g_strdup_printf("color: %s", code
);
567 cur
= purple_xmlnode_new_child(cur
, "span");
568 purple_xmlnode_set_attrib(cur
, "style", tmp
);
571 cur
= purple_xmlnode_new_child(cur
, "font");
572 purple_xmlnode_set_attrib(cur
, "color", code
);
573 #endif /* !USE_CSS_FORMATTING */
575 } else if ((match
= g_hash_table_lookup(esc_codes_ht
, code
))) {
576 /* Some tags are in the hash table only because we
577 * want to ignore them */
578 if (match
[0] != '\0') {
579 gboolean is_closing_tag
;
581 tag_name
= yahoo_markup_get_tag_name(match
, &is_closing_tag
);
582 yahoo_codes_to_html_add_tag(&cur
, match
, is_closing_tag
, tag_name
, FALSE
);
587 purple_debug_error("yahoo",
588 "Ignoring unknown ansi code 'ESC[%sm'.\n", code
);
596 } else if (x
[i
] == '<' && !no_more_gt_brackets
) {
597 /* The start of an HTML tag */
600 while (j
++ < x_len
) {
602 gboolean is_closing_tag
;
607 /* We're inside a quoted attribute value. Skip to the end */
609 while (j
!= x_len
&& x
[j
] != '"')
611 } else if (x
[j
] == '\'') {
612 /* We're inside a quoted attribute value. Skip to the end */
614 while (j
!= x_len
&& x
[j
] != '\'')
618 /* Keep looking for the end of this tag */
621 /* This < has no corresponding > */
622 g_string_append_c(cdata
, x
[i
]);
623 no_more_gt_brackets
= TRUE
;
627 tag
= g_strndup(x
+ i
, j
- i
+ 1);
628 tag_name
= yahoo_markup_get_tag_name(tag
, &is_closing_tag
);
630 match
= g_hash_table_lookup(tags_ht
, tag_name
);
632 /* Unknown tag. The user probably typed a less-than sign */
633 g_string_append_c(cdata
, x
[i
]);
639 /* Some tags are in the hash table only because we
640 * want to ignore them */
641 if (match
[0] != '\0') {
642 /* Append any character data that belongs in the current node */
643 if (cdata
->len
> 0) {
644 purple_xmlnode_insert_data(cur
, cdata
->str
, cdata
->len
);
645 g_string_truncate(cdata
, 0);
647 if (g_str_equal(tag_name
, "font"))
648 /* Font tags are a special case. We don't
649 * necessarily want to replace the whole thing--
650 * we just want to fix the size attribute. */
651 yahoo_codes_to_html_add_tag(&cur
, tag
, is_closing_tag
, tag_name
, TRUE
);
653 yahoo_codes_to_html_add_tag(&cur
, match
, is_closing_tag
, tag_name
, FALSE
);
663 g_string_append_c(cdata
, x
[i
]);
667 /* Append any remaining character data */
669 purple_xmlnode_insert_data(cur
, cdata
->str
, cdata
->len
);
670 g_string_free(cdata
, TRUE
);
672 /* Serialize our HTML */
673 xmlstr1
= purple_xmlnode_to_str(html
, NULL
);
674 purple_xmlnode_free(html
);
676 /* Strip off the outter HTML node */
677 /* This probably isn't necessary, especially if we made the outter HTML
678 * node an empty span. But the HTML is simpler this way. */
679 if (!purple_strequal(xmlstr1
, "<html/>"))
680 xmlstr2
= g_strndup(xmlstr1
+ 6, strlen(xmlstr1
) - 13);
682 xmlstr2
= g_strdup("");
685 esc
= g_strescape(x
, NULL
);
686 purple_debug_misc("yahoo", "yahoo_codes_to_html(%s)=%s\n", esc
, xmlstr2
);
692 /* borrowed from gtkimhtml */
693 #define MAX_FONT_SIZE 7
694 #define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1])
695 static const gint _point_sizes
[] = { 8, 10, 12, 14, 20, 30, 40 };
708 static void yahoo_htc_list_cleanup(GSList
*l
)
712 l
= g_slist_delete_link(l
, l
);
716 static void parse_font_tag(GString
*dest
, const char *tag_name
, const char *tag
,
717 GSList
**colors
, GSList
**tags
)
722 const char *attribute
;
726 if (!purple_markup_find_tag(tag_name
, tag
, &start
, &end
, &attributes
))
727 g_return_if_reached();
730 tmp
= g_string_new(NULL
);
732 attribute
= g_datalist_get_data(&attributes
, "color");
733 if (attribute
!= NULL
) {
734 g_string_append(tmp
, *colors
? (*colors
)->data
: "\033[#000000m");
735 g_string_append_printf(dest
, "\033[%sm", attribute
);
736 *colors
= g_slist_prepend(*colors
,
737 g_strdup_printf("\033[%sm", attribute
));
739 /* We need to add a value to the colors stack even if we're not
740 * setting a color because we ALWAYS pop exactly 1 element from
741 * this stack for every </font> tag. If we don't add anything
742 * then we'll pop something that we shouldn't when we hit this
743 * corresponding </font>. */
744 *colors
= g_slist_prepend(*colors
,
745 *colors
? g_strdup((*colors
)->data
) : g_strdup("\033[#000000m"));
748 attribute
= g_datalist_get_data(&attributes
, "face");
749 if (attribute
!= NULL
) {
751 g_string_append(dest
, "<font ");
752 g_string_append_printf(dest
, "face=\"%s\" ", attribute
);
755 attribute
= g_datalist_get_data(&attributes
, "size");
756 if (attribute
!= NULL
) {
759 g_string_append(dest
, "<font ");
762 g_string_append_printf(dest
, "size=\"%d\" ",
763 POINT_SIZE(strtol(attribute
, NULL
, 10)));
767 dest
->str
[dest
->len
-1] = '>';
768 *tags
= g_slist_prepend(*tags
, g_strdup("</font>"));
769 g_string_free(tmp
, TRUE
);
771 *tags
= g_slist_prepend(*tags
, tmp
->str
);
772 g_string_free(tmp
, FALSE
);
775 g_datalist_clear(&attributes
);
778 char *yahoo_html_to_codes(const char *src
)
780 GSList
*colors
= NULL
;
783 * A stack of char*s where each char* is the string that should be
784 * appended to dest in order to close all the tags that were opened
793 gboolean no_more_gt_brackets
= FALSE
;
794 gchar
*tag
, *tag_name
;
795 gboolean is_closing_tag
;
796 CurrentMsgState current_state
;
798 memset(¤t_state
, 0, sizeof(current_state
));
800 src_len
= strlen(src
);
801 dest
= g_string_sized_new(src_len
);
803 for (i
= 0; i
< src_len
; i
++) {
804 if (src
[i
] == '<' && !no_more_gt_brackets
) {
805 /* The start of an HTML tag */
808 while (j
++ < src_len
) {
811 /* We're inside a quoted attribute value. Skip to the end */
813 while (j
!= src_len
&& src
[j
] != '"')
815 } else if (src
[j
] == '\'') {
816 /* We're inside a quoted attribute value. Skip to the end */
818 while (j
!= src_len
&& src
[j
] != '\'')
822 /* Keep looking for the end of this tag */
825 /* This < has no corresponding > */
826 g_string_append_c(dest
, src
[i
]);
827 no_more_gt_brackets
= TRUE
;
831 tag
= g_strndup(src
+ i
, j
- i
+ 1);
832 tag_name
= yahoo_markup_get_tag_name(tag
, &is_closing_tag
);
834 if (g_str_equal(tag_name
, "a")) {
838 const char *attribute
;
841 * TODO: Ideally we would replace this:
842 * <a href="https://pidgin.im/">Pidgin</a>
844 * Pidgin (https://pidgin.im/)
846 * Currently we drop the text within the <a> tag and
847 * just show the URL. Doing it the fancy way is
848 * complicated when dealing with HTML tags within the
853 if (!purple_markup_find_tag(tag_name
,
854 tag
, &start
, &end
, &attributes
))
863 attribute
= g_datalist_get_data(&attributes
, "href");
864 if (attribute
!= NULL
) {
865 if (purple_str_has_prefix(attribute
, "mailto:"))
867 g_string_append(dest
, attribute
);
869 g_datalist_clear(&attributes
);
871 /* Skip past the closing </a> tag */
872 end
= purple_strcasestr(src
+ j
, "</a>");
876 } else if (g_str_equal(tag_name
, "font")) {
877 parse_font_tag(dest
, tag_name
, tag
, &colors
, &tags
);
878 } else if (g_str_equal(tag_name
, "b")) {
879 g_string_append(dest
, "\033[1m");
880 current_state
.bold
= TRUE
;
881 } else if (g_str_equal(tag_name
, "/b")) {
882 if (current_state
.bold
) {
883 g_string_append(dest
, "\033[x1m");
884 current_state
.bold
= FALSE
;
886 } else if (g_str_equal(tag_name
, "i")) {
887 current_state
.italic
= TRUE
;
888 g_string_append(dest
, "\033[2m");
889 } else if (g_str_equal(tag_name
, "/i")) {
890 if (current_state
.italic
) {
891 g_string_append(dest
, "\033[x2m");
892 current_state
.italic
= FALSE
;
894 } else if (g_str_equal(tag_name
, "u")) {
895 current_state
.underline
= TRUE
;
896 g_string_append(dest
, "\033[4m");
897 } else if (g_str_equal(tag_name
, "/u")) {
898 if (current_state
.underline
) {
899 g_string_append(dest
, "\033[x4m");
900 current_state
.underline
= FALSE
;
902 } else if (g_str_equal(tag_name
, "/a")) {
904 } else if (g_str_equal(tag_name
, "br")) {
905 g_string_append_c(dest
, '\n');
906 } else if (g_str_equal(tag_name
, "/font")) {
908 char *etag
= tags
->data
;
909 tags
= g_slist_delete_link(tags
, tags
);
910 g_string_append(dest
, etag
);
911 if (colors
!= NULL
) {
912 g_free(colors
->data
);
913 colors
= g_slist_delete_link(colors
, colors
);
917 } else if (g_str_equal(tag_name
, "span") || g_str_equal(tag_name
, "/span")) {
920 /* We don't know what the tag is. Send it unmodified. */
921 g_string_append(dest
, tag
);
934 entity
= purple_markup_unescape_entity(src
+ i
, &length
);
935 if (entity
!= NULL
) {
936 /* src[i] is the start of an HTML entity */
937 g_string_append(dest
, entity
);
940 /* src[i] is a normal character */
941 g_string_append_c(dest
, src
[i
]);
945 esc
= g_strescape(dest
->str
, NULL
);
946 purple_debug_misc("yahoo", "yahoo_html_to_codes(%s)=%s\n", src
, esc
);
949 yahoo_htc_list_cleanup(colors
);
950 yahoo_htc_list_cleanup(tags
);
952 return g_string_free(dest
, FALSE
);