comparison libpurple/protocols/myspace/myspace.c @ 17662:7b890c79aabf

Add msim_markup_to_html() to convert MySpaceIM-style markup to Purple-compatible HTML, on incoming messages. So far, the <f> tag is supported, with font face, size, and bold/italic/underline decoration. Also add msim_test_xml(), containing experiments in using xmlnode to parse MySpaceIM markup.
author Jeffrey Connelly <jaconnel@calpoly.edu>
date Mon, 25 Jun 2007 04:11:06 +0000
parents 38030f1a2e56
children 814992cefc9c
comparison
equal deleted inserted replaced
17661:38030f1a2e56 17662:7b890c79aabf
675 msim_msg_free(msg); 675 msim_msg_free(msg);
676 676
677 return rc; 677 return rc;
678 } 678 }
679 679
680 /** Convert a font point size to purple's HTML font size.
681 *
682 * Based on libpurple/protocols/bonjour/jabber.c.
683 */
684 static guint
685 msim_font_size_to_purple(int size)
686 {
687 if (size > 24) {
688 return 7;
689 } else if (size >= 21) {
690 return 6;
691 } else if (size >= 17) {
692 return 5;
693 } else if (size >= 14) {
694 return 4;
695 } else if (size >= 12) {
696 return 3;
697 } else if (size >= 10) {
698 return 2;
699 }
700
701 return 1;
702 }
703
704 /** Convert a msim markup font height to points. */
705 static guint
706 msim_font_height_to_point(guint height)
707 {
708 /* See also: libpurple/protocols/bonjour/jabber.c
709 * _font_size_ichat_to_purple */
710 switch (height)
711 {
712 case 11: return 8;
713 case 12: return 9;
714 case 13: return 10;
715 case 14:
716 case 15: return 11;
717 case 16: return 12;
718 case 17:
719 case 18: return 13;
720 case 19: return 14;
721 case 20: return 15;
722 case 21: return 16;
723 default: return 12;
724 }
725 }
726
727 /** Convert an xmlnode of msim markup to an HTML string.
728 * @return An HTML string. Caller frees.
729 */
730 static gchar *msim_markup_xmlnode_to_html(xmlnode *root)
731 {
732 xmlnode *node;
733 gchar *begin, *inner, *end;
734 gchar *final;
735
736 if (!root)
737 return g_strdup("");
738
739 purple_debug_info("msim", "msim_markup_xmlnode_to_html: got root=%s\n",
740 root->name);
741
742 begin = inner = end = NULL;
743
744 if (!strcmp(root->name, "f"))
745 {
746 const gchar *face, *height_str, *decor_str;
747 GString *gs_end, *gs_begin;
748 guint decor, height;
749
750 face = xmlnode_get_attrib(root, "n");
751 height_str = xmlnode_get_attrib(root, "h");
752 decor_str = xmlnode_get_attrib(root, "s");
753
754 if (height_str)
755 height = atol(height_str);
756 else
757 height = 12;
758
759 if (decor_str)
760 decor = atol(decor_str);
761 else
762 decor = 0;
763
764 gs_begin = g_string_new("");
765 g_string_printf(gs_begin, "<font face='%s' size='%d'>", face,
766 msim_font_size_to_purple(msim_font_height_to_point(height)));
767 gs_end = g_string_new("</font>");
768
769 if (decor & 1)
770 {
771 g_string_append(gs_begin, "<b>");
772 g_string_prepend(gs_end, "</b>");
773 }
774
775 if (decor & 2)
776 {
777 g_string_append(gs_begin, "<i>");
778 g_string_append(gs_end, "</i>");
779 }
780
781 if (decor & 4)
782 {
783 g_string_append(gs_begin, "<u>");
784 g_string_append(gs_end, "</u>");
785 }
786
787
788 begin = gs_begin->str;
789 end = gs_end->str;
790 } else {
791 purple_debug_info("msim", "msim_markup_xmlnode_to_html: "
792 "unknown tag name=%s, ignoring", root->name);
793 begin = g_strdup("");
794 end = g_strdup("");
795 }
796
797 /* Loop over all child nodes. */
798 for (node = root->child; node != NULL; node = node->next)
799 {
800 switch (node->type)
801 {
802 case XMLNODE_TYPE_ATTRIB:
803 /* Attributes handled above. */
804 break;
805
806 case XMLNODE_TYPE_TAG:
807 /* A tag or tag with attributes. Recursively descend. */
808 inner = msim_markup_xmlnode_to_html(node);
809
810 purple_debug_info("msim", " ** node name=%s\n", node->name);
811 break;
812
813
814 case XMLNODE_TYPE_DATA:
815 /* Literal text. */
816 inner = g_new0(char, node->data_sz + 1);
817 strncpy(inner, node->data, node->data_sz);
818 inner[node->data_sz + 1] = 0;
819
820 purple_debug_info("msim", " ** node data=%s (%s)\n", inner,
821 node->data);
822 break;
823
824 default:
825 purple_debug_info("msim",
826 "msim_markup_xmlnode_to_html: strange node\n");
827 inner = g_strdup("");
828 }
829 }
830
831 final = g_strconcat(begin, inner, end, NULL);
832 g_free(begin);
833 g_free(inner);
834 g_free(end);
835
836 purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n",
837 final);
838
839 return final;
840 }
841
842 /** Convert MySpaceIM markup to GtkIMHtml markup.
843 * TODO
844 *
845 * @return GtkIMHtml markup string, must be g_free()'d. */
846 gchar *
847 msim_markup_to_html(const gchar *raw)
848 {
849 xmlnode *root;
850 gchar *str;
851
852 root = xmlnode_from_str(raw, -1);
853 if (!root)
854 {
855 purple_debug_info("msim", "msim_markup_to_html: couldn't parse "
856 "%s as XML, returning raw\n", raw);
857 return g_strdup(raw);
858 }
859
860 str = msim_markup_xmlnode_to_html(root);
861 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str);
862
863 xmlnode_free(root);
864
865 return g_strdup(str);
866 }
867
680 /** 868 /**
681 * Handle an incoming instant message. 869 * Handle an incoming instant message.
682 * 870 *
683 * @param session The session 871 * @param session The session
684 * @param msg Message from the server, containing 'f' (userid from) and 'msg'. 872 * @param msg Message from the server, containing 'f' (userid from) and 'msg'.
687 * @return TRUE if successful. 875 * @return TRUE if successful.
688 */ 876 */
689 gboolean 877 gboolean
690 msim_incoming_im(MsimSession *session, MsimMessage *msg) 878 msim_incoming_im(MsimSession *session, MsimMessage *msg)
691 { 879 {
692 gchar *username; 880 gchar *username, *msg_msim_markup, *msg_purple_markup;
693 gchar *msg_text;
694 881
695 username = msim_msg_get_string(msg, "_username"); 882 username = msim_msg_get_string(msg, "_username");
696 msg_text = msim_msg_get_string(msg, "msg"); 883 msg_msim_markup = msim_msg_get_string(msg, "msg");
697 884
698 /* TODO: replace msim-markup with gtkimhtml. */ 885 msg_purple_markup = msim_markup_to_html(msg_msim_markup);
699 886 g_free(msg_msim_markup);
700 serv_got_im(session->gc, username, msg_text, PURPLE_MESSAGE_RECV, time(NULL)); 887
888 serv_got_im(session->gc, username, msg_purple_markup,
889 PURPLE_MESSAGE_RECV, time(NULL));
701 890
702 g_free(username); 891 g_free(username);
703 g_free(msg_text); 892 g_free(msg_purple_markup);
704 893
705 return TRUE; 894 return TRUE;
706 } 895 }
707 896
708 /** 897 /**
2340 * Used to test or try out the internal workings of msimprpl. If you're reading 2529 * Used to test or try out the internal workings of msimprpl. If you're reading
2341 * this code for the first time, these functions can be instructive in how 2530 * this code for the first time, these functions can be instructive in how
2342 * msimprpl is architected. 2531 * msimprpl is architected.
2343 */ 2532 */
2344 void 2533 void
2345 msim_test_all(void) __attribute__((__noreturn__())); 2534 msim_test_all(void)
2346 { 2535 {
2347 guint failures; 2536 guint failures;
2348 2537
2538
2349 failures = 0; 2539 failures = 0;
2540 failures += msim_test_xml();
2350 failures += msim_test_msg(); 2541 failures += msim_test_msg();
2351 failures += msim_test_escaping(); 2542 failures += msim_test_escaping();
2352 2543
2353 if (failures) 2544 if (failures)
2354 { 2545 {
2357 else 2548 else
2358 { 2549 {
2359 purple_debug_info("msim", "msim_test_all - all tests passed!\n"); 2550 purple_debug_info("msim", "msim_test_all - all tests passed!\n");
2360 } 2551 }
2361 exit(0); 2552 exit(0);
2553 }
2554
2555 int
2556 msim_test_xml(void)
2557 {
2558 gchar *msg_text;
2559 xmlnode *root, *n;
2560 guint failures;
2561 char *s;
2562 int len;
2563
2564 failures = 0;
2565
2566 msg_text = "<p><f n=\"Arial\" h=\"12\">woo!</f>xxx<c v='black'>yyy</c></p>";
2567
2568 purple_debug_info("msim", "msim_test_xml: msg_text=%s\n", msg_text);
2569
2570 root = xmlnode_from_str(msg_text, -1);
2571 if (!root)
2572 {
2573 purple_debug_info("msim", "there is no root\n");
2574 exit(0);
2575 }
2576
2577 purple_debug_info("msim", "root name=%s, child name=%s\n", root->name,
2578 root->child->name);
2579
2580 purple_debug_info("msim", "last child name=%s\n", root->lastchild->name);
2581 purple_debug_info("msim", "Root xml=%s\n",
2582 xmlnode_to_str(root, &len));
2583 purple_debug_info("msim", "Child xml=%s\n",
2584 xmlnode_to_str(root->child, &len));
2585 purple_debug_info("msim", "Lastchild xml=%s\n",
2586 xmlnode_to_str(root->lastchild, &len));
2587 purple_debug_info("msim", "Next xml=%s\n",
2588 xmlnode_to_str(root->next, &len));
2589 purple_debug_info("msim", "Next data=%s\n",
2590 xmlnode_get_data(root->next));
2591 purple_debug_info("msim", "Child->next xml=%s\n",
2592 xmlnode_to_str(root->child->next, &len));
2593
2594 for (n = root->child; n; n = n->next)
2595 {
2596 if (n->name)
2597 {
2598 purple_debug_info("msim", " ** n=%s\n",n->name);
2599 } else {
2600 purple_debug_info("msim", " ** n data=%s\n", n->data);
2601 }
2602 }
2603
2604 purple_debug_info("msim", "root data=%s, child data=%s, child 'h'=%s\n",
2605 xmlnode_get_data(root),
2606 xmlnode_get_data(root->child),
2607 xmlnode_get_attrib(root->child, "h"));
2608
2609
2610 for (n = root->child;
2611 n != NULL;
2612 n = n->next)
2613 {
2614 purple_debug_info("msim", "next name=%s\n", n->name);
2615 }
2616
2617 s = xmlnode_to_str(root, &len);
2618 s[len] = 0;
2619
2620 purple_debug_info("msim", "str: %s\n", s);
2621 g_free(s);
2622
2623 xmlnode_free(root);
2624
2625 exit(0);
2626
2627 return failures;
2362 } 2628 }
2363 2629
2364 /** Test MsimMessage for basic functionality. */ 2630 /** Test MsimMessage for basic functionality. */
2365 int 2631 int
2366 msim_test_msg(void) 2632 msim_test_msg(void)