diff options
author | Danny van Heumen <danny@dannyvanheumen.nl> | 2014-08-31 22:55:45 +0200 |
---|---|---|
committer | Danny van Heumen <danny@dannyvanheumen.nl> | 2014-09-30 20:28:56 +0200 |
commit | f970c50c8e0f85f8502d32e98c39d1d5b5b9f29c (patch) | |
tree | 27014fd0bc9579247ec0ed5b86ada649cc721c3a /src/net/java/sip | |
parent | 9c302d8c96897337b12b60afa9d1d0d5c633f76b (diff) | |
download | jitsi-f970c50c8e0f85f8502d32e98c39d1d5b5b9f29c.zip jitsi-f970c50c8e0f85f8502d32e98c39d1d5b5b9f29c.tar.gz jitsi-f970c50c8e0f85f8502d32e98c39d1d5b5b9f29c.tar.bz2 |
Initial support for updated otr4j with support for outgoing message fragmentation.
Modifications include the following:
- Updated otr4j which includes support for fragmentation of outgoing
messages. The modifications to otr4j to enable outgoing message
fragmentation includes breaking the API such that we are able to
return more than 1 message after it has been transformed.
(Corresponding modifications have been made to
AbstractOperationSetBasicInstantMessaging to facilitate the new API.)
- Fixed IRC implementation for OperationSetInstantMessageTransform.
- Modified AbstractOperationSetBasicInstantMessaging to handle multiple
Events returning from a call to messageTransform.
- Modified OperationSetBasicInstantMessaging implementations to
correspond to changes in AbstractOSBIM.
- OTR plugin has been modified to implement the newly added
getFragmenterInstructions method which is used to query instructions
on desired fragmentation behaviour.
- As a temporary solution, a hard dependency has been added to IRC
library such that I'm able to test fragmentation behaviour in a real
use case until an OperationSet is defined that can be used to query
for Instant Messaging transport parameters necessary to determine
appropriate fragmentation instructions.
Diffstat (limited to 'src/net/java/sip')
14 files changed, 552 insertions, 284 deletions
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 b4e08e7..5ef2532 100644 --- a/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java @@ -172,29 +172,35 @@ public class OperationSetBasicInstantMessagingIcqImpl MessageDeliveredEvent msgDeliveryPendingEvt = new MessageDeliveredEvent(message, to); - msgDeliveryPendingEvt = this.messageDeliveryPendingTransform(msgDeliveryPendingEvt); - - if (msgDeliveryPendingEvt == null) + MessageDeliveredEvent[] msgDeliveryPendingEvts = this.messageDeliveryPendingTransform(msgDeliveryPendingEvt); + if (msgDeliveryPendingEvts == null || msgDeliveryPendingEvts.length == 0) return; - String transformedContent = msgDeliveryPendingEvt.getSourceMessage().getContent(); - - if (to.getPresenceStatus().isOnline()) + for (MessageDeliveredEvent pendingEvt : msgDeliveryPendingEvts) { - //do not add the conversation listener in here. we'll add it - //inside the icbm listener - imConversation.sendMessage(new SimpleMessage(transformedContent)); - } - else - imConversation.sendMessage(new SimpleMessage(transformedContent), true); + String transformedContent = + pendingEvt.getSourceMessage().getContent(); - MessageDeliveredEvent msgDeliveredEvt - = new MessageDeliveredEvent(message, to); + if (to.getPresenceStatus().isOnline()) + { + // do not add the conversation listener in here. we'll add it + // inside the icbm listener + imConversation + .sendMessage(new SimpleMessage(transformedContent)); + } + else + imConversation.sendMessage( + new SimpleMessage(transformedContent), true); - // msgDeliveredEvt = this.messageDeliveredTransform(msgDeliveredEvt); + MessageDeliveredEvent msgDeliveredEvt = + new MessageDeliveredEvent(message, to); - if (msgDeliveredEvt != null) - fireMessageEvent(msgDeliveredEvt); + // msgDeliveredEvt = + // this.messageDeliveredTransform(msgDeliveredEvt); + + if (msgDeliveredEvt != null) + fireMessageEvent(msgDeliveredEvt); + } } /** diff --git a/src/net/java/sip/communicator/impl/protocol/irc/IrcStack.java b/src/net/java/sip/communicator/impl/protocol/irc/IrcStack.java index b819131..22aab66 100644 --- a/src/net/java/sip/communicator/impl/protocol/irc/IrcStack.java +++ b/src/net/java/sip/communicator/impl/protocol/irc/IrcStack.java @@ -152,6 +152,12 @@ public class IrcStack private PresenceManager presence = null; /** + * The local user's identity as it will be used in server-client + * communication for sent messages. + */ + private Identity identity; + + /** * The cached channel list. * * Contained inside a simple container object in order to lock the container @@ -253,6 +259,8 @@ public class IrcStack connectSynchronized(); + queryIdentity(); + // TODO Read IRC network capabilities based on RPL_ISUPPORT (005) // replies if available. This information should be available in // irc-api if possible. @@ -363,6 +371,27 @@ public class IrcStack } /** + * Issue WHOIS query to discover identity as seen by the server. + */ + private void queryIdentity() + { + this.session.get().rawMessage( + "WHOIS " + this.connectionState.getNickname()); + } + + /** + * Get the current identity string, based on nick, user and host of local + * user. + * + * @return returns identity string + */ + public String getIdentityString() + { + final String currentNick = this.connectionState.getNickname(); + return this.identity.getIdentityString(currentNick); + } + + /** * Create a custom SSL context for this particular server. * * @param hostname host name of the host we are connecting to such that we @@ -1022,16 +1051,12 @@ public class IrcStack { final IRCApi irc = this.session.get(); irc.message(target, message.getContent()); - IrcStack.this.provider.getBasicInstantMessaging() - .fireMessageDelivered(message, contact); LOGGER.trace("Message delivered to server successfully."); } catch (RuntimeException e) { - IrcStack.this.provider.getBasicInstantMessaging() - .fireMessageDeliveryFailed(message, contact, - MessageDeliveryFailedEvent.NETWORK_FAILURE); LOGGER.trace("Failed to deliver message: " + e.getMessage(), e); + throw e; } } @@ -1185,6 +1210,11 @@ public class IrcStack */ private static final int RPL_LISTEND = IRCServerNumerics.CHANNEL_NICKS_END_OF_LIST; + + /** + * Reply for WHOIS query. + */ + private static final int IRC_RPL_WHOISUSER = 311; /** * IRCApi instance. @@ -1359,6 +1389,29 @@ public class IrcStack .fireMessageReceived(awayMessage, awayUser); break; + case IRC_RPL_WHOISUSER: + final String whoismsg = msg.getText(); + final int endNickIndex = whoismsg.indexOf(' '); + final String nick = whoismsg.substring(0, endNickIndex); + if (!IrcStack.this.connectionState.getNickname().equals(nick)) + { + // We need WHOIS info on ourselves to discover our identity + // on the IRC server. So skip other WHOIS replies. + return; + } + final int endUserIndex = + whoismsg.indexOf(' ', endNickIndex + 1); + final int endHostIndex = + whoismsg.indexOf(' ', endUserIndex + 1); + final String user = + whoismsg.substring(endNickIndex + 1, endUserIndex); + final String host = + whoismsg.substring(endUserIndex + 1, endHostIndex); + LOGGER.debug(String.format("Current identity: %s!%s@%s", + IrcStack.this.connectionState.getNickname(), user, host)); + IrcStack.this.identity = new IrcStack.Identity(user, host); + break; + default: if (LOGGER.isTraceEnabled()) { @@ -2592,4 +2645,49 @@ public class IrcStack LOGGER.debug("Old channel list cache has been cleared."); } } + + /** + * Storage container for identity components. + * + * IRC identity components user and host are stored. The nick name component + * isn't stored, because it changes too frequently. When getting the + * identity string, the nick name component is provided at calling time. + * + * @author Danny van Heumen + */ + private static final class Identity + { + /** + * User name. + */ + private final String user; + + /** + * Host name. + */ + private final String host; + + /** + * Constructor. + * + * @param user user + * @param host host + */ + private Identity(final String user, final String host) + { + this.user = user; + this.host = host; + } + + /** + * Get identity string. + * + * @param currentNick the current nick + * @return returns identity string + */ + public String getIdentityString(final String currentNick) + { + return String.format("%s!%s@%s", currentNick, this.user, this.host); + } + } } diff --git a/src/net/java/sip/communicator/impl/protocol/irc/OperationSetBasicInstantMessagingIrcImpl.java b/src/net/java/sip/communicator/impl/protocol/irc/OperationSetBasicInstantMessagingIrcImpl.java index 09a0b79..6130263 100644 --- a/src/net/java/sip/communicator/impl/protocol/irc/OperationSetBasicInstantMessagingIrcImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/irc/OperationSetBasicInstantMessagingIrcImpl.java @@ -6,6 +6,8 @@ package net.java.sip.communicator.impl.protocol.irc; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; /** * Implementation of Basic Instant Messaging as utilized for IRC private @@ -17,6 +19,11 @@ public class OperationSetBasicInstantMessagingIrcImpl extends AbstractOperationSetBasicInstantMessaging { /** + * Logger. + */ + private static final Logger LOGGER = Logger.getLogger(OperationSetBasicInstantMessagingIrcImpl.class); + + /** * IRC protocol provider service. */ private final ProtocolProviderServiceIrcImpl provider; @@ -59,28 +66,74 @@ public class OperationSetBasicInstantMessagingIrcImpl * Send instant message. * * @param to contact to send message to - * @param message message to send + * @param original message to send * @throws IllegalStateException in case of bad internal state * @throws IllegalArgumentException in case invalid arguments have been * passed */ @Override - public void sendInstantMessage(final Contact to, final Message message) + public void sendInstantMessage(final Contact to, final Message original) throws IllegalStateException, IllegalArgumentException { + if (!(original instanceof MessageIrcImpl)) + { + LOGGER.error("Invalid class of Message implementation received. " + + "Not sending message."); + return; + } + // OTR seems to be compatible with the command syntax (starts with '/') // and there were no other obvious problems so we decided to implement // IRC command support for IM infrastructure too. - if (message instanceof MessageIrcImpl - && ((MessageIrcImpl) message).isCommand()) + final MessageDeliveredEvent[] msgDeliveryPendingEvts = + messageDeliveryPendingTransform(new MessageDeliveredEvent(original, + to)); + + try { - this.provider.getIrcStack().command(to, (MessageIrcImpl) message); + for (MessageDeliveredEvent event : msgDeliveryPendingEvts) + { + if (event == null) + { + return; + } + + String transformedContent = + event.getSourceMessage().getContent(); + + // FIXME how to handle HTML content? + + // Note: can't set subject since it leaks information while + // message content actually gets encoded. + MessageIrcImpl message = this.createMessage(transformedContent, + original.getContentType(), original.getEncoding(), ""); + + try + { + if (message.isCommand()) + { + this.provider.getIrcStack().command(to, message); + } + else + { + this.provider.getIrcStack().message(to, message); + } + } + catch (RuntimeException e) + { + LOGGER.debug("Failed to deliver (raw) message: " + message); + throw e; + } + } + fireMessageDelivered(original, to); } - else + catch (RuntimeException e) { - this.provider.getIrcStack().message(to, message); + LOGGER.warn("Failed to deliver message: " + original, e); + fireMessageDeliveryFailed(original, to, + MessageDeliveryFailedEvent.NETWORK_FAILURE); } } diff --git a/src/net/java/sip/communicator/impl/protocol/irc/irc.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/irc/irc.provider.manifest.mf index a28001a..2de13b0 100644 --- a/src/net/java/sip/communicator/impl/protocol/irc/irc.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/irc/irc.provider.manifest.mf @@ -3,7 +3,8 @@ Bundle-Name: Irc Protocol Provider Bundle-Description: A bundle providing support for the Irc protocol. Bundle-Vendor: jitsi.org Bundle-Version: 0.0.1 -Bundle-SymbolicName: net.java.sip.communicator.protocol.irc +Bundle-SymbolicName: net.java.sip.communicator.impl.protocol.irc +Export-Package: net.java.sip.communicator.impl.protocol.irc Import-Package: org.osgi.framework, org.jitsi.service.configuration, org.jitsi.service.resources, 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 0b53ccb..486d49f 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java @@ -442,62 +442,60 @@ public class OperationSetBasicInstantMessagingJabberImpl MessageDeliveredEvent msgDeliveryPendingEvt = new MessageDeliveredEvent(message, to, toResource); - msgDeliveryPendingEvt - = messageDeliveryPendingTransform(msgDeliveryPendingEvt); + MessageDeliveredEvent[] transformedEvents = messageDeliveryPendingTransform(msgDeliveryPendingEvt); - if (msgDeliveryPendingEvt == null) + if (transformedEvents == null || transformedEvents.length == 0) return null; - String content = msgDeliveryPendingEvt - .getSourceMessage().getContent(); - - if(message.getContentType().equals(HTML_MIME_TYPE)) + for (MessageDeliveredEvent event : transformedEvents) { - msg.setBody(Html2Text.extractText(content)); + String content = event.getSourceMessage().getContent(); - // Check if the other user supports XHTML messages - // make sure we use our discovery manager as it caches calls - if(jabberProvider.isFeatureListSupported( - toJID, - HTML_NAMESPACE)) + if (message.getContentType().equals(HTML_MIME_TYPE)) { - // 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.setBody(Html2Text.extractText(content)); - //msg.addExtension(new Version()); + // Check if the other user supports XHTML messages + // make sure we use our discovery manager as it caches calls + if (jabberProvider + .isFeatureListSupported(toJID, HTML_NAMESPACE)) + { + // 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); + } - if(msgDeliveryPendingEvt.isMessageEncrypted()) - { - msg.addExtension(new CarbonPacketExtension.PrivateExtension()); - } + // msg.addExtension(new Version()); - MessageEventManager. - addNotificationsRequests(msg, true, false, false, true); + if (event.isMessageEncrypted()) + { + msg.addExtension(new CarbonPacketExtension.PrivateExtension()); + } - String threadID = getThreadIDForAddress(toJID); - if(threadID == null) - threadID = nextThreadID(); + MessageEventManager.addNotificationsRequests(msg, true, false, + false, true); - msg.setThread(threadID); - msg.setType(org.jivesoftware.smack.packet.Message.Type.chat); - msg.setFrom(jabberProvider.getConnection().getUser()); + String threadID = getThreadIDForAddress(toJID); + if (threadID == null) + threadID = nextThreadID(); - jabberProvider.getConnection().sendPacket(msg); + msg.setThread(threadID); + msg.setType(org.jivesoftware.smack.packet.Message.Type.chat); + msg.setFrom(jabberProvider.getConnection().getUser()); - putJidForAddress(toJID, threadID); + jabberProvider.getConnection().sendPacket(msg); - MessageDeliveredEvent msgDeliveredEvt - = new MessageDeliveredEvent(message, to, toResource); + putJidForAddress(toJID, threadID); + } - // msgDeliveredEvt = messageDeliveredTransform(msgDeliveredEvt); + MessageDeliveredEvent msgDeliveredEvt = + new MessageDeliveredEvent(message, to, toResource); return msgDeliveredEvt; } @@ -1119,6 +1117,7 @@ public class OperationSetBasicInstantMessagingJabberImpl ? "service.gui.NEW_GMAIL_MANY_FOOTER" : "service.gui.NEW_GMAIL_FOOTER"; + // FIXME Escape HTML! String newMailHeader = JabberActivator.getResources().getI18NString( resourceHeaderKey, new String[] diff --git a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java index e614586..e09d9c1 100644 --- a/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java @@ -113,7 +113,7 @@ public class OperationSetBasicInstantMessagingMsnImpl * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an * instance of ContactImpl. */ - public void sendInstantMessage(final Contact to, Message message) + public void sendInstantMessage(final Contact to, final Message message) throws IllegalStateException, IllegalArgumentException { assertConnected(); @@ -123,29 +123,30 @@ public class OperationSetBasicInstantMessagingMsnImpl "The specified contact is not an MSN contact." + to); - final MessageDeliveredEvent msgDeliveryPendingEvt - = messageDeliveryPendingTransform( - new MessageDeliveredEvent(message, to)); + MessageDeliveredEvent[] transformedEvents = messageDeliveryPendingTransform( + new MessageDeliveredEvent(message, to)); - if (msgDeliveryPendingEvt == null) + if (transformedEvents == null || transformedEvents.length == 0) return; - MessageDeliveredEvent msgDeliveredEvt - = new MessageDeliveredEvent(message, to); + MessageDeliveredEvent msgDeliveredEvt = + new MessageDeliveredEvent(message, to); fireMessageEvent(msgDeliveredEvt); - // send message in separate thread so we won't block ui if - // it takes time. - if(senderThread == null) + // send message in separate thread so we won't block ui if it takes + // time. + if (senderThread == null) { senderThread = new SenderThread(); senderThread.start(); } - senderThread.sendMessage( - (ContactMsnImpl)to, - msgDeliveryPendingEvt.getSourceMessage().getContent()); + for (MessageDeliveredEvent event : transformedEvents) + { + senderThread.sendMessage((ContactMsnImpl) to, event + .getSourceMessage().getContent()); + } } /** @@ -323,6 +324,7 @@ public class OperationSetBasicInstantMessagingMsnImpl logger.warn("Failed to decode the subject of a new e-mail", ex); } + // FIXME Escape HTML! String messageFrom = message.getFrom(); Message newMailMessage = new MessageMsnImpl( MessageFormat.format( 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 3b40ba8..2e0534a 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java @@ -214,53 +214,44 @@ public class OperationSetBasicInstantMessagingSipImpl } // create the message - Request mes; - try - { - Message transformedMessage = transformSIPMessage(to, message); - mes = createMessageRequest(to, transformedMessage); - } - catch (OperationFailedException ex) + Message[] transformedMessages = transformSIPMessage(to, message); + for (Message msg : transformedMessages) { - logger.error( - "Failed to create the message." - , ex); + Request mes; + try + { + mes = createMessageRequest(to, msg); + } + catch (OperationFailedException ex) + { + logger.error("Failed to create the message.", ex); - fireMessageDeliveryFailed( - message, - to, - MessageDeliveryFailedEvent.INTERNAL_ERROR); - return; - } + fireMessageDeliveryFailed(message, to, + MessageDeliveryFailedEvent.INTERNAL_ERROR); + continue; + } - try - { - sendMessageRequest(mes, to, message); - } - catch(TransactionUnavailableException ex) - { - logger.error( - "Failed to create messageTransaction.\n" - + "This is most probably a network connection error." - , ex); + try + { + sendMessageRequest(mes, to, message); + } + catch (TransactionUnavailableException ex) + { + logger.error("Failed to create messageTransaction.\n" + + "This is most probably a network connection error.", ex); - fireMessageDeliveryFailed( - message, - to, - MessageDeliveryFailedEvent.NETWORK_FAILURE); - return; - } - catch(SipException ex) - { - logger.error( - "Failed to send the message." - , ex); + fireMessageDeliveryFailed(message, to, + MessageDeliveryFailedEvent.NETWORK_FAILURE); + continue; + } + catch (SipException ex) + { + logger.error("Failed to send the message.", ex); - fireMessageDeliveryFailed( - message, - to, - MessageDeliveryFailedEvent.INTERNAL_ERROR); - return; + fireMessageDeliveryFailed(message, to, + MessageDeliveryFailedEvent.INTERNAL_ERROR); + continue; + } } } @@ -482,28 +473,36 @@ public class OperationSetBasicInstantMessagingSipImpl * * @return The new transformed <tt>Message</tt> */ - private Message transformSIPMessage(Contact to, Message message) + private Message[] transformSIPMessage(final Contact to, + final Message message) { MessageDeliveredEvent msgDeliveryPendingEvt = new MessageDeliveredEvent(message, to); - msgDeliveryPendingEvt - = messageDeliveryPendingTransform(msgDeliveryPendingEvt); + MessageDeliveredEvent[] msgDeliveryPendingEvts = + messageDeliveryPendingTransform(msgDeliveryPendingEvt); - if (msgDeliveryPendingEvt == null) - return null; - - String content = msgDeliveryPendingEvt.getSourceMessage().getContent(); + if (msgDeliveryPendingEvts == null + || msgDeliveryPendingEvts.length == 0) + { + return new Message[0]; + } OperationSetBasicInstantMessaging opSetBasicIM = (OperationSetBasicInstantMessaging) sipProvider .getSupportedOperationSets().get( OperationSetBasicInstantMessaging.class.getName()); - Message transformedMesssage = - opSetBasicIM.createMessage(content, message.getContentType(), - message.getEncoding(), message.getSubject()); - - return transformedMesssage; + Message[] transformedMessages = + new Message[msgDeliveryPendingEvts.length]; + for (int i = 0; i < msgDeliveryPendingEvts.length; i++) + { + MessageDeliveredEvent event = msgDeliveryPendingEvts[i]; + String content = event.getSourceMessage().getContent(); + transformedMessages[i] = + opSetBasicIM.createMessage(content, message.getContentType(), + message.getEncoding(), message.getSubject()); + } + return transformedMessages; } /** 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 ab9ba26..eb53d4a 100644 --- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java @@ -150,56 +150,55 @@ public class OperationSetBasicInstantMessagingYahooImpl { String toUserID = ((ContactYahooImpl) to).getID(); - MessageDeliveredEvent msgDeliveryPendingEvt - = new MessageDeliveredEvent( - message, to, new Date()); + MessageDeliveredEvent msgDeliveryPendingEvt = + new MessageDeliveredEvent(message, to, new Date()); - msgDeliveryPendingEvt = messageDeliveryPendingTransform(msgDeliveryPendingEvt); + MessageDeliveredEvent[] msgDeliveryPendingEvts = messageDeliveryPendingTransform(msgDeliveryPendingEvt); - if (msgDeliveryPendingEvt == null) + if (msgDeliveryPendingEvts == null || msgDeliveryPendingEvts.length == 0) return; - byte[] msgBytesToBeSent = msgDeliveryPendingEvt.getSourceMessage(). - getContent().trim().getBytes("UTF-8"); - - // split the message in parts with max allowed length - // and send them all - do + for (MessageDeliveredEvent event : msgDeliveryPendingEvts) { - if(msgBytesToBeSent.length > MAX_MESSAGE_LENGTH) - { - byte[] tmp1 = new byte[MAX_MESSAGE_LENGTH]; - System.arraycopy(msgBytesToBeSent, - 0, tmp1, 0, MAX_MESSAGE_LENGTH); - - byte[] tmp2 = - new byte[msgBytesToBeSent.length - MAX_MESSAGE_LENGTH]; - System.arraycopy(msgBytesToBeSent, - MAX_MESSAGE_LENGTH, tmp2, 0, tmp2.length); + byte[] msgBytesToBeSent = + event.getSourceMessage().getContent().trim() + .getBytes("UTF-8"); - msgBytesToBeSent = tmp2; - - yahooProvider.getYahooSession().sendMessage( - toUserID, - new String(tmp1, "UTF-8")); - } - else + // split the message in parts with max allowed length + // and send them all + do { - yahooProvider.getYahooSession().sendMessage( - toUserID, - new String(msgBytesToBeSent, "UTF-8")); + if (msgBytesToBeSent.length > MAX_MESSAGE_LENGTH) + { + byte[] tmp1 = new byte[MAX_MESSAGE_LENGTH]; + System.arraycopy(msgBytesToBeSent, 0, tmp1, 0, + MAX_MESSAGE_LENGTH); + + byte[] tmp2 = + new byte[msgBytesToBeSent.length + - MAX_MESSAGE_LENGTH]; + System.arraycopy(msgBytesToBeSent, MAX_MESSAGE_LENGTH, + tmp2, 0, tmp2.length); + + msgBytesToBeSent = tmp2; + + yahooProvider.getYahooSession().sendMessage(toUserID, + new String(tmp1, "UTF-8")); + } + else + { + yahooProvider.getYahooSession().sendMessage(toUserID, + new String(msgBytesToBeSent, "UTF-8")); + } + + MessageDeliveredEvent msgDeliveredEvt = + new MessageDeliveredEvent(message, to, new Date()); + + if (msgDeliveredEvt != null) + fireMessageEvent(msgDeliveredEvt); } - - MessageDeliveredEvent msgDeliveredEvt - = new MessageDeliveredEvent( - message, to, new Date()); - - // msgDeliveredEvt = messageDeliveredTransform(msgDeliveredEvt); - - if (msgDeliveredEvt != null) - fireMessageEvent(msgDeliveredEvt); + while (msgBytesToBeSent.length > MAX_MESSAGE_LENGTH); } - while(msgBytesToBeSent.length > MAX_MESSAGE_LENGTH); } catch (IOException ex) { @@ -210,8 +209,6 @@ public class OperationSetBasicInstantMessagingYahooImpl to, MessageDeliveryFailedEvent.NETWORK_FAILURE); - // evt = messageDeliveryFailedTransform(evt); - if (evt != null) fireMessageEvent(evt); } @@ -374,6 +371,7 @@ public class OperationSetBasicInstantMessagingYahooImpl + yahooMailLogon + "\">" + yahooMailLogon + "</a>"; + // FIXME Escape HTML! String newMail = YahooActivator.getResources().getI18NString( "service.gui.NEW_MAIL", new String[]{ev.getFrom(), diff --git a/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java b/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java index f447de0..7c94eac 100644 --- a/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java +++ b/src/net/java/sip/communicator/plugin/otr/OtrTransformLayer.java @@ -61,7 +61,7 @@ public class OtrTransformLayer /* * Implements TransformLayer#messageDeliveryPending(MessageDeliveredEvent). */ - public MessageDeliveredEvent messageDeliveryPending( + public MessageDeliveredEvent[] messageDeliveryPending( MessageDeliveredEvent evt) { Contact contact = evt.getDestinationContact(); @@ -76,47 +76,57 @@ public class OtrTransformLayer if (!policy.getEnableManual() && sessionStatus != ScSessionStatus.ENCRYPTED && sessionStatus != ScSessionStatus.FINISHED) - return evt; + return new MessageDeliveredEvent[] {evt}; // If this is a message otr4j injected earlier, return the event as is. if (OtrActivator.scOtrEngine.isMessageUIDInjected(evt .getSourceMessage().getMessageUID())) - return evt; + return new MessageDeliveredEvent[] {evt}; // Process the outgoing message. String msgContent = evt.getSourceMessage().getContent(); - String processedMessageContent = + String[] processedMessageContent = OtrActivator.scOtrEngine.transformSending(otrContact, msgContent); if (processedMessageContent == null - || processedMessageContent.length() < 1) - return null; - - if (processedMessageContent.equals(msgContent)) - return evt; - - // Forge a new message based on the new contents. - OperationSetBasicInstantMessaging imOpSet = - contact.getProtocolProvider().getOperationSet( - OperationSetBasicInstantMessaging.class); - Message processedMessage = - imOpSet.createMessage( - processedMessageContent, - evt.getSourceMessage().getContentType(), - evt.getSourceMessage().getEncoding(), - evt.getSourceMessage().getSubject()); + || processedMessageContent.length <= 0 + || processedMessageContent[0].length() < 1) + return new MessageDeliveredEvent[0]; - // Create a new event and return. - MessageDeliveredEvent processedEvent = - new MessageDeliveredEvent(processedMessage, contact, evt - .getTimestamp()); + if (processedMessageContent.length == 1 + && processedMessageContent[0].equals(msgContent)) + return new MessageDeliveredEvent[] {evt}; - if(processedMessage.getContent().contains(SerializationConstants.HEAD)) + final MessageDeliveredEvent[] processedEvents = + new MessageDeliveredEvent[processedMessageContent.length]; + for (int i = 0; i < processedMessageContent.length; i++) { - processedEvent.setMessageEncrypted(true); + final String fragmentContent = processedMessageContent[i]; + // Forge a new message based on the new contents. + OperationSetBasicInstantMessaging imOpSet = + contact.getProtocolProvider().getOperationSet( + OperationSetBasicInstantMessaging.class); + Message processedMessage = + imOpSet.createMessage(fragmentContent, evt + .getSourceMessage().getContentType(), evt + .getSourceMessage().getEncoding(), evt.getSourceMessage() + .getSubject()); + + // Create a new event and return. + final MessageDeliveredEvent processedEvent = + new MessageDeliveredEvent(processedMessage, contact, + evt.getTimestamp()); + + if (processedMessage.getContent().contains( + SerializationConstants.HEAD)) + { + processedEvent.setMessageEncrypted(true); + } + + processedEvents[i] = processedEvent; } - return processedEvent; + return processedEvents; } /* diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java index f9ee909..5bd9688 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngine.java @@ -70,7 +70,7 @@ public interface ScOtrEngine * @param content the original message content. * @return the transformed message content. */ - public abstract String transformSending(OtrContact contact, String content); + public abstract String[] transformSending(OtrContact contact, String content); /** * Transforms an incoming message. diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java index a06b61d..f14c2ec 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java @@ -14,6 +14,7 @@ import java.util.concurrent.*; import net.java.otr4j.*;
import net.java.otr4j.crypto.*;
import net.java.otr4j.session.*;
+import net.java.sip.communicator.impl.protocol.irc.*;
import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact;
import net.java.sip.communicator.plugin.otr.authdialog.*;
import net.java.sip.communicator.service.browserlauncher.*;
@@ -420,6 +421,44 @@ public class ScOtrEngineImpl message,
OperationSetBasicInstantMessaging.HTML_MIME_TYPE);
}
+
+ /**
+ * Provide fragmenter instructions according to the Instant Messaging
+ * transport channel of the contact's protocol.
+ */
+ @Override
+ public FragmenterInstructions getFragmenterInstructions(
+ final SessionID sessionID)
+ {
+ final OtrContact otrContact = getOtrContact(sessionID);
+ // FIXME Change this into querying an Operation Set that provides
+ // Instant Message medium/transport information that we can use to
+ // determine fragmentation parameters.
+ if (ProtocolNames.IRC.equals(otrContact.contact
+ .getProtocolProvider().getProtocolName()))
+ {
+ // :<nick>!<user>@<host> PRIVMSG <targetnick> :<message>
+ //
+ // Example:
+ // :ircotrtest!~ircotrtes@77-175-185-165.FTTH.ispfabriek.nl
+ // PRIVMSG test12345abc :
+ final String identity =
+ ((ProtocolProviderServiceIrcImpl) otrContact.contact
+ .getProtocolProvider()).getIrcStack()
+ .getIdentityString();
+ final int size =
+ 510 - (":" + identity + " PRIVMSG "
+ + otrContact.contact.getAddress() + " :").length();
+ return new FragmenterInstructions(
+ FragmenterInstructions.UNLIMITED, size);
+ }
+ else
+ {
+ return new FragmenterInstructions(
+ FragmenterInstructions.UNLIMITED,
+ FragmenterInstructions.UNLIMITED);
+ }
+ }
}
/**
@@ -510,11 +549,11 @@ public class ScOtrEngineImpl private final OtrEngineHost otrEngineHost = new ScOtrEngineHost();
- private final OtrEngine otrEngine;
+ private final OtrSessionManager otrEngine;
public ScOtrEngineImpl()
{
- otrEngine = new OtrEngineImpl(otrEngineHost);
+ otrEngine = new OtrSessionManagerImpl(otrEngineHost);
// Clears the map after previous instance
// This is required because of OSGi restarts in the same VM on Android
@@ -539,13 +578,13 @@ public class ScOtrEngineImpl ScSessionStatus scSessionStatus = getSessionStatus(otrContact);
String message = "";
- switch (otrEngine.getSessionStatus(sessionID))
+ final Session session = otrEngine.getSession(sessionID);
+ switch (session.getSessionStatus())
{
case ENCRYPTED:
scSessionStatus = ScSessionStatus.ENCRYPTED;
scSessionStatusMap.put(sessionID, scSessionStatus);
- PublicKey remotePubKey =
- otrEngine.getRemotePublicKey(sessionID);
+ PublicKey remotePubKey = session.getRemotePublicKey();
String remoteFingerprint = null;
try
@@ -790,7 +829,7 @@ public class ScOtrEngineImpl {
setSessionStatus(otrContact, ScSessionStatus.PLAINTEXT);
- otrEngine.endSession(sessionID);
+ otrEngine.getSession(sessionID).endSession();
}
catch (OtrException e)
{
@@ -926,7 +965,7 @@ public class ScOtrEngineImpl public ScSessionStatus getSessionStatus(OtrContact contact)
{
SessionID sessionID = getSessionID(contact);
- SessionStatus sessionStatus = otrEngine.getSessionStatus(sessionID);
+ SessionStatus sessionStatus = otrEngine.getSession(sessionID).getSessionStatus();
ScSessionStatus scSessionStatus = null;
if (!scSessionStatusMap.containsKey(sessionID))
{
@@ -976,7 +1015,7 @@ public class ScOtrEngineImpl SessionID sessionID = getSessionID(otrContact);
try
{
- otrEngine.refreshSession(sessionID);
+ otrEngine.getSession(sessionID).refreshSession();
}
catch (OtrException e)
{
@@ -1112,7 +1151,7 @@ public class ScOtrEngineImpl try
{
- otrEngine.startSession(sessionID);
+ otrEngine.getSession(sessionID).startSession();
}
catch (OtrException e)
{
@@ -1127,7 +1166,7 @@ public class ScOtrEngineImpl SessionID sessionID = getSessionID(otrContact);
try
{
- return otrEngine.transformReceiving(sessionID, msgText);
+ return otrEngine.getSession(sessionID).transformReceiving(msgText);
}
catch (OtrException e)
{
@@ -1138,12 +1177,12 @@ public class ScOtrEngineImpl }
@Override
- public String transformSending(OtrContact otrContact, String msgText)
+ public String[] transformSending(OtrContact otrContact, String msgText)
{
SessionID sessionID = getSessionID(otrContact);
try
{
- return otrEngine.transformSending(sessionID, msgText);
+ return otrEngine.getSession(sessionID).transformSending(msgText);
}
catch (OtrException e)
{
@@ -1280,6 +1319,6 @@ public class ScOtrEngineImpl SessionID sessionID = getSessionID(contact);
- return otrEngine.getOutgoingSession(sessionID);
+ return otrEngine.getSession(sessionID).getOutgoingInstance();
}
}
diff --git a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf index 07807d4..0dbc565 100644 --- a/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf +++ b/src/net/java/sip/communicator/plugin/otr/otr.manifest.mf @@ -33,4 +33,5 @@ Import-Package: org.osgi.framework, org.bouncycastle.crypto.modes,
org.bouncycastle.util,
org.bouncycastle.util.encoders,
- net.java.sip.communicator.service.msghistory
+ net.java.sip.communicator.service.msghistory,
+ net.java.sip.communicator.impl.protocol.irc
diff --git a/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicInstantMessaging.java b/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicInstantMessaging.java index d3c42eb..28ab995 100644 --- a/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicInstantMessaging.java +++ b/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicInstantMessaging.java @@ -205,38 +205,41 @@ public abstract class AbstractOperationSetBasicInstantMessaging } // Transform the event. - try + EventObject[] events = messageTransform(evt, eventType); + for (EventObject event : events) { - evt = messageTransform(evt, eventType); - if (evt == null) - return; - - for (MessageListener listener : listeners) + try { - switch (eventType) + if (event == null) + return; + + for (MessageListener listener : listeners) { - case MessageDelivered: - listener.messageDelivered((MessageDeliveredEvent) evt); - break; - case MessageDeliveryFailed: - listener.messageDeliveryFailed( - (MessageDeliveryFailedEvent)evt); - break; - case MessageReceived: - listener.messageReceived((MessageReceivedEvent) evt); - break; - default: - /* - * We either have nothing to do or we do not know what to - * do. Anyway, we'll silence the compiler. - */ - break; + switch (eventType) + { + case MessageDelivered: + listener.messageDelivered((MessageDeliveredEvent) event); + break; + case MessageDeliveryFailed: + listener + .messageDeliveryFailed((MessageDeliveryFailedEvent) event); + break; + case MessageReceived: + listener.messageReceived((MessageReceivedEvent) event); + break; + default: + /* + * We either have nothing to do or we do not know what + * to do. Anyway, we'll silence the compiler. + */ + break; + } } } - } - catch (Throwable e) - { - logger.error("Error delivering message", e); + catch (Throwable e) + { + logger.error("Error delivering message", e); + } } } @@ -268,18 +271,44 @@ public abstract class AbstractOperationSetBasicInstantMessaging } } - public MessageDeliveredEvent messageDeliveryPendingTransform( - MessageDeliveredEvent evt) + /** + * Messages pending delivery to be transformed. + * + * @param evt the message delivery event + * @return returns message delivery events + */ + public MessageDeliveredEvent[] messageDeliveryPendingTransform( + final MessageDeliveredEvent evt) { - return (MessageDeliveredEvent) messageTransform( + EventObject[] transformed = messageTransform( evt, MessageEventType.MessageDeliveryPending); - } - private EventObject messageTransform( EventObject evt, - MessageEventType eventType){ + final int size = transformed.length; + MessageDeliveredEvent[] events = + new MessageDeliveredEvent[size]; + System.arraycopy(transformed, 0, events, 0, size); + return events; + } + /** + * Transform provided source event by processing transform layers in + * sequence. + * + * @param evt the source event to transform + * @param eventType the event type of the source event + * @return returns the resulting (transformed) events, if any. (I.e. an + * array of 0 or more size containing events.) + */ + private EventObject[] messageTransform(final EventObject evt, + final MessageEventType eventType) + { + if (evt == null) + { + return new EventObject[0]; + } ProtocolProviderService protocolProvider; - switch (eventType){ + switch (eventType) + { case MessageDelivered: protocolProvider = ((MessageDeliveredEvent) evt) @@ -301,7 +330,7 @@ public abstract class AbstractOperationSetBasicInstantMessaging .getSourceContact().getProtocolProvider(); break; default: - return evt; + return new EventObject[] {evt}; } OperationSetInstantMessageTransformImpl opSetMessageTransform @@ -309,49 +338,81 @@ public abstract class AbstractOperationSetBasicInstantMessaging .getOperationSet(OperationSetInstantMessageTransform.class); if (opSetMessageTransform == null) - return evt; - + return new EventObject[] {evt}; + + // 'current' contains the events that need to be transformed. It should + // not contain null values. + final LinkedList<EventObject> current = new LinkedList<EventObject>(); + // Add source event as start of transformation. + current.add(evt); + // 'next' contains the resulting events after transformation in the + // current iteration. It should not contain null values. + final LinkedList<EventObject> next = new LinkedList<EventObject>(); for (Map.Entry<Integer, Vector<TransformLayer>> entry : opSetMessageTransform.transformLayers.entrySet()) { for (TransformLayer transformLayer : entry.getValue()) { - if (evt != null){ - switch (eventType){ + next.clear(); + while (!current.isEmpty()) + { + final EventObject event = current.remove(); + switch (eventType) + { case MessageDelivered: - evt - = transformLayer - .messageDelivered((MessageDeliveredEvent)evt); + MessageDeliveredEvent transformedDelivered = + transformLayer.messageDelivered( + (MessageDeliveredEvent) event); + if (transformedDelivered != null) + { + next.add(transformedDelivered); + } break; case MessageDeliveryPending: - evt - = transformLayer - .messageDeliveryPending( - (MessageDeliveredEvent)evt); + MessageDeliveredEvent[] evts = transformLayer + .messageDeliveryPending( + (MessageDeliveredEvent) event); + for (MessageDeliveredEvent mde : evts) + { + if (mde != null) + { + next.add(mde); + } + } break; case MessageDeliveryFailed: - evt - = transformLayer - .messageDeliveryFailed( - (MessageDeliveryFailedEvent)evt); + MessageDeliveryFailedEvent transformedDeliveryFailed = + transformLayer.messageDeliveryFailed( + (MessageDeliveryFailedEvent) event); + if (transformedDeliveryFailed != null) + { + next.add(transformedDeliveryFailed); + } break; case MessageReceived: - evt - = transformLayer - .messageReceived((MessageReceivedEvent)evt); + MessageReceivedEvent transformedReceived = + transformLayer + .messageReceived((MessageReceivedEvent) event); + if (transformedReceived != null) + { + next.add(transformedReceived); + } break; default: + next.add(event); /* - * We either have nothing to do or we do not know what - * to do. Anyway, we'll silence the compiler. + * We either have nothing to do or we do not know + * what to do. Anyway, we'll silence the compiler. */ break; } } + // Set events for next round of transformations. + current.addAll(next); } } - return evt; + return current.toArray(new EventObject[current.size()]); } /** diff --git a/src/net/java/sip/communicator/service/protocol/TransformLayer.java b/src/net/java/sip/communicator/service/protocol/TransformLayer.java index fb48f6c..f9615bd 100644 --- a/src/net/java/sip/communicator/service/protocol/TransformLayer.java +++ b/src/net/java/sip/communicator/service/protocol/TransformLayer.java @@ -61,12 +61,13 @@ public interface TransformLayer * @param evt the MessageDeliveredEvent containing the id of the message * that has caused the event. * - * @return an instance of a (possibly new) <tt>MessageDeliveredEvent</tt> - * instance containing the transformed message or <tt>null</tt> if the - * <tt>TransportLayer</tt> has determined that this message event should not - * be delivered to the upper layers. + * @return a number of instances of (possibly new) + * <tt>MessageDeliveredEvent</tt> instances containing the transformed + * message(s) or an empty array if the <tt>TransportLayer</tt> has + * determined that there are no message event that should be delivered to + * the upper layers. */ - public MessageDeliveredEvent messageDeliveryPending(MessageDeliveredEvent evt); + public MessageDeliveredEvent[] messageDeliveryPending(MessageDeliveredEvent evt); /** * Called when the underlying implementation has received an indication |