From 7fc0bdc1e4ed324b6442e4e7d70b5be81cb368cb Mon Sep 17 00:00:00 2001 From: Emil Ivov Date: Fri, 11 Jul 2008 23:58:06 +0000 Subject: [PATCH] Added support for formatted messags in XMPP. Introduced a HTML_MIME_TYPE static field in OpSetBasicInstantMessaging --- ...ationSetBasicInstantMessagingDictImpl.java | 4 +- ...rationSetBasicInstantMessagingIcqImpl.java | 11 +-- ...ionSetBasicInstantMessagingJabberImpl.java | 87 ++++++++++++++++--- ...ationSetTypingNotificationsJabberImpl.java | 9 +- .../ProtocolProviderServiceJabberImpl.java | 2 + ...rationSetBasicInstantMessagingRssImpl.java | 4 +- ...rationSetBasicInstantMessagingSipImpl.java | 9 +- ...tionSetBasicInstantMessagingYahooImpl.java | 11 +-- .../OperationSetBasicInstantMessaging.java | 5 ++ .../java/sip/communicator/util/Html2Text.java | 52 +++++++++++ .../sip/communicator/util/util.manifest.mf | 2 + 11 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 src/net/java/sip/communicator/util/Html2Text.java diff --git a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java index c3954645b..626dda057 100644 --- a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java @@ -227,7 +227,7 @@ public boolean isContentTypeSupported(String contentType) { if(contentType.equals(DEFAULT_MIME_TYPE)) return true; - else if(contentType.equals("text/html")) + else if(contentType.equals(HTML_MIME_TYPE)) return true; else return false; @@ -299,7 +299,7 @@ private void submitDictQuery(ContactDictImpl dictContact, Message message) { fctResult = dictAdapter.define(database, word); msg = this.createMessage(this.retrieveDefine(fctResult, word).getBytes() - , "text/html" + , HTML_MIME_TYPE , DEFAULT_MIME_ENCODING, null); } diff --git a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java index 494cf10a7..d58fe3aa5 100644 --- a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java @@ -35,11 +35,6 @@ public class OperationSetBasicInstantMessagingIcqImpl private static final Logger logger = Logger.getLogger(OperationSetBasicInstantMessagingIcqImpl.class); - /** - * HTML content type - */ - private static final String CONTENT_TYPE_HTML = "text/html"; - /** * A list of listeneres registered for message events. */ @@ -227,7 +222,7 @@ public void sendInstantMessage(Contact to, Message message) new Screenname(to.getAddress())); String messageContent = null; - if (message.getContentType().equals(CONTENT_TYPE_HTML) + if (message.getContentType().equals(HTML_MIME_TYPE) && !message.getContent().startsWith(defaultHtmlStartTag)) { messageContent = defaultHtmlStartTag @@ -403,7 +398,7 @@ public boolean isOfflineMessagingSupported() public boolean isContentTypeSupported(String contentType) { if(contentType.equals(DEFAULT_MIME_TYPE) || - (contentType.equals(CONTENT_TYPE_HTML))) + (contentType.equals(HTML_MIME_TYPE))) return true; else return false; @@ -595,7 +590,7 @@ public void gotMessage(Conversation conversation, MessageInfo minfo) msgContent = msgBody; Message newMessage = createMessage(msgContent.getBytes(), - CONTENT_TYPE_HTML, DEFAULT_MIME_ENCODING, null); + HTML_MIME_TYPE, DEFAULT_MIME_ENCODING, null); Contact sourceContact = opSetPersPresence.findContactByID( conversation.getBuddy() diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java index 3da178102..47c969591 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java @@ -23,6 +23,7 @@ import org.jivesoftware.smack.provider.*; import org.jivesoftware.smack.util.*; import org.jivesoftware.smackx.*; +import org.jivesoftware.smackx.packet.XHTMLExtension; /** * A straightforward implementation of the basic instant messaging operation @@ -78,6 +79,22 @@ public class OperationSetBasicInstantMessagingJabberImpl * to match incoming messages to Contacts and vice versa. */ private OperationSetPersistentPresenceJabberImpl opSetPersPresence = null; + + /** + * The opening BODY HTML TAG: <body> + */ + private static final String OPEN_BODY_TAG = ""; + + /** + * The closing BODY HTML TAG: <body> + */ + private static final String CLOSE_BODY_TAG = ""; + + /** + * The instance of the html2Txt converter that we use to send messages in + * both the text/html and text/plain content types. + */ + private Html2Text html2Txt = new Html2Text(); /** * Creates an instance of this operation set. @@ -145,6 +162,18 @@ public Message createMessage(byte[] content, String contentType, return new MessageJabberImpl(new String(content), contentType , contentEncoding, subject); } + + /** + * Create a Message instance for sending arbitrary MIME-encoding content. + * + * @param content content value + * @param contentType the MIME-type for content + * @return the newly created message. + */ + public Message createMessage(byte[] content, String contentType) + { + return createMessage(content, contentType, DEFAULT_MIME_ENCODING, null); + } /** * Create a Message instance for sending a simple text messages with @@ -155,7 +184,7 @@ public Message createMessage(byte[] content, String contentType, */ public Message createMessage(String messageText) { - return new MessageJabberImpl(messageText, DEFAULT_MIME_TYPE + return createMessage(messageText.getBytes(), DEFAULT_MIME_TYPE , DEFAULT_MIME_ENCODING, null); } @@ -187,7 +216,8 @@ public boolean isOfflineMessagingSupported() */ public boolean isContentTypeSupported(String contentType) { - if(contentType.equals(DEFAULT_MIME_TYPE)) + if(contentType.equals(DEFAULT_MIME_TYPE) + || contentType.equals(HTML_MIME_TYPE)) return true; else return false; @@ -218,23 +248,38 @@ public void sendInstantMessage(Contact to, Message message) org.jivesoftware.smack.MessageListener msgListener = new org.jivesoftware.smack.MessageListener() { - public void processMessage(Chat arg0, org.jivesoftware.smack.packet.Message arg1) { - } + public void processMessage( + Chat arg0, + org.jivesoftware.smack.packet.Message arg1) {} }; Chat chat = jabberProvider.getConnection().getChatManager().createChat( - to.getAddress(), msgListener - ); + to.getAddress(), msgListener); org.jivesoftware.smack.packet.Message msg = - new org.jivesoftware.smack.packet.Message(); + new org.jivesoftware.smack.packet.Message(); - msg.setBody(message.getContent()); - msg.addExtension(new Version()); + String content = message.getContent(); + + if(message.getContentType().equals(HTML_MIME_TYPE)) + { + msg.setBody(html2Txt.extractText(content)); + + // Add the XHTML text to the message + XHTMLManager.addBody(msg, + OPEN_BODY_TAG + content + CLOSE_BODY_TAG); + } + else + { + // this is plain text so keep it as it is. + msg.setBody(content); + } + msg.addExtension(new Version()); + MessageEventManager. addNotificationsRequests(msg, true, false, false, true); - + chat.sendMessage(msg); MessageDeliveredEvent msgDeliveredEvt @@ -374,6 +419,28 @@ public void processPacket(Packet packet) } Message newMessage = createMessage(msg.getBody()); + + //check if the message is available in xhtml + PacketExtension ext = msg.getExtension("http://jabber.org/protocol/xhtml-im"); + + if(ext != null) + { + XHTMLExtension xhtmlExt + = (XHTMLExtension)ext; + + //parse all bodies + Iterator bodies = xhtmlExt.getBodies(); + StringBuffer messageBuff = new StringBuffer(); + while (bodies.hasNext()) + { + String body = (String)bodies.next(); + messageBuff.append(body); + } + + if(messageBuff.length() > 0) + newMessage = createMessage(messageBuff.toString().getBytes(), + HTML_MIME_TYPE); + } Contact sourceContact = opSetPersPresence.findContactByID(fromUserID); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTypingNotificationsJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTypingNotificationsJabberImpl.java index 3c1584311..ab986fddb 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTypingNotificationsJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTypingNotificationsJabberImpl.java @@ -266,10 +266,12 @@ private class IncomingMessageEventsListener implements MessageEventNotificationListener { public void deliveredNotification(String from, String packetID) - {} + { + } public void displayedNotification(String from, String packetID) - {} + { + } public void composingNotification(String from, String packetID) { @@ -287,7 +289,8 @@ public void composingNotification(String from, String packetID) } public void offlineNotification(String from, String packetID) - {} + { + } public void cancelledNotification(String from, String packetID) { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index 73dd56d54..a1a37bd42 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -569,6 +569,8 @@ protected void initialize(String screenname, // TODO: add the feature, if any, corresponding to IM if someone // knows // supportedFeatures.add(_IM_); + //XHTML-IM + supportedFeatures.add("http://jabber.org/protocol/xhtml-im"); //initialize the Whiteboard operation set OperationSetWhiteboardingJabberImpl whiteboard = diff --git a/src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java index 15d4009df..9d6597771 100644 --- a/src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java @@ -114,7 +114,7 @@ public Message createMessage(byte[] content, String contentType, public Message createMessage(String messageText) { return new MessageRssImpl(messageText, - "text/html", + HTML_MIME_TYPE, DEFAULT_MIME_ENCODING, null); } @@ -400,7 +400,7 @@ public boolean isContentTypeSupported(String contentType) { if(contentType.equals(DEFAULT_MIME_TYPE)) return true; - else if(contentType.equals("text/html")) + else if(contentType.equals(HTML_MIME_TYPE)) return true; else return false; diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java index 5479e9deb..ae5a924fa 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java @@ -32,11 +32,6 @@ public class OperationSetBasicInstantMessagingSipImpl private static final Logger logger = Logger.getLogger(OperationSetBasicInstantMessagingSipImpl.class); - /** - * HTML content type - */ - private static final String CONTENT_TYPE_HTML = "text/html"; - /** * A list of listeners registered for message events. */ @@ -228,8 +223,8 @@ public boolean isOfflineMessagingSupported() */ public boolean isContentTypeSupported(String contentType) { - if(contentType.equals(DEFAULT_MIME_TYPE) || - contentType.equals(CONTENT_TYPE_HTML)) + if(contentType.equals(DEFAULT_MIME_TYPE) + || contentType.equals(HTML_MIME_TYPE)) return true; else return false; diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java index 9e5427636..dc48fc79b 100644 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java @@ -33,11 +33,6 @@ public class OperationSetBasicInstantMessagingYahooImpl private static final Logger logger = Logger.getLogger(OperationSetBasicInstantMessagingYahooImpl.class); - /** - * HTML content type - */ - private static final String CONTENT_TYPE_HTML = "text/html"; - /** * Yahoo has limit of message length. If exceeded * message is not delivered and no notification is received for that. @@ -148,7 +143,7 @@ public boolean isOfflineMessagingSupported() public boolean isContentTypeSupported(String contentType) { if(contentType.equals(DEFAULT_MIME_TYPE) || - contentType.equals(CONTENT_TYPE_HTML)) + contentType.equals(HTML_MIME_TYPE)) return true; else return false; @@ -451,7 +446,7 @@ public void newMailReceived(SessionNewMailEvent ev) Message newMailMessage = new MessageYahooImpl( newMail, - CONTENT_TYPE_HTML, + HTML_MIME_TYPE, DEFAULT_MIME_ENCODING, null); @@ -520,7 +515,7 @@ private void handleNewMessage(SessionEvent ev) //to set all messages html - doesn't affect the appearance of the gui Message newMessage = createMessage( formattedMessage.getBytes(), - CONTENT_TYPE_HTML, + HTML_MIME_TYPE, DEFAULT_MIME_ENCODING, null); diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java b/src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java index d53655211..ff7cb1f18 100644 --- a/src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java +++ b/src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java @@ -25,6 +25,11 @@ public interface OperationSetBasicInstantMessaging * Default mime type for outgoing messages. */ public static final String DEFAULT_MIME_TYPE = "text/plain"; + + /** + * HTML mime type for use with messages using html. + */ + public static final String HTML_MIME_TYPE = "text/html"; /** * Create a Message instance for sending arbitrary MIME-encoding content. diff --git a/src/net/java/sip/communicator/util/Html2Text.java b/src/net/java/sip/communicator/util/Html2Text.java new file mode 100644 index 000000000..8e778eafc --- /dev/null +++ b/src/net/java/sip/communicator/util/Html2Text.java @@ -0,0 +1,52 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ + +package net.java.sip.communicator.util; + +import java.io.*; +import javax.swing.text.html.*; +import javax.swing.text.*; + +/** + * A utility class that allows to extract the text content of an html page + * stripped from all formatting tags. + * + * @author Emil Ivov + */ +public class Html2Text +{ + private static final Logger logger + = Logger.getLogger(Html2Text.class); + /** + * The editor kit we use for conversions. + */ + private HTMLEditorKit htmlEditorKit = new HTMLEditorKit(); + + /** + * A utility class that allows to extract the text content of an html page + * stripped from all formatting tags. Method is synchronized to avoid + * concurrent access to the underlying html editor kit. + * + * @param html the html string that we will extract the text from. + * @return the text content of the html parameter. + */ + public synchronized String extractText(String html) + { + Document doc = htmlEditorKit.createDefaultDocument(); + + try + { + htmlEditorKit.read(new StringReader(html), doc, 0); + return doc.getText(1, doc.getLength() - 1); + } + catch (Exception exc) + { + logger.info("Failed to extract plain text from html="+html, exc); + return html; + } + } +} diff --git a/src/net/java/sip/communicator/util/util.manifest.mf b/src/net/java/sip/communicator/util/util.manifest.mf index 07157f60e..0d64b9a10 100644 --- a/src/net/java/sip/communicator/util/util.manifest.mf +++ b/src/net/java/sip/communicator/util/util.manifest.mf @@ -13,6 +13,8 @@ Import-Package: org.osgi.framework, javax.xml.transform.stream, javax.naming, javax.naming.directory, + javax.swing.text.html, + javax.swing.text, net.java.sip.communicator.util.xml, net.java.sip.communicator.util, Export-Package: net.java.sip.communicator.util.xml,