| 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
| 18 */ |
18 */ |
| 19 |
19 |
| 20 #include "myspace.h" |
20 #include "myspace.h" |
| 21 |
21 |
| 22 typedef int (*MSIM_XMLNODE_CONVERT)(MsimSession *, xmlnode *, gchar **, gchar **); |
22 typedef int (*MSIM_XMLNODE_CONVERT)(MsimSession *, PurpleXmlNode *, gchar **, gchar **); |
| 23 |
23 |
| 24 /* Globals */ |
24 /* Globals */ |
| 25 |
25 |
| 26 /* The names in in emoticon_names (for <i n=whatever>) map to corresponding |
26 /* The names in in emoticon_names (for <i n=whatever>) map to corresponding |
| 27 * entries in emoticon_symbols (for the ASCII representation of the emoticon). |
27 * entries in emoticon_symbols (for the ASCII representation of the emoticon). |
| 190 |
190 |
| 191 /** |
191 /** |
| 192 * Convert the msim markup <f> (font) tag into HTML. |
192 * Convert the msim markup <f> (font) tag into HTML. |
| 193 */ |
193 */ |
| 194 static void |
194 static void |
| 195 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
195 msim_markup_f_to_html(MsimSession *session, PurpleXmlNode *root, gchar **begin, gchar **end) |
| 196 { |
196 { |
| 197 const gchar *face, *height_str, *decor_str; |
197 const gchar *face, *height_str, *decor_str; |
| 198 GString *gs_end, *gs_begin; |
198 GString *gs_end, *gs_begin; |
| 199 guint decor, height; |
199 guint decor, height; |
| 200 |
200 |
| 201 face = xmlnode_get_attrib(root, "f"); |
201 face = purple_xmlnode_get_attrib(root, "f"); |
| 202 height_str = xmlnode_get_attrib(root, "h"); |
202 height_str = purple_xmlnode_get_attrib(root, "h"); |
| 203 decor_str = xmlnode_get_attrib(root, "s"); |
203 decor_str = purple_xmlnode_get_attrib(root, "s"); |
| 204 |
204 |
| 205 /* Validate the font face, to avoid constructing invalid HTML later */ |
205 /* Validate the font face, to avoid constructing invalid HTML later */ |
| 206 if (face != NULL && strchr(face, '\'') != NULL) |
206 if (face != NULL && strchr(face, '\'') != NULL) |
| 207 face = NULL; |
207 face = NULL; |
| 208 |
208 |
| 281 |
281 |
| 282 /** |
282 /** |
| 283 * Convert the msim markup <a> (anchor) tag into HTML. |
283 * Convert the msim markup <a> (anchor) tag into HTML. |
| 284 */ |
284 */ |
| 285 static void |
285 static void |
| 286 msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
286 msim_markup_a_to_html(MsimSession *session, PurpleXmlNode *root, gchar **begin, gchar **end) |
| 287 { |
287 { |
| 288 const gchar *href; |
288 const gchar *href; |
| 289 |
289 |
| 290 href = xmlnode_get_attrib(root, "h"); |
290 href = purple_xmlnode_get_attrib(root, "h"); |
| 291 if (!href) { |
291 if (!href) { |
| 292 href = ""; |
292 href = ""; |
| 293 } |
293 } |
| 294 |
294 |
| 295 *begin = g_strdup_printf("<a href=\"%s\">%s", href, href); |
295 *begin = g_strdup_printf("<a href=\"%s\">%s", href, href); |
| 298 |
298 |
| 299 /** |
299 /** |
| 300 * Convert the msim markup <p> (paragraph) tag into HTML. |
300 * Convert the msim markup <p> (paragraph) tag into HTML. |
| 301 */ |
301 */ |
| 302 static void |
302 static void |
| 303 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
303 msim_markup_p_to_html(MsimSession *session, PurpleXmlNode *root, gchar **begin, gchar **end) |
| 304 { |
304 { |
| 305 /* Just pass through unchanged. |
305 /* Just pass through unchanged. |
| 306 * |
306 * |
| 307 * Note: attributes currently aren't passed, if there are any. */ |
307 * Note: attributes currently aren't passed, if there are any. */ |
| 308 *begin = g_strdup("<p>"); |
308 *begin = g_strdup("<p>"); |
| 311 |
311 |
| 312 /** |
312 /** |
| 313 * Convert the msim markup <c> tag (text color) into HTML. |
313 * Convert the msim markup <c> tag (text color) into HTML. |
| 314 */ |
314 */ |
| 315 static void |
315 static void |
| 316 msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
316 msim_markup_c_to_html(MsimSession *session, PurpleXmlNode *root, gchar **begin, gchar **end) |
| 317 { |
317 { |
| 318 const gchar *color; |
318 const gchar *color; |
| 319 gchar *purple_color; |
319 gchar *purple_color; |
| 320 |
320 |
| 321 color = xmlnode_get_attrib(root, "v"); |
321 color = purple_xmlnode_get_attrib(root, "v"); |
| 322 if (!color) { |
322 if (!color) { |
| 323 purple_debug_info("msim", "msim_markup_c_to_html: <c> tag w/o v attr\n"); |
323 purple_debug_info("msim", "msim_markup_c_to_html: <c> tag w/o v attr\n"); |
| 324 *begin = g_strdup(""); |
324 *begin = g_strdup(""); |
| 325 *end = g_strdup(""); |
325 *end = g_strdup(""); |
| 326 /* TODO: log as unrecognized */ |
326 /* TODO: log as unrecognized */ |
| 342 |
342 |
| 343 /** |
343 /** |
| 344 * Convert the msim markup <b> tag (background color) into HTML. |
344 * Convert the msim markup <b> tag (background color) into HTML. |
| 345 */ |
345 */ |
| 346 static void |
346 static void |
| 347 msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
347 msim_markup_b_to_html(MsimSession *session, PurpleXmlNode *root, gchar **begin, gchar **end) |
| 348 { |
348 { |
| 349 const gchar *color; |
349 const gchar *color; |
| 350 gchar *purple_color; |
350 gchar *purple_color; |
| 351 |
351 |
| 352 color = xmlnode_get_attrib(root, "v"); |
352 color = purple_xmlnode_get_attrib(root, "v"); |
| 353 if (!color) { |
353 if (!color) { |
| 354 *begin = g_strdup(""); |
354 *begin = g_strdup(""); |
| 355 *end = g_strdup(""); |
355 *end = g_strdup(""); |
| 356 purple_debug_info("msim", "msim_markup_b_to_html: <b> w/o v attr\n"); |
356 purple_debug_info("msim", "msim_markup_b_to_html: <b> w/o v attr\n"); |
| 357 /* TODO: log as unrecognized. */ |
357 /* TODO: log as unrecognized. */ |
| 373 |
373 |
| 374 /** |
374 /** |
| 375 * Convert the msim markup <i> tag (emoticon image) into HTML. |
375 * Convert the msim markup <i> tag (emoticon image) into HTML. |
| 376 */ |
376 */ |
| 377 static void |
377 static void |
| 378 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) |
378 msim_markup_i_to_html(MsimSession *session, PurpleXmlNode *root, gchar **begin, gchar **end) |
| 379 { |
379 { |
| 380 const gchar *name; |
380 const gchar *name; |
| 381 guint i; |
381 guint i; |
| 382 struct MSIM_EMOTICON *emote; |
382 struct MSIM_EMOTICON *emote; |
| 383 |
383 |
| 384 name = xmlnode_get_attrib(root, "n"); |
384 name = purple_xmlnode_get_attrib(root, "n"); |
| 385 if (!name) { |
385 if (!name) { |
| 386 purple_debug_info("msim", "msim_markup_i_to_html: <i> w/o n\n"); |
386 purple_debug_info("msim", "msim_markup_i_to_html: <i> w/o n\n"); |
| 387 *begin = g_strdup(""); |
387 *begin = g_strdup(""); |
| 388 *end = g_strdup(""); |
388 *end = g_strdup(""); |
| 389 /* TODO: log as unrecognized */ |
389 /* TODO: log as unrecognized */ |
| 406 |
406 |
| 407 /** |
407 /** |
| 408 * Convert an individual msim markup tag to HTML. |
408 * Convert an individual msim markup tag to HTML. |
| 409 */ |
409 */ |
| 410 static int |
410 static int |
| 411 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, |
411 msim_markup_tag_to_html(MsimSession *session, PurpleXmlNode *root, gchar **begin, |
| 412 gchar **end) |
412 gchar **end) |
| 413 { |
413 { |
| 414 g_return_val_if_fail(root != NULL, 0); |
414 g_return_val_if_fail(root != NULL, 0); |
| 415 |
415 |
| 416 if (g_str_equal(root->name, "f")) { |
416 if (g_str_equal(root->name, "f")) { |
| 437 |
437 |
| 438 /** |
438 /** |
| 439 * Convert an individual HTML tag to msim markup. |
439 * Convert an individual HTML tag to msim markup. |
| 440 */ |
440 */ |
| 441 static int |
441 static int |
| 442 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, |
442 html_tag_to_msim_markup(MsimSession *session, PurpleXmlNode *root, gchar **begin, |
| 443 gchar **end) |
443 gchar **end) |
| 444 { |
444 { |
| 445 int ret = 0; |
445 int ret = 0; |
| 446 |
446 |
| 447 if (!purple_utf8_strcasecmp(root->name, "root") || |
447 if (!purple_utf8_strcasecmp(root->name, "root") || |
| 451 /* TODO: Coalesce nested tags into one <f> tag! |
451 /* TODO: Coalesce nested tags into one <f> tag! |
| 452 * Currently, the 's' value will be overwritten when b/i/u is nested |
452 * Currently, the 's' value will be overwritten when b/i/u is nested |
| 453 * within another one, and only the inner-most formatting will be |
453 * within another one, and only the inner-most formatting will be |
| 454 * applied to the text. */ |
454 * applied to the text. */ |
| 455 } else if (!purple_utf8_strcasecmp(root->name, "b")) { |
455 } else if (!purple_utf8_strcasecmp(root->name, "b")) { |
| 456 if (root->child->type == XMLNODE_TYPE_DATA) { |
456 if (root->child->type == PURPLE_XMLNODE_TYPE_DATA) { |
| 457 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); |
457 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); |
| 458 *end = g_strdup("</f>"); |
458 *end = g_strdup("</f>"); |
| 459 } else { |
459 } else { |
| 460 if (!purple_utf8_strcasecmp(root->child->name,"i")) { |
460 if (!purple_utf8_strcasecmp(root->child->name,"i")) { |
| 461 ret++; |
461 ret++; |
| 462 if (root->child->child->type == XMLNODE_TYPE_DATA) { |
462 if (root->child->child->type == PURPLE_XMLNODE_TYPE_DATA) { |
| 463 *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_ITALIC)); |
463 *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_ITALIC)); |
| 464 *end = g_strdup("</f>"); |
464 *end = g_strdup("</f>"); |
| 465 } else { |
465 } else { |
| 466 if (!purple_utf8_strcasecmp(root->child->child->name,"u")) { |
466 if (!purple_utf8_strcasecmp(root->child->child->name,"u")) { |
| 467 ret++; |
467 ret++; |
| 474 *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_UNDERLINE)); |
474 *begin = g_strdup_printf("<f s='%d'>", (MSIM_TEXT_BOLD + MSIM_TEXT_UNDERLINE)); |
| 475 *end = g_strdup("</f>"); |
475 *end = g_strdup("</f>"); |
| 476 } |
476 } |
| 477 } |
477 } |
| 478 } else if (!purple_utf8_strcasecmp(root->name, "i")) { |
478 } else if (!purple_utf8_strcasecmp(root->name, "i")) { |
| 479 if (root->child->type == XMLNODE_TYPE_DATA) { |
479 if (root->child->type == PURPLE_XMLNODE_TYPE_DATA) { |
| 480 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC); |
480 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC); |
| 481 *end = g_strdup("</f>"); |
481 *end = g_strdup("</f>"); |
| 482 } else { |
482 } else { |
| 483 if (!purple_utf8_strcasecmp(root->child->name,"u")) { |
483 if (!purple_utf8_strcasecmp(root->child->name,"u")) { |
| 484 ret++; |
484 ret++; |
| 491 *end = g_strdup("</f>"); |
491 *end = g_strdup("</f>"); |
| 492 } else if (!purple_utf8_strcasecmp(root->name, "a")) { |
492 } else if (!purple_utf8_strcasecmp(root->name, "a")) { |
| 493 const gchar *href; |
493 const gchar *href; |
| 494 gchar *link_text; |
494 gchar *link_text; |
| 495 |
495 |
| 496 href = xmlnode_get_attrib(root, "href"); |
496 href = purple_xmlnode_get_attrib(root, "href"); |
| 497 |
497 |
| 498 if (!href) { |
498 if (!href) { |
| 499 href = xmlnode_get_attrib(root, "HREF"); |
499 href = purple_xmlnode_get_attrib(root, "HREF"); |
| 500 } |
500 } |
| 501 |
501 |
| 502 link_text = xmlnode_get_data(root); |
502 link_text = purple_xmlnode_get_data(root); |
| 503 |
503 |
| 504 if (href) { |
504 if (href) { |
| 505 if (g_str_equal(link_text, href)) { |
505 if (g_str_equal(link_text, href)) { |
| 506 /* Purple gives us: <a href="URL">URL</a> |
506 /* Purple gives us: <a href="URL">URL</a> |
| 507 * Translate to <a h='URL' /> |
507 * Translate to <a h='URL' /> |
| 520 } else { |
520 } else { |
| 521 *begin = g_strdup("<a />"); |
521 *begin = g_strdup("<a />"); |
| 522 } |
522 } |
| 523 |
523 |
| 524 /* Sorry, kid. MySpace doesn't support you within <a> tags. */ |
524 /* Sorry, kid. MySpace doesn't support you within <a> tags. */ |
| 525 xmlnode_free(root->child); |
525 purple_xmlnode_free(root->child); |
| 526 g_free(link_text); |
526 g_free(link_text); |
| 527 root->child = NULL; |
527 root->child = NULL; |
| 528 |
528 |
| 529 *end = g_strdup(""); |
529 *end = g_strdup(""); |
| 530 } else if (!purple_utf8_strcasecmp(root->name, "font")) { |
530 } else if (!purple_utf8_strcasecmp(root->name, "font")) { |
| 531 GString *tmpbegin, *tmpend; |
531 GString *tmpbegin, *tmpend; |
| 532 const gchar *size; |
532 const gchar *size; |
| 533 const gchar *face; |
533 const gchar *face; |
| 534 const gchar *color; |
534 const gchar *color; |
| 535 |
535 |
| 536 size = xmlnode_get_attrib(root, "size"); |
536 size = purple_xmlnode_get_attrib(root, "size"); |
| 537 face = xmlnode_get_attrib(root, "face"); |
537 face = purple_xmlnode_get_attrib(root, "face"); |
| 538 color = xmlnode_get_attrib(root, "color"); |
538 color = purple_xmlnode_get_attrib(root, "color"); |
| 539 |
539 |
| 540 tmpbegin = g_string_new("<f"); |
540 tmpbegin = g_string_new("<f"); |
| 541 tmpend = g_string_new("</f>"); |
541 tmpend = g_string_new("</f>"); |
| 542 |
542 |
| 543 if (face != NULL) |
543 if (face != NULL) |
| 560 *end = g_string_free(tmpend, FALSE); |
560 *end = g_string_free(tmpend, FALSE); |
| 561 |
561 |
| 562 } else if (!purple_utf8_strcasecmp(root->name, "body")) { |
562 } else if (!purple_utf8_strcasecmp(root->name, "body")) { |
| 563 const gchar *bgcolor; |
563 const gchar *bgcolor; |
| 564 |
564 |
| 565 bgcolor = xmlnode_get_attrib(root, "bgcolor"); |
565 bgcolor = purple_xmlnode_get_attrib(root, "bgcolor"); |
| 566 |
566 |
| 567 if (bgcolor != NULL) { |
567 if (bgcolor != NULL) { |
| 568 *begin = g_strdup_printf("<b v='%s'>", bgcolor); |
568 *begin = g_strdup_printf("<b v='%s'>", bgcolor); |
| 569 *end = g_strdup("</b>"); |
569 *end = g_strdup("</b>"); |
| 570 } |
570 } |
| 588 } |
588 } |
| 589 return ret; |
589 return ret; |
| 590 } |
590 } |
| 591 |
591 |
| 592 /** |
592 /** |
| 593 * Convert an xmlnode of msim markup or HTML to an HTML string or msim markup. |
593 * Convert an PurpleXmlNode of msim markup or HTML to an HTML string or msim markup. |
| 594 * |
594 * |
| 595 * @param f Function to convert tags. |
595 * @param f Function to convert tags. |
| 596 * |
596 * |
| 597 * @return An HTML string. Caller frees. |
597 * @return An HTML string. Caller frees. |
| 598 */ |
598 */ |
| 599 static void |
599 static void |
| 600 msim_convert_xmlnode(MsimSession *session, GString *out, xmlnode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed) |
600 msim_convert_xmlnode(MsimSession *session, GString *out, PurpleXmlNode *root, MSIM_XMLNODE_CONVERT f, int nodes_processed) |
| 601 { |
601 { |
| 602 xmlnode *node; |
602 PurpleXmlNode *node; |
| 603 gchar *begin, *end, *tmp; |
603 gchar *begin, *end, *tmp; |
| 604 int descended = nodes_processed; |
604 int descended = nodes_processed; |
| 605 |
605 |
| 606 if (!root || !root->name) |
606 if (!root || !root->name) |
| 607 return; |
607 return; |
| 618 g_free(begin); |
618 g_free(begin); |
| 619 |
619 |
| 620 /* Loop over all child nodes. */ |
620 /* Loop over all child nodes. */ |
| 621 for (node = root->child; node != NULL; node = node->next) { |
621 for (node = root->child; node != NULL; node = node->next) { |
| 622 switch (node->type) { |
622 switch (node->type) { |
| 623 case XMLNODE_TYPE_ATTRIB: |
623 case PURPLE_XMLNODE_TYPE_ATTRIB: |
| 624 /* Attributes handled above. */ |
624 /* Attributes handled above. */ |
| 625 break; |
625 break; |
| 626 |
626 |
| 627 case XMLNODE_TYPE_TAG: |
627 case PURPLE_XMLNODE_TYPE_TAG: |
| 628 /* A tag or tag with attributes. Recursively descend. */ |
628 /* A tag or tag with attributes. Recursively descend. */ |
| 629 msim_convert_xmlnode(session, out, node, f, descended); |
629 msim_convert_xmlnode(session, out, node, f, descended); |
| 630 |
630 |
| 631 purple_debug_info("msim", " ** node name=%s\n", |
631 purple_debug_info("msim", " ** node name=%s\n", |
| 632 node->name ? node->name : "(NULL)"); |
632 node->name ? node->name : "(NULL)"); |
| 633 break; |
633 break; |
| 634 |
634 |
| 635 case XMLNODE_TYPE_DATA: |
635 case PURPLE_XMLNODE_TYPE_DATA: |
| 636 /* Literal text. */ |
636 /* Literal text. */ |
| 637 /* |
637 /* |
| 638 * TODO: Why is it necessary to escape here? I thought |
638 * TODO: Why is it necessary to escape here? I thought |
| 639 * node->data was already escaped? |
639 * node->data was already escaped? |
| 640 */ |
640 */ |
| 661 * Convert XML to something based on MSIM_XMLNODE_CONVERT. |
661 * Convert XML to something based on MSIM_XMLNODE_CONVERT. |
| 662 */ |
662 */ |
| 663 static gchar * |
663 static gchar * |
| 664 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) |
664 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) |
| 665 { |
665 { |
| 666 xmlnode *root; |
666 PurpleXmlNode *root; |
| 667 GString *str; |
667 GString *str; |
| 668 gchar *enclosed_raw; |
668 gchar *enclosed_raw; |
| 669 |
669 |
| 670 g_return_val_if_fail(raw != NULL, NULL); |
670 g_return_val_if_fail(raw != NULL, NULL); |
| 671 |
671 |
| 672 /* Enclose text in one root tag, to try to make it valid XML for parsing. */ |
672 /* Enclose text in one root tag, to try to make it valid XML for parsing. */ |
| 673 enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL); |
673 enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL); |
| 674 |
674 |
| 675 root = xmlnode_from_str(enclosed_raw, -1); |
675 root = purple_xmlnode_from_str(enclosed_raw, -1); |
| 676 |
676 |
| 677 if (!root) { |
677 if (!root) { |
| 678 purple_debug_warning("msim", "msim_markup_to_html: couldn't parse " |
678 purple_debug_warning("msim", "msim_markup_to_html: couldn't parse " |
| 679 "%s as XML, returning raw: %s\n", enclosed_raw, raw); |
679 "%s as XML, returning raw: %s\n", enclosed_raw, raw); |
| 680 /* TODO: msim_unrecognized */ |
680 /* TODO: msim_unrecognized */ |
| 684 |
684 |
| 685 g_free(enclosed_raw); |
685 g_free(enclosed_raw); |
| 686 |
686 |
| 687 str = g_string_new(NULL); |
687 str = g_string_new(NULL); |
| 688 msim_convert_xmlnode(session, str, root, f, 0); |
688 msim_convert_xmlnode(session, str, root, f, 0); |
| 689 xmlnode_free(root); |
689 purple_xmlnode_free(root); |
| 690 |
690 |
| 691 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str->str); |
691 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str->str); |
| 692 |
692 |
| 693 return g_string_free(str, FALSE); |
693 return g_string_free(str, FALSE); |
| 694 } |
694 } |