Mercurial > pidgin
comparison libpurple/protocols/myspace/myspace.c @ 19434:1e00b684c46f
In msimprpl, move formatting functions to a markup module. It only exposes
two functions to convert between MySpaceIM markup and Purple HTML markup.
author | Jeffrey Connelly <jaconnel@calpoly.edu> |
---|---|
date | Sun, 26 Aug 2007 06:51:17 +0000 |
parents | 9a1b28a10c95 |
children | bddc6a6fddf0 |
comparison
equal
deleted
inserted
replaced
19433:9a1b28a10c95 | 19434:1e00b684c46f |
---|---|
34 #define PURPLE_PLUGIN | 34 #define PURPLE_PLUGIN |
35 | 35 |
36 #include "message.h" | 36 #include "message.h" |
37 #include "persist.h" | 37 #include "persist.h" |
38 #include "myspace.h" | 38 #include "myspace.h" |
39 | |
40 /* Globals */ | |
41 | |
42 /* The names in in emoticon_names (for <i n=whatever>) map to corresponding | |
43 * entries in emoticon_symbols (for the ASCII representation of the emoticon). | |
44 * | |
45 * Multiple emoticon symbols in Pidgin can map to one name. List the | |
46 * canonical form, as inserted by the "Smile!" dialog, first. For example, | |
47 * :) comes before :-), because although both are recognized as 'happy', | |
48 * the first is inserted by the smiley button (first symbol in theme). | |
49 * | |
50 * Note that symbols are case-sensitive in Pidgin -- :-X is not :-x. */ | |
51 static struct MSIM_EMOTICON | |
52 { | |
53 gchar *name; | |
54 gchar *symbol; | |
55 } msim_emoticons[] = { | |
56 /* Unfortunately, this list duplicates much of the file | |
57 * pidgin/pidgin/pixmaps/emotes/default/22/default.theme.in, because | |
58 * that file is part of Pidgin, but we're part of libpurple. | |
59 */ | |
60 { "bigsmile", ":D" }, | |
61 { "bigsmile", ":-D" }, | |
62 { "devil", "}:)" }, | |
63 { "frazzled", ":Z" }, | |
64 { "geek", "B)" }, | |
65 { "googles", "%)" }, | |
66 { "growl", ":E" }, | |
67 { "laugh", ":))" }, /* Must be before ':)' */ | |
68 { "happy", ":)" }, | |
69 { "happy", ":-)" }, | |
70 { "happi", ":)" }, | |
71 { "heart", ":X" }, | |
72 { "mohawk", "-:" }, | |
73 { "mad", "X(" }, | |
74 { "messed", "X)" }, | |
75 { "nerd", "Q)" }, | |
76 { "oops", ":G" }, | |
77 { "pirate", "P)" }, | |
78 { "scared", ":O" }, | |
79 { "sidefrown", ":{" }, | |
80 { "sinister", ":B" }, | |
81 { "smirk", ":," }, | |
82 { "straight", ":|" }, | |
83 { "tongue", ":P" }, | |
84 { "tongue", ":p" }, | |
85 { "tongy", ":P" }, | |
86 { "upset", "B|" }, | |
87 { "wink", ";-)" }, | |
88 { "wink", ";)" }, | |
89 { "winc", ";)" }, | |
90 { "worried", ":[" }, | |
91 { "kiss", ":x" }, | |
92 { NULL, NULL } | |
93 }; | |
94 | 39 |
95 /* Internal functions */ | 40 /* Internal functions */ |
96 | 41 |
97 #ifdef MSIM_DEBUG_MSG | 42 #ifdef MSIM_DEBUG_MSG |
98 static void print_hash_item(gpointer key, gpointer value, gpointer user_data); | 43 static void print_hash_item(gpointer key, gpointer value, gpointer user_data); |
103 static gboolean msim_login_challenge(MsimSession *session, MsimMessage *msg); | 48 static gboolean msim_login_challenge(MsimSession *session, MsimMessage *msg); |
104 static const gchar *msim_compute_login_response( | 49 static const gchar *msim_compute_login_response( |
105 const gchar nonce[2 * NONCE_SIZE], const gchar *email, | 50 const gchar nonce[2 * NONCE_SIZE], const gchar *email, |
106 const gchar *password, guint *response_len); | 51 const gchar *password, guint *response_len); |
107 | 52 |
108 static guint msim_point_to_purple_size(MsimSession *session, guint point); | |
109 static guint msim_purple_size_to_point(MsimSession *session, guint size); | |
110 static guint msim_height_to_point(MsimSession *session, guint height); | |
111 static guint msim_point_to_height(MsimSession *session, guint point); | |
112 | |
113 static void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note); | 53 static void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note); |
114 | |
115 static void msim_markup_tag_to_html(MsimSession *, xmlnode *root, | |
116 gchar **begin, gchar **end); | |
117 static void html_tag_to_msim_markup(MsimSession *, xmlnode *root, | |
118 gchar **begin, gchar **end); | |
119 static gchar *msim_convert_xml(MsimSession *, const gchar *raw, | |
120 MSIM_XMLNODE_CONVERT f); | |
121 static gchar *msim_convert_smileys_to_markup(gchar *before); | |
122 | |
123 /* High-level msim markup <=> html conversion functions. */ | |
124 static gchar *msim_markup_to_html(MsimSession *, const gchar *raw); | |
125 static gchar *html_to_msim_markup(MsimSession *, const gchar *raw); | |
126 | 54 |
127 static MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy); | 55 static MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy); |
128 static MsimUser *msim_find_user(MsimSession *session, const gchar *username); | 56 static MsimUser *msim_find_user(MsimSession *session, const gchar *username); |
129 | 57 |
130 static gboolean msim_incoming_bm_record_cv(MsimSession *session, | 58 static gboolean msim_incoming_bm_record_cv(MsimSession *session, |
199 | 127 |
200 static void msim_lookup_user(MsimSession *session, const gchar *user, | 128 static void msim_lookup_user(MsimSession *session, const gchar *user, |
201 MSIM_USER_LOOKUP_CB cb, gpointer data); | 129 MSIM_USER_LOOKUP_CB cb, gpointer data); |
202 | 130 |
203 static void msim_import_friends(PurplePluginAction *action); | 131 static void msim_import_friends(PurplePluginAction *action); |
204 | |
205 double msim_round(double round); | |
206 | |
207 /* round is part of C99, but sometimes is unavailable before then. | |
208 * Based on http://forums.belution.com/en/cpp/000/050/13.shtml | |
209 */ | |
210 double msim_round(double value) | |
211 { | |
212 if (value < 0) { | |
213 return -(floor(-value + 0.5)); | |
214 } else { | |
215 return floor( value + 0.5); | |
216 } | |
217 } | |
218 | 132 |
219 /** | 133 /** |
220 * Load the plugin. | 134 * Load the plugin. |
221 */ | 135 */ |
222 gboolean | 136 gboolean |
748 rc = msim_postprocess_outgoing(session, msg, who, "t", "cv"); | 662 rc = msim_postprocess_outgoing(session, msg, who, "t", "cv"); |
749 | 663 |
750 msim_msg_free(msg); | 664 msim_msg_free(msg); |
751 | 665 |
752 return rc; | 666 return rc; |
753 } | |
754 | |
755 /* Indexes of this array + 1 map HTML font size to scale of normal font size. * | |
756 * Based on _point_sizes from libpurple/gtkimhtml.c | |
757 * 1 2 3 4 5 6 7 */ | |
758 static gdouble _font_scale[] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736 }; | |
759 | |
760 #define MAX_FONT_SIZE 7 /* Purple maximum font size */ | |
761 #define POINTS_PER_INCH 72 /* How many pt's in an inch */ | |
762 | |
763 /** Convert typographical font point size to HTML font size. | |
764 * Based on libpurple/gtkimhtml.c */ | |
765 static guint | |
766 msim_point_to_purple_size(MsimSession *session, guint point) | |
767 { | |
768 guint size, this_point, base; | |
769 gdouble scale; | |
770 | |
771 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); | |
772 | |
773 for (size = 0; | |
774 size < sizeof(_font_scale) / sizeof(_font_scale[0]); | |
775 ++size) { | |
776 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; | |
777 this_point = (guint)msim_round(scale * base); | |
778 | |
779 if (this_point >= point) { | |
780 purple_debug_info("msim", "msim_point_to_purple_size: %d pt -> size=%d\n", | |
781 point, size); | |
782 return size; | |
783 } | |
784 } | |
785 | |
786 /* No HTML font size was this big; return largest possible. */ | |
787 return this_point; | |
788 } | |
789 | |
790 /** Convert HTML font size to point size. */ | |
791 static guint | |
792 msim_purple_size_to_point(MsimSession *session, guint size) | |
793 { | |
794 gdouble scale; | |
795 guint point; | |
796 guint base; | |
797 | |
798 scale = _font_scale[CLAMP(size, 1, MAX_FONT_SIZE) - 1]; | |
799 | |
800 base = purple_account_get_int(session->account, "base_font_size", MSIM_BASE_FONT_POINT_SIZE); | |
801 | |
802 point = (guint)msim_round(scale * base); | |
803 | |
804 purple_debug_info("msim", "msim_purple_size_to_point: size=%d -> %d pt\n", | |
805 size, point); | |
806 | |
807 return point; | |
808 } | |
809 | |
810 /** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ | |
811 static guint | |
812 msim_height_to_point(MsimSession *session, guint height) | |
813 { | |
814 guint dpi; | |
815 | |
816 dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); | |
817 | |
818 return (guint)msim_round((POINTS_PER_INCH * 1. / dpi) * height); | |
819 | |
820 /* See also: libpurple/protocols/bonjour/jabber.c | |
821 * _font_size_ichat_to_purple */ | |
822 } | |
823 | |
824 /** Convert point size to msim pixel height font size specification, for outgoing messages. */ | |
825 static guint | |
826 msim_point_to_height(MsimSession *session, guint point) | |
827 { | |
828 guint dpi; | |
829 | |
830 dpi = purple_account_get_int(session->account, "port", MSIM_DEFAULT_DPI); | |
831 | |
832 return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); | |
833 } | |
834 | |
835 /** Convert the msim markup <f> (font) tag into HTML. */ | |
836 static void | |
837 msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | |
838 { | |
839 const gchar *face, *height_str, *decor_str; | |
840 GString *gs_end, *gs_begin; | |
841 guint decor, height; | |
842 | |
843 face = xmlnode_get_attrib(root, "f"); | |
844 height_str = xmlnode_get_attrib(root, "h"); | |
845 decor_str = xmlnode_get_attrib(root, "s"); | |
846 | |
847 if (height_str) { | |
848 height = atol(height_str); | |
849 } else { | |
850 height = 12; | |
851 } | |
852 | |
853 if (decor_str) { | |
854 decor = atol(decor_str); | |
855 } else { | |
856 decor = 0; | |
857 } | |
858 | |
859 gs_begin = g_string_new(""); | |
860 /* TODO: get font size working */ | |
861 if (height && !face) { | |
862 g_string_printf(gs_begin, "<font size='%d'>", | |
863 msim_point_to_purple_size(session, msim_height_to_point(session, height))); | |
864 } else if (height && face) { | |
865 g_string_printf(gs_begin, "<font face='%s' size='%d'>", face, | |
866 msim_point_to_purple_size(session, msim_height_to_point(session, height))); | |
867 } else { | |
868 g_string_printf(gs_begin, "<font>"); | |
869 } | |
870 | |
871 /* No support for font-size CSS? */ | |
872 /* g_string_printf(gs_begin, "<span style='font-family: %s; font-size: %dpt'>", face, | |
873 msim_height_to_point(height)); */ | |
874 | |
875 gs_end = g_string_new("</font>"); | |
876 | |
877 if (decor & MSIM_TEXT_BOLD) { | |
878 g_string_append(gs_begin, "<b>"); | |
879 g_string_prepend(gs_end, "</b>"); | |
880 } | |
881 | |
882 if (decor & MSIM_TEXT_ITALIC) { | |
883 g_string_append(gs_begin, "<i>"); | |
884 g_string_append(gs_end, "</i>"); | |
885 } | |
886 | |
887 if (decor & MSIM_TEXT_UNDERLINE) { | |
888 g_string_append(gs_begin, "<u>"); | |
889 g_string_append(gs_end, "</u>"); | |
890 } | |
891 | |
892 | |
893 *begin = gs_begin->str; | |
894 *end = gs_end->str; | |
895 } | |
896 | |
897 /** Convert a msim markup color to a color suitable for libpurple. | |
898 * | |
899 * @param msim Either a color name, or an rgb(x,y,z) code. | |
900 * | |
901 * @return A new string, either a color name or #rrggbb code. Must g_free(). | |
902 */ | |
903 static char * | |
904 msim_color_to_purple(const char *msim) | |
905 { | |
906 guint red, green, blue; | |
907 | |
908 if (!msim) { | |
909 return g_strdup("black"); | |
910 } | |
911 | |
912 if (sscanf(msim, "rgb(%d,%d,%d)", &red, &green, &blue) != 3) { | |
913 /* Color name. */ | |
914 return g_strdup(msim); | |
915 } | |
916 /* TODO: rgba (alpha). */ | |
917 | |
918 return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); | |
919 } | |
920 | |
921 /** Convert the msim markup <a> (anchor) tag into HTML. */ | |
922 static void | |
923 msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | |
924 { | |
925 const gchar *href; | |
926 | |
927 href = xmlnode_get_attrib(root, "h"); | |
928 if (!href) { | |
929 href = ""; | |
930 } | |
931 | |
932 *begin = g_strdup_printf("<a href=\"%s\">%s", href, href); | |
933 *end = g_strdup("</a>"); | |
934 } | |
935 | |
936 /** Convert the msim markup <p> (paragraph) tag into HTML. */ | |
937 static void | |
938 msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | |
939 { | |
940 /* Just pass through unchanged. | |
941 * | |
942 * Note: attributes currently aren't passed, if there are any. */ | |
943 *begin = g_strdup("<p>"); | |
944 *end = g_strdup("</p>"); | |
945 } | |
946 | |
947 /** Convert the msim markup <c> tag (text color) into HTML. TODO: Test */ | |
948 static void | |
949 msim_markup_c_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | |
950 { | |
951 const gchar *color; | |
952 gchar *purple_color; | |
953 | |
954 color = xmlnode_get_attrib(root, "v"); | |
955 if (!color) { | |
956 purple_debug_info("msim", "msim_markup_c_to_html: <c> tag w/o v attr"); | |
957 *begin = g_strdup(""); | |
958 *end = g_strdup(""); | |
959 /* TODO: log as unrecognized */ | |
960 return; | |
961 } | |
962 | |
963 purple_color = msim_color_to_purple(color); | |
964 | |
965 *begin = g_strdup_printf("<font color='%s'>", purple_color); | |
966 | |
967 g_free(purple_color); | |
968 | |
969 /* *begin = g_strdup_printf("<span style='color: %s'>", color); */ | |
970 *end = g_strdup("</font>"); | |
971 } | |
972 | |
973 /** Convert the msim markup <b> tag (background color) into HTML. TODO: Test */ | |
974 static void | |
975 msim_markup_b_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | |
976 { | |
977 const gchar *color; | |
978 gchar *purple_color; | |
979 | |
980 color = xmlnode_get_attrib(root, "v"); | |
981 if (!color) { | |
982 *begin = g_strdup(""); | |
983 *end = g_strdup(""); | |
984 purple_debug_info("msim", "msim_markup_b_to_html: <b> w/o v attr"); | |
985 /* TODO: log as unrecognized. */ | |
986 return; | |
987 } | |
988 | |
989 purple_color = msim_color_to_purple(color); | |
990 | |
991 /* TODO: find out how to set background color. */ | |
992 *begin = g_strdup_printf("<span style='background-color: %s'>", | |
993 purple_color); | |
994 g_free(purple_color); | |
995 | |
996 *end = g_strdup("</p>"); | |
997 } | |
998 | |
999 /** Convert the msim markup <i> tag (emoticon image) into HTML. */ | |
1000 static void | |
1001 msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) | |
1002 { | |
1003 const gchar *name; | |
1004 guint i; | |
1005 struct MSIM_EMOTICON *emote; | |
1006 | |
1007 name = xmlnode_get_attrib(root, "n"); | |
1008 if (!name) { | |
1009 purple_debug_info("msim", "msim_markup_i_to_html: <i> w/o n"); | |
1010 *begin = g_strdup(""); | |
1011 *end = g_strdup(""); | |
1012 /* TODO: log as unrecognized */ | |
1013 return; | |
1014 } | |
1015 | |
1016 /* Find and use canonical form of smiley symbol. */ | |
1017 for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { | |
1018 if (g_str_equal(name, emote->name)) { | |
1019 *begin = g_strdup(emote->symbol); | |
1020 *end = g_strdup(""); | |
1021 return; | |
1022 } | |
1023 } | |
1024 | |
1025 /* Couldn't find it, sorry. Try to degrade gracefully. */ | |
1026 *begin = g_strdup_printf("**%s**", name); | |
1027 *end = g_strdup(""); | |
1028 } | |
1029 | |
1030 /** Convert an individual msim markup tag to HTML. */ | |
1031 static void | |
1032 msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, | |
1033 gchar **end) | |
1034 { | |
1035 if (g_str_equal(root->name, "f")) { | |
1036 msim_markup_f_to_html(session, root, begin, end); | |
1037 } else if (g_str_equal(root->name, "a")) { | |
1038 msim_markup_a_to_html(session, root, begin, end); | |
1039 } else if (g_str_equal(root->name, "p")) { | |
1040 msim_markup_p_to_html(session, root, begin, end); | |
1041 } else if (g_str_equal(root->name, "c")) { | |
1042 msim_markup_c_to_html(session, root, begin, end); | |
1043 } else if (g_str_equal(root->name, "b")) { | |
1044 msim_markup_b_to_html(session, root, begin, end); | |
1045 } else if (g_str_equal(root->name, "i")) { | |
1046 msim_markup_i_to_html(session, root, begin, end); | |
1047 } else { | |
1048 purple_debug_info("msim", "msim_markup_tag_to_html: " | |
1049 "unknown tag name=%s, ignoring", | |
1050 (root && root->name) ? root->name : "(NULL)"); | |
1051 *begin = g_strdup(""); | |
1052 *end = g_strdup(""); | |
1053 } | |
1054 } | |
1055 | |
1056 /** Convert an individual HTML tag to msim markup. */ | |
1057 static void | |
1058 html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, | |
1059 gchar **end) | |
1060 { | |
1061 /* TODO: Coalesce nested tags into one <f> tag! | |
1062 * Currently, the 's' value will be overwritten when b/i/u is nested | |
1063 * within another one, and only the inner-most formatting will be | |
1064 * applied to the text. */ | |
1065 if (!purple_utf8_strcasecmp(root->name, "root")) { | |
1066 *begin = g_strdup(""); | |
1067 *end = g_strdup(""); | |
1068 } else if (!purple_utf8_strcasecmp(root->name, "b")) { | |
1069 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_BOLD); | |
1070 *end = g_strdup("</f>"); | |
1071 } else if (!purple_utf8_strcasecmp(root->name, "i")) { | |
1072 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_ITALIC); | |
1073 *end = g_strdup("</f>"); | |
1074 } else if (!purple_utf8_strcasecmp(root->name, "u")) { | |
1075 *begin = g_strdup_printf("<f s='%d'>", MSIM_TEXT_UNDERLINE); | |
1076 *end = g_strdup("</f>"); | |
1077 } else if (!purple_utf8_strcasecmp(root->name, "a")) { | |
1078 const gchar *href, *link_text; | |
1079 | |
1080 href = xmlnode_get_attrib(root, "href"); | |
1081 | |
1082 if (!href) { | |
1083 href = xmlnode_get_attrib(root, "HREF"); | |
1084 } | |
1085 | |
1086 link_text = xmlnode_get_data(root); | |
1087 | |
1088 if (href) { | |
1089 if (g_str_equal(link_text, href)) { | |
1090 /* Purple gives us: <a href="URL">URL</a> | |
1091 * Translate to <a h='URL' /> | |
1092 * Displayed as text of URL with link to URL | |
1093 */ | |
1094 *begin = g_strdup_printf("<a h='%s' />", href); | |
1095 } else { | |
1096 /* But if we get: <a href="URL">text</a> | |
1097 * Translate to: text: <a h='URL' /> | |
1098 * | |
1099 * Because official client only supports self-closed <a> | |
1100 * tags; you can't change the link text. | |
1101 */ | |
1102 *begin = g_strdup_printf("%s: <a h='%s' />", link_text, href); | |
1103 } | |
1104 } else { | |
1105 *begin = g_strdup("<a />"); | |
1106 } | |
1107 | |
1108 /* Sorry, kid. MySpace doesn't support you within <a> tags. */ | |
1109 xmlnode_free(root->child); | |
1110 root->child = NULL; | |
1111 | |
1112 *end = g_strdup(""); | |
1113 } else if (!purple_utf8_strcasecmp(root->name, "font")) { | |
1114 const gchar *size; | |
1115 const gchar *face; | |
1116 | |
1117 size = xmlnode_get_attrib(root, "size"); | |
1118 face = xmlnode_get_attrib(root, "face"); | |
1119 | |
1120 if (face && size) { | |
1121 *begin = g_strdup_printf("<f f='%s' h='%d'>", face, | |
1122 msim_point_to_height(session, | |
1123 msim_purple_size_to_point(session, atoi(size)))); | |
1124 } else if (face) { | |
1125 *begin = g_strdup_printf("<f f='%s'>", face); | |
1126 } else if (size) { | |
1127 *begin = g_strdup_printf("<f h='%d'>", | |
1128 msim_point_to_height(session, | |
1129 msim_purple_size_to_point(session, atoi(size)))); | |
1130 } else { | |
1131 *begin = g_strdup("<f>"); | |
1132 } | |
1133 | |
1134 *end = g_strdup("</f>"); | |
1135 | |
1136 /* TODO: color (bg uses <body>), emoticons */ | |
1137 } else { | |
1138 *begin = g_strdup_printf("[%s]", root->name); | |
1139 *end = g_strdup_printf("[/%s]", root->name); | |
1140 } | |
1141 } | |
1142 | |
1143 /** Convert an xmlnode of msim markup or HTML to an HTML string or msim markup. | |
1144 * | |
1145 * @param f Function to convert tags. | |
1146 * | |
1147 * @return An HTML string. Caller frees. | |
1148 */ | |
1149 static gchar * | |
1150 msim_convert_xmlnode(MsimSession *session, xmlnode *root, MSIM_XMLNODE_CONVERT f) | |
1151 { | |
1152 xmlnode *node; | |
1153 gchar *begin, *inner, *end; | |
1154 GString *final; | |
1155 | |
1156 if (!root || !root->name) { | |
1157 return g_strdup(""); | |
1158 } | |
1159 | |
1160 purple_debug_info("msim", "msim_convert_xmlnode: got root=%s\n", | |
1161 root->name); | |
1162 | |
1163 begin = inner = end = NULL; | |
1164 | |
1165 final = g_string_new(""); | |
1166 | |
1167 f(session, root, &begin, &end); | |
1168 | |
1169 g_string_append(final, begin); | |
1170 | |
1171 /* Loop over all child nodes. */ | |
1172 for (node = root->child; node != NULL; node = node->next) { | |
1173 switch (node->type) { | |
1174 case XMLNODE_TYPE_ATTRIB: | |
1175 /* Attributes handled above. */ | |
1176 break; | |
1177 | |
1178 case XMLNODE_TYPE_TAG: | |
1179 /* A tag or tag with attributes. Recursively descend. */ | |
1180 inner = msim_convert_xmlnode(session, node, f); | |
1181 g_return_val_if_fail(inner != NULL, NULL); | |
1182 | |
1183 purple_debug_info("msim", " ** node name=%s\n", | |
1184 (node && node->name) ? node->name : "(NULL)"); | |
1185 break; | |
1186 | |
1187 case XMLNODE_TYPE_DATA: | |
1188 /* Literal text. */ | |
1189 inner = g_new0(char, node->data_sz + 1); | |
1190 strncpy(inner, node->data, node->data_sz); | |
1191 inner[node->data_sz] = 0; | |
1192 | |
1193 purple_debug_info("msim", " ** node data=%s\n", | |
1194 inner ? inner : "(NULL)"); | |
1195 break; | |
1196 | |
1197 default: | |
1198 purple_debug_info("msim", | |
1199 "msim_convert_xmlnode: strange node\n"); | |
1200 inner = g_strdup(""); | |
1201 } | |
1202 | |
1203 if (inner) { | |
1204 g_string_append(final, inner); | |
1205 } | |
1206 } | |
1207 | |
1208 /* TODO: Note that msim counts each piece of text enclosed by <f> as | |
1209 * a paragraph and will display each on its own line. You actually have | |
1210 * to _nest_ <f> tags to intersperse different text in one paragraph! | |
1211 * Comment out this line below to see. */ | |
1212 g_string_append(final, end); | |
1213 | |
1214 purple_debug_info("msim", "msim_markup_xmlnode_to_gtkhtml: RETURNING %s\n", | |
1215 (final && final->str) ? final->str : "(NULL)"); | |
1216 | |
1217 return final->str; | |
1218 } | |
1219 | |
1220 /** Convert XML to something based on MSIM_XMLNODE_CONVERT. */ | |
1221 static gchar * | |
1222 msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) | |
1223 { | |
1224 xmlnode *root; | |
1225 gchar *str; | |
1226 gchar *enclosed_raw; | |
1227 | |
1228 g_return_val_if_fail(raw != NULL, NULL); | |
1229 | |
1230 /* Enclose text in one root tag, to try to make it valid XML for parsing. */ | |
1231 enclosed_raw = g_strconcat("<root>", raw, "</root>", NULL); | |
1232 | |
1233 root = xmlnode_from_str(enclosed_raw, -1); | |
1234 | |
1235 if (!root) { | |
1236 purple_debug_info("msim", "msim_markup_to_html: couldn't parse " | |
1237 "%s as XML, returning raw: %s\n", enclosed_raw, raw); | |
1238 /* TODO: msim_unrecognized */ | |
1239 g_free(enclosed_raw); | |
1240 return g_strdup(raw); | |
1241 } | |
1242 | |
1243 g_free(enclosed_raw); | |
1244 | |
1245 str = msim_convert_xmlnode(session, root, f); | |
1246 g_return_val_if_fail(str != NULL, NULL); | |
1247 purple_debug_info("msim", "msim_markup_to_html: returning %s\n", str); | |
1248 | |
1249 xmlnode_free(root); | |
1250 | |
1251 return str; | |
1252 } | |
1253 | |
1254 /** Convert plaintext smileys to <i> markup tags. | |
1255 * | |
1256 * @param before Original text with ASCII smileys. Will be freed. | |
1257 * @return A new string with <i> tags, if applicable. Must be g_free()'d. | |
1258 */ | |
1259 static gchar * | |
1260 msim_convert_smileys_to_markup(gchar *before) | |
1261 { | |
1262 gchar *old, *new, *replacement; | |
1263 guint i; | |
1264 struct MSIM_EMOTICON *emote; | |
1265 | |
1266 old = before; | |
1267 new = NULL; | |
1268 | |
1269 for (i = 0; (emote = &msim_emoticons[i]) && emote->name != NULL; ++i) { | |
1270 gchar *name, *symbol; | |
1271 | |
1272 name = emote->name; | |
1273 symbol = emote->symbol; | |
1274 | |
1275 replacement = g_strdup_printf("<i n=\"%s\"/>", name); | |
1276 | |
1277 purple_debug_info("msim", "msim_convert_smileys_to_markup: %s->%s\n", | |
1278 symbol ? symbol : "(NULL)", | |
1279 replacement ? replacement : "(NULL)"); | |
1280 new = str_replace(old, symbol, replacement); | |
1281 | |
1282 g_free(replacement); | |
1283 g_free(old); | |
1284 | |
1285 old = new; | |
1286 } | |
1287 | |
1288 return new; | |
1289 } | |
1290 | |
1291 | |
1292 /** High-level function to convert MySpaceIM markup to Purple (HTML) markup. | |
1293 * | |
1294 * @return Purple markup string, must be g_free()'d. */ | |
1295 static gchar * | |
1296 msim_markup_to_html(MsimSession *session, const gchar *raw) | |
1297 { | |
1298 return msim_convert_xml(session, raw, | |
1299 (MSIM_XMLNODE_CONVERT)(msim_markup_tag_to_html)); | |
1300 } | |
1301 | |
1302 /** High-level function to convert Purple (HTML) to MySpaceIM markup. | |
1303 * | |
1304 * @return HTML markup string, must be g_free()'d. */ | |
1305 static gchar * | |
1306 html_to_msim_markup(MsimSession *session, const gchar *raw) | |
1307 { | |
1308 gchar *markup; | |
1309 | |
1310 markup = msim_convert_xml(session, raw, | |
1311 (MSIM_XMLNODE_CONVERT)(html_tag_to_msim_markup)); | |
1312 | |
1313 if (purple_account_get_bool(session->account, "emoticons", TRUE)) { | |
1314 /* Frees markup and allocates a new one. */ | |
1315 markup = msim_convert_smileys_to_markup(markup); | |
1316 } | |
1317 | |
1318 return markup; | |
1319 } | 667 } |
1320 | 668 |
1321 /** Get the MsimUser from a PurpleBuddy, creating it if needed. */ | 669 /** Get the MsimUser from a PurpleBuddy, creating it if needed. */ |
1322 static MsimUser * | 670 static MsimUser * |
1323 msim_get_user_from_buddy(PurpleBuddy *buddy) | 671 msim_get_user_from_buddy(PurpleBuddy *buddy) |