| 481 } |
485 } |
| 482 } |
486 } |
| 483 } |
487 } |
| 484 |
488 |
| 485 static void |
489 static void |
| 486 jabber_message_request_data_cb(JabberData *data, gchar *alt, |
490 jabber_message_remote_smiley_got(JabberData *data, gchar *alt, gpointer _smiley) |
| 487 gpointer userdata) |
491 { |
| 488 { |
492 PurpleRemoteSmiley *smiley = _smiley; |
| 489 PurpleConversation *conv = PURPLE_CONVERSATION(userdata); |
493 |
| |
494 g_free(alt); /* we really don't need it */ |
| 490 |
495 |
| 491 if (data) { |
496 if (data) { |
| 492 purple_conversation_custom_smiley_write(conv, alt, |
497 purple_debug_info("jabber", |
| 493 jabber_data_get_data(data), |
498 "smiley data retrieved successfully"); |
| 494 jabber_data_get_size(data)); |
499 purple_remote_smiley_write(smiley, jabber_data_get_data(data), |
| 495 purple_conversation_custom_smiley_close(conv, alt); |
500 jabber_data_get_size(data)); |
| 496 } |
501 purple_remote_smiley_close(smiley); |
| 497 |
502 } else { |
| 498 g_free(alt); |
503 purple_debug_error("jabber", "failed retrieving smiley data"); |
| |
504 purple_remote_smiley_failed(smiley); |
| |
505 } |
| |
506 |
| |
507 g_object_unref(smiley); |
| |
508 } |
| |
509 |
| |
510 static void |
| |
511 jabber_message_remote_smiley_add(JabberStream *js, PurpleConversation *conv, |
| |
512 const gchar *from, const gchar *shortcut, const gchar *cid) |
| |
513 { |
| |
514 PurpleRemoteSmiley *smiley; |
| |
515 const JabberData *jdata; |
| |
516 |
| |
517 purple_debug_misc("jabber", "about to add remote smiley %s to the conv", |
| |
518 shortcut); |
| |
519 |
| |
520 smiley = purple_conversation_add_remote_smiley(conv, shortcut); |
| |
521 if (!smiley) { |
| |
522 purple_debug_misc("jabber", "smiley was already present"); |
| |
523 return; |
| |
524 } |
| |
525 |
| |
526 /* TODO: cache lookup by "cid" */ |
| |
527 |
| |
528 jdata = jabber_data_find_remote_by_cid(js, from, cid); |
| |
529 if (jdata) { |
| |
530 purple_debug_info("jabber", "smiley data is already known"); |
| |
531 |
| |
532 purple_remote_smiley_write(smiley, jabber_data_get_data(jdata), |
| |
533 jabber_data_get_size(jdata)); |
| |
534 purple_remote_smiley_close(smiley); |
| |
535 } else { |
| |
536 gchar *alt = g_strdup(shortcut); /* it it really necessary? */ |
| |
537 |
| |
538 purple_debug_info("jabber", "smiley data is unknown, " |
| |
539 "need to request it"); |
| |
540 |
| |
541 g_object_ref(smiley); |
| |
542 jabber_data_request(js, cid, from, alt, FALSE, |
| |
543 jabber_message_remote_smiley_got, smiley); |
| |
544 } |
| 499 } |
545 } |
| 500 |
546 |
| 501 void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet) |
547 void jabber_message_parse(JabberStream *js, PurpleXmlNode *packet) |
| 502 { |
548 { |
| 503 JabberMessage *jm; |
549 JabberMessage *jm; |
| 650 |
696 |
| 651 /* add known custom emoticons to the conversation */ |
697 /* add known custom emoticons to the conversation */ |
| 652 /* note: if there were no smileys in the incoming message, or |
698 /* note: if there were no smileys in the incoming message, or |
| 653 if receiving custom smileys is turned off, smiley_refs will |
699 if receiving custom smileys is turned off, smiley_refs will |
| 654 be NULL */ |
700 be NULL */ |
| 655 for (; conv && smiley_refs ; smiley_refs = g_list_delete_link(smiley_refs, smiley_refs)) { |
701 for (it = smiley_refs; it; it = g_list_next(it)) { |
| 656 JabberSmileyRef *ref = (JabberSmileyRef *) smiley_refs->data; |
702 JabberSmileyRef *ref = it->data; |
| 657 const gchar *cid = ref->cid; |
703 |
| 658 gchar *alt = g_strdup(ref->alt); |
704 if (conv) { |
| 659 |
705 jabber_message_remote_smiley_add(js, |
| 660 purple_debug_info("jabber", |
706 conv, from, ref->alt, ref->cid); |
| 661 "about to add custom smiley %s to the conv\n", alt); |
|
| 662 if (purple_conversation_custom_smiley_add(conv, alt, "cid", cid, |
|
| 663 TRUE)) { |
|
| 664 const JabberData *data = |
|
| 665 jabber_data_find_remote_by_cid(js, from, cid); |
|
| 666 /* if data is already known, we write it immediatly */ |
|
| 667 if (data) { |
|
| 668 purple_debug_info("jabber", |
|
| 669 "data is already known\n"); |
|
| 670 purple_conversation_custom_smiley_write(conv, alt, |
|
| 671 jabber_data_get_data(data), |
|
| 672 jabber_data_get_size(data)); |
|
| 673 purple_conversation_custom_smiley_close(conv, alt); |
|
| 674 } else { |
|
| 675 /* we need to request the smiley (data) */ |
|
| 676 purple_debug_info("jabber", |
|
| 677 "data is unknown, need to request it\n"); |
|
| 678 jabber_data_request(js, cid, from, alt, FALSE, |
|
| 679 jabber_message_request_data_cb, conv); |
|
| 680 } |
|
| 681 } |
707 } |
| |
708 |
| 682 g_free(ref->cid); |
709 g_free(ref->cid); |
| 683 g_free(ref->alt); |
710 g_free(ref->alt); |
| 684 g_free(ref); |
711 g_free(ref); |
| 685 } |
712 } |
| |
713 g_list_free(smiley_refs); |
| 686 |
714 |
| 687 /* Convert all newlines to whitespace. Technically, even regular, non-XML HTML is supposed to ignore newlines, but Pidgin has, as convention |
715 /* Convert all newlines to whitespace. Technically, even regular, non-XML HTML is supposed to ignore newlines, but Pidgin has, as convention |
| 688 * treated \n as a newline for compatibility with other protocols |
716 * treated \n as a newline for compatibility with other protocols |
| 689 */ |
717 */ |
| 690 for (c = jm->xhtml; *c != '\0'; c++) { |
718 for (c = jm->xhtml; *c != '\0'; c++) { |
| 819 } else { |
847 } else { |
| 820 return "image/x-icon"; /* or something... */ |
848 return "image/x-icon"; /* or something... */ |
| 821 } |
849 } |
| 822 } |
850 } |
| 823 |
851 |
| 824 static GList * |
|
| 825 jabber_message_xhtml_find_smileys(const char *xhtml) |
|
| 826 { |
|
| 827 GList *smileys = purple_smileys_get_all(); |
|
| 828 GList *found_smileys = NULL; |
|
| 829 |
|
| 830 for (; smileys ; smileys = g_list_delete_link(smileys, smileys)) { |
|
| 831 PurpleSmiley *smiley = (PurpleSmiley *) smileys->data; |
|
| 832 |
|
| 833 const gchar *shortcut = purple_smiley_get_shortcut(smiley); |
|
| 834 const gssize len = strlen(shortcut); |
|
| 835 |
|
| 836 gchar *escaped = g_markup_escape_text(shortcut, len); |
|
| 837 const char *pos = strstr(xhtml, escaped); |
|
| 838 |
|
| 839 if (pos) { |
|
| 840 found_smileys = g_list_append(found_smileys, smiley); |
|
| 841 } |
|
| 842 |
|
| 843 g_free(escaped); |
|
| 844 } |
|
| 845 |
|
| 846 return found_smileys; |
|
| 847 } |
|
| 848 |
|
| 849 static gchar * |
|
| 850 jabber_message_get_smileyfied_xhtml(const gchar *xhtml, const GList *smileys) |
|
| 851 { |
|
| 852 /* create XML element for all smileys (img tags) */ |
|
| 853 GString *result = g_string_new(NULL); |
|
| 854 int pos = 0; |
|
| 855 int length = strlen(xhtml); |
|
| 856 |
|
| 857 while (pos < length) { |
|
| 858 const GList *iterator; |
|
| 859 gboolean found_smiley = FALSE; |
|
| 860 |
|
| 861 for (iterator = smileys ; iterator ; |
|
| 862 iterator = g_list_next(iterator)) { |
|
| 863 const PurpleSmiley *smiley = (PurpleSmiley *) iterator->data; |
|
| 864 const gchar *shortcut = purple_smiley_get_shortcut(smiley); |
|
| 865 const gssize len = strlen(shortcut); |
|
| 866 gchar *escaped = g_markup_escape_text(shortcut, len); |
|
| 867 |
|
| 868 if (g_str_has_prefix(&(xhtml[pos]), escaped)) { |
|
| 869 /* we found the current smiley at this position */ |
|
| 870 const JabberData *data = |
|
| 871 jabber_data_find_local_by_alt(shortcut); |
|
| 872 PurpleXmlNode *img = jabber_data_get_xhtml_im(data, shortcut); |
|
| 873 int len; |
|
| 874 gchar *img_text = purple_xmlnode_to_str(img, &len); |
|
| 875 |
|
| 876 found_smiley = TRUE; |
|
| 877 result = g_string_append(result, img_text); |
|
| 878 g_free(img_text); |
|
| 879 pos += strlen(escaped); |
|
| 880 g_free(escaped); |
|
| 881 purple_xmlnode_free(img); |
|
| 882 break; |
|
| 883 } else { |
|
| 884 /* cleanup from the before the next round... */ |
|
| 885 g_free(escaped); |
|
| 886 } |
|
| 887 } |
|
| 888 if (!found_smiley) { |
|
| 889 /* there was no smiley here, just copy one byte */ |
|
| 890 result = g_string_append_c(result, xhtml[pos]); |
|
| 891 pos++; |
|
| 892 } |
|
| 893 } |
|
| 894 |
|
| 895 return g_string_free(result, FALSE); |
|
| 896 } |
|
| 897 |
|
| 898 static gboolean |
852 static gboolean |
| 899 jabber_conv_support_custom_smileys(JabberStream *js, |
853 jabber_conv_support_custom_smileys(JabberStream *js, |
| 900 PurpleConversation *conv, |
854 PurpleConversation *conv, |
| 901 const gchar *who) |
855 const gchar *who) |
| 902 { |
856 { |
| 924 } else { |
878 } else { |
| 925 return FALSE; |
879 return FALSE; |
| 926 } |
880 } |
| 927 } |
881 } |
| 928 |
882 |
| |
883 static gboolean |
| |
884 jabber_message_smileyify_cb(GString *out, PurpleSmiley *smiley, |
| |
885 PurpleConversation *_empty, gpointer _unused) |
| |
886 { |
| |
887 const gchar *shortcut; |
| |
888 const JabberData *data; |
| |
889 PurpleXmlNode *smiley_node; |
| |
890 gchar *node_xml; |
| |
891 |
| |
892 shortcut = purple_smiley_get_shortcut(smiley); |
| |
893 data = jabber_data_find_local_by_alt(shortcut); |
| |
894 |
| |
895 if (!data) |
| |
896 return FALSE; |
| |
897 |
| |
898 smiley_node = jabber_data_get_xhtml_im(data, shortcut); |
| |
899 node_xml = purple_xmlnode_to_str(smiley_node, NULL); |
| |
900 |
| |
901 g_string_append(out, node_xml); |
| |
902 |
| |
903 purple_xmlnode_free(smiley_node); |
| |
904 g_free(node_xml); |
| |
905 |
| |
906 return TRUE; |
| |
907 } |
| |
908 |
| 929 static char * |
909 static char * |
| 930 jabber_message_smileyfy_xhtml(JabberMessage *jm, const char *xhtml) |
910 jabber_message_smileyfy_xhtml(JabberMessage *jm, const char *xhtml) |
| 931 { |
911 { |
| 932 PurpleAccount *account = purple_connection_get_account(jm->js->gc); |
912 PurpleAccount *account = purple_connection_get_account(jm->js->gc); |
| 933 PurpleConversation *conv = |
913 GList *found_smileys, *it, *it_next; |
| 934 purple_conversations_find_with_account(jm->to, |
914 PurpleConversation *conv; |
| 935 account); |
915 gboolean has_too_large_smiley = FALSE; |
| 936 |
916 gchar *smileyfied_xhtml = NULL; |
| 937 if (jabber_conv_support_custom_smileys(jm->js, conv, jm->to)) { |
917 |
| 938 GList *found_smileys = jabber_message_xhtml_find_smileys(xhtml); |
918 conv = purple_conversations_find_with_account(jm->to, account); |
| 939 |
919 |
| 940 if (found_smileys) { |
920 if (!jabber_conv_support_custom_smileys(jm->js, conv, jm->to)) |
| 941 gchar *smileyfied_xhtml = NULL; |
921 return NULL; |
| 942 const GList *iterator; |
922 |
| 943 GList *valid_smileys = NULL; |
923 found_smileys = purple_smiley_find(purple_smiley_custom_get_list(), |
| 944 gboolean has_too_large_smiley = FALSE; |
924 xhtml, TRUE); |
| 945 |
925 if (!found_smileys) |
| 946 for (iterator = found_smileys; iterator ; |
926 return NULL; |
| 947 iterator = g_list_next(iterator)) { |
927 |
| 948 PurpleSmiley *smiley = (PurpleSmiley *) iterator->data; |
928 for (it = found_smileys; it; it = it_next) { |
| 949 const gchar *shortcut = purple_smiley_get_shortcut(smiley); |
929 PurpleSmiley *smiley = it->data; |
| 950 const JabberData *data = |
930 PurpleStoredImage *smiley_image; |
| 951 jabber_data_find_local_by_alt(shortcut); |
931 gboolean valid = TRUE; |
| 952 PurpleStoredImage *image = purple_smiley_get_stored_image(smiley); |
932 |
| 953 |
933 it_next = g_list_next(it); |
| 954 if (purple_imgstore_get_size(image) <= JABBER_DATA_MAX_SIZE) { |
934 |
| 955 /* the object has not been sent before */ |
935 smiley_image = purple_smiley_get_image(smiley); |
| 956 if (!data) { |
936 if (!smiley_image) { |
| 957 const gchar *ext = purple_imgstore_get_extension(image); |
937 valid = FALSE; |
| 958 JabberStream *js = jm->js; |
938 purple_debug_warning("jabber", "broken smiley %s", |
| 959 |
939 purple_smiley_get_shortcut(smiley)); |
| 960 JabberData *new_data = |
940 } else if (purple_imgstore_get_size(smiley_image) > |
| 961 jabber_data_create_from_data(purple_imgstore_get_data(image), |
941 JABBER_DATA_MAX_SIZE) |
| 962 purple_imgstore_get_size(image), |
942 { |
| 963 jabber_message_get_mimetype_from_ext(ext), FALSE, js); |
943 has_too_large_smiley = TRUE; |
| 964 purple_debug_info("jabber", |
944 valid = FALSE; |
| 965 "cache local smiley alt = %s, cid = %s\n", |
945 purple_debug_warning("jabber", "Refusing to send " |
| 966 shortcut, jabber_data_get_cid(new_data)); |
946 "smiley %s (too large, max is %d)", |
| 967 jabber_data_associate_local(new_data, shortcut); |
947 purple_smiley_get_shortcut(smiley), |
| 968 } |
948 JABBER_DATA_MAX_SIZE); |
| 969 valid_smileys = g_list_append(valid_smileys, smiley); |
949 } |
| 970 } else { |
950 |
| 971 has_too_large_smiley = TRUE; |
951 if (!valid) |
| 972 purple_debug_warning("jabber", "Refusing to send smiley %s " |
952 found_smileys = g_list_delete_link(found_smileys, it); |
| 973 "(too large, max is %d)\n", |
953 } |
| 974 purple_smiley_get_shortcut(smiley), |
954 |
| 975 JABBER_DATA_MAX_SIZE); |
955 if (has_too_large_smiley) { |
| 976 } |
956 purple_conversation_write(conv, NULL, |
| 977 } |
957 _("A custom smiley in the message is too large to send."), |
| 978 |
958 PURPLE_MESSAGE_ERROR, time(NULL)); |
| 979 if (has_too_large_smiley) { |
959 } |
| 980 purple_conversation_write(conv, NULL, |
960 |
| 981 _("A custom smiley in the message is too large to send."), |
961 if (!found_smileys) |
| 982 PURPLE_MESSAGE_ERROR, time(NULL)); |
962 return NULL; |
| 983 } |
963 |
| 984 |
964 for (it = found_smileys; it; it = g_list_next(it)) { |
| 985 smileyfied_xhtml = |
965 PurpleSmiley *smiley = it->data; |
| 986 jabber_message_get_smileyfied_xhtml(xhtml, valid_smileys); |
966 PurpleStoredImage *smiley_image; |
| 987 g_list_free(found_smileys); |
967 const gchar *shortcut = purple_smiley_get_shortcut(smiley); |
| 988 g_list_free(valid_smileys); |
968 const gchar *mimetype; |
| 989 |
969 JabberData *jdata; |
| 990 return smileyfied_xhtml; |
970 |
| 991 } |
971 /* the object has been sent before */ |
| 992 } |
972 if (jabber_data_find_local_by_alt(shortcut)) |
| 993 |
973 continue; |
| 994 return NULL; |
974 |
| |
975 smiley_image = purple_smiley_get_image(smiley); |
| |
976 g_assert(smiley_image != NULL); |
| |
977 |
| |
978 mimetype = jabber_message_get_mimetype_from_ext( |
| |
979 purple_imgstore_get_extension(smiley_image)); |
| |
980 |
| |
981 jdata = jabber_data_create_from_data( |
| |
982 purple_imgstore_get_data(smiley_image), |
| |
983 purple_imgstore_get_size(smiley_image), |
| |
984 mimetype, FALSE, jm->js); |
| |
985 |
| |
986 purple_debug_info("jabber", "cache local smiley alt=%s, cid=%s", |
| |
987 shortcut, jabber_data_get_cid(jdata)); |
| |
988 jabber_data_associate_local(jdata, shortcut); |
| |
989 } |
| |
990 |
| |
991 g_list_free(found_smileys); |
| |
992 |
| |
993 smileyfied_xhtml = purple_smiley_parse_custom(xhtml, |
| |
994 jabber_message_smileyify_cb, NULL); |
| |
995 |
| |
996 return smileyfied_xhtml; |
| 995 } |
997 } |
| 996 |
998 |
| 997 void jabber_message_send(JabberMessage *jm) |
999 void jabber_message_send(JabberMessage *jm) |
| 998 { |
1000 { |
| 999 PurpleXmlNode *message, *child; |
1001 PurpleXmlNode *message, *child; |