|
|
|
|
@ -33,6 +33,7 @@
|
|
|
|
|
* @author Matthieu Helleringer
|
|
|
|
|
* @author Alain Knaebel
|
|
|
|
|
* @author Emil Ivov
|
|
|
|
|
* @author Hristo Terezov
|
|
|
|
|
*/
|
|
|
|
|
public class OperationSetBasicInstantMessagingJabberImpl
|
|
|
|
|
extends AbstractOperationSetBasicInstantMessaging
|
|
|
|
|
@ -56,9 +57,14 @@ public class OperationSetBasicInstantMessagingJabberImpl
|
|
|
|
|
* target a specific resource (rather than sending a message to all logged
|
|
|
|
|
* instances of a user).
|
|
|
|
|
*/
|
|
|
|
|
private Map<String, TargetAddress> jids
|
|
|
|
|
= new Hashtable<String, TargetAddress>();
|
|
|
|
|
private Map<String, StoredThreadID> jids
|
|
|
|
|
= new Hashtable<String, StoredThreadID>();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The most recent full JID used for the contact address.
|
|
|
|
|
*/
|
|
|
|
|
private Map<String, String> recentJIDForAddress
|
|
|
|
|
= new Hashtable<String, String>();
|
|
|
|
|
/**
|
|
|
|
|
* The smackMessageListener instance listens for incoming messages.
|
|
|
|
|
* Keep a reference of it so if anything goes wrong we don't add
|
|
|
|
|
@ -70,18 +76,27 @@ public class OperationSetBasicInstantMessagingJabberImpl
|
|
|
|
|
* Contains the complete jid of a specific user and the time that it was
|
|
|
|
|
* last used so that we could remove it after a certain point.
|
|
|
|
|
*/
|
|
|
|
|
public static class TargetAddress
|
|
|
|
|
public static class StoredThreadID
|
|
|
|
|
{
|
|
|
|
|
/** The last complete JID (including resource) that we got a msg from*/
|
|
|
|
|
String jid;
|
|
|
|
|
|
|
|
|
|
/** The time that we last sent or received a message from this jid */
|
|
|
|
|
long lastUpdatedTime;
|
|
|
|
|
|
|
|
|
|
/** The last chat used, this way we will reuse the thread-id */
|
|
|
|
|
Chat chat;
|
|
|
|
|
String threadID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A prefix helps to make sure that thread ID's are unique across mutliple
|
|
|
|
|
* instances.
|
|
|
|
|
*/
|
|
|
|
|
private static String prefix = StringUtils.randomString(5);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Keeps track of the current increment, which is appended to the prefix to
|
|
|
|
|
* forum a unique thread ID.
|
|
|
|
|
*/
|
|
|
|
|
private static long id = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The number of milliseconds that we preserve threads with no traffic
|
|
|
|
|
* before considering them dead.
|
|
|
|
|
@ -266,11 +281,7 @@ public boolean isContentTypeSupported(String contentType, Contact contact)
|
|
|
|
|
return true;
|
|
|
|
|
else if(contentType.equals(HTML_MIME_TYPE))
|
|
|
|
|
{
|
|
|
|
|
String toJID = null;
|
|
|
|
|
|
|
|
|
|
TargetAddress ta = getJidForAddress(contact.getAddress());
|
|
|
|
|
if(ta != null)
|
|
|
|
|
toJID = ta.jid;
|
|
|
|
|
String toJID = recentJIDForAddress.get(contact.getAddress());
|
|
|
|
|
|
|
|
|
|
if (toJID == null)
|
|
|
|
|
toJID = contact.getAddress();
|
|
|
|
|
@ -283,74 +294,25 @@ else if(contentType.equals(HTML_MIME_TYPE))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a reference to an open chat with the specified
|
|
|
|
|
* <tt>jid</tt> if one exists or creates a new one otherwise.
|
|
|
|
|
*
|
|
|
|
|
* @param toAddress the address without the resource, we used to put
|
|
|
|
|
* a reference in jids table.
|
|
|
|
|
* @param jid the Jabber ID that we'd like to obtain a chat instance for.
|
|
|
|
|
*
|
|
|
|
|
* @return a reference to an open chat with the specified
|
|
|
|
|
* <tt>jid</tt> if one exists or creates a new one otherwise.
|
|
|
|
|
*/
|
|
|
|
|
public Chat obtainChatInstance(String toAddress,
|
|
|
|
|
String jid)
|
|
|
|
|
{
|
|
|
|
|
XMPPConnection jabberConnection
|
|
|
|
|
= jabberProvider.getConnection();
|
|
|
|
|
|
|
|
|
|
TargetAddress ta = getJidForAddress(toAddress);
|
|
|
|
|
|
|
|
|
|
if (ta != null
|
|
|
|
|
&& ta.chat != null
|
|
|
|
|
&& ta.jid.equals(jid)
|
|
|
|
|
&& ta.chat.getParticipant().equals(jid))
|
|
|
|
|
{
|
|
|
|
|
return ta.chat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org.jivesoftware.smack.MessageListener msgListener
|
|
|
|
|
= new org.jivesoftware.smack.MessageListener()
|
|
|
|
|
{
|
|
|
|
|
public void processMessage(
|
|
|
|
|
Chat chat,
|
|
|
|
|
org.jivesoftware.smack.packet.Message message)
|
|
|
|
|
{
|
|
|
|
|
//we are not fully supporting chat based messaging
|
|
|
|
|
//right now and only use a hack to make it look that
|
|
|
|
|
//way. as a result we don't listen on the chat
|
|
|
|
|
//itself and the only thing we do here is an update
|
|
|
|
|
//of the active thread timestamp.
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//we don't have a thread for this chat, so let's create one.
|
|
|
|
|
Chat chat = jabberConnection.getChatManager()
|
|
|
|
|
.createChat(jid, msgListener);
|
|
|
|
|
|
|
|
|
|
return chat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove from our <tt>jids</tt> map all entries that have not seen any
|
|
|
|
|
* activity (i.e. neither outgoing nor incoming messags) for more than
|
|
|
|
|
* JID_INACTIVITY_TIMEOUT. Note that this method is not synchronous and that
|
|
|
|
|
* it is only meant for use by the {@link #getJidForAddress(String)} and
|
|
|
|
|
* it is only meant for use by the {@link #getThreadIDForAddress(String)} and
|
|
|
|
|
* {@link #putJidForAddress(String, String, Chat)}
|
|
|
|
|
*/
|
|
|
|
|
private void purgeOldJids()
|
|
|
|
|
{
|
|
|
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
Iterator<Map.Entry<String, TargetAddress>> entries
|
|
|
|
|
Iterator<Map.Entry<String, StoredThreadID>> entries
|
|
|
|
|
= jids.entrySet().iterator();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while( entries.hasNext() )
|
|
|
|
|
{
|
|
|
|
|
Map.Entry<String, TargetAddress> entry = entries.next();
|
|
|
|
|
TargetAddress target = entry.getValue();
|
|
|
|
|
Map.Entry<String, StoredThreadID> entry = entries.next();
|
|
|
|
|
StoredThreadID target = entry.getValue();
|
|
|
|
|
|
|
|
|
|
if (currentTime - target.lastUpdatedTime
|
|
|
|
|
> JID_INACTIVITY_TIMEOUT)
|
|
|
|
|
@ -371,19 +333,19 @@ private void purgeOldJids()
|
|
|
|
|
* contacted us from or <tt>null</tt> if we don't have a jid for the
|
|
|
|
|
* specified <tt>address</tt> yet.
|
|
|
|
|
*/
|
|
|
|
|
TargetAddress getJidForAddress(String address)
|
|
|
|
|
String getThreadIDForAddress(String jid)
|
|
|
|
|
{
|
|
|
|
|
synchronized(jids)
|
|
|
|
|
{
|
|
|
|
|
purgeOldJids();
|
|
|
|
|
TargetAddress ta = jids.get(address);
|
|
|
|
|
StoredThreadID ta = jids.get(jid);
|
|
|
|
|
|
|
|
|
|
if (ta == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
ta.lastUpdatedTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
return ta;
|
|
|
|
|
return ta.threadID;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -398,23 +360,24 @@ TargetAddress getJidForAddress(String address)
|
|
|
|
|
* @param jid the jid (i.e. address/resource) that the contact with the
|
|
|
|
|
* specified <tt>address</tt> last contacted us from.
|
|
|
|
|
*/
|
|
|
|
|
private void putJidForAddress(String address, String jid, Chat chat)
|
|
|
|
|
private void putJidForAddress(String jid, String threadID)
|
|
|
|
|
{
|
|
|
|
|
synchronized(jids)
|
|
|
|
|
{
|
|
|
|
|
purgeOldJids();
|
|
|
|
|
|
|
|
|
|
TargetAddress ta = jids.get(address);
|
|
|
|
|
StoredThreadID ta = jids.get(jid);
|
|
|
|
|
|
|
|
|
|
if (ta == null)
|
|
|
|
|
{
|
|
|
|
|
ta = new TargetAddress();
|
|
|
|
|
jids.put(address, ta);
|
|
|
|
|
ta = new StoredThreadID();
|
|
|
|
|
jids.put(jid, ta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ta.jid = jid;
|
|
|
|
|
recentJIDForAddress.put(StringUtils.parseBareAddress(jid), jid);
|
|
|
|
|
|
|
|
|
|
ta.lastUpdatedTime = System.currentTimeMillis();
|
|
|
|
|
ta.chat = chat;
|
|
|
|
|
ta.threadID = threadID;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -441,115 +404,100 @@ private MessageDeliveredEvent sendMessage( Contact to,
|
|
|
|
|
"The specified contact is not a Jabber contact."
|
|
|
|
|
+ to);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
assertConnected();
|
|
|
|
|
|
|
|
|
|
org.jivesoftware.smack.packet.Message msg =
|
|
|
|
|
new org.jivesoftware.smack.packet.Message();
|
|
|
|
|
assertConnected();
|
|
|
|
|
|
|
|
|
|
String toJID = null;
|
|
|
|
|
|
|
|
|
|
boolean sendToBaseResource = false;
|
|
|
|
|
if (toResource != null)
|
|
|
|
|
{
|
|
|
|
|
if(toResource.equals(ContactResource.BASE_RESOURCE))
|
|
|
|
|
{
|
|
|
|
|
toJID = to.getAddress();
|
|
|
|
|
sendToBaseResource = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
toJID =
|
|
|
|
|
((ContactResourceJabberImpl) toResource).getFullJid();
|
|
|
|
|
}
|
|
|
|
|
org.jivesoftware.smack.packet.Message msg =
|
|
|
|
|
new org.jivesoftware.smack.packet.Message();
|
|
|
|
|
|
|
|
|
|
if (toJID == null)
|
|
|
|
|
{
|
|
|
|
|
TargetAddress ta = getJidForAddress(to.getAddress());
|
|
|
|
|
if(ta != null)
|
|
|
|
|
toJID = ta.jid;
|
|
|
|
|
}
|
|
|
|
|
String toJID = null;
|
|
|
|
|
|
|
|
|
|
if (toJID == null)
|
|
|
|
|
if (toResource != null)
|
|
|
|
|
{
|
|
|
|
|
if(toResource.equals(ContactResource.BASE_RESOURCE))
|
|
|
|
|
{
|
|
|
|
|
sendToBaseResource = true;
|
|
|
|
|
toJID = to.getAddress();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
toJID =
|
|
|
|
|
((ContactResourceJabberImpl) toResource).getFullJid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Chat chat = obtainChatInstance(to.getAddress(), toJID);
|
|
|
|
|
if (toJID == null)
|
|
|
|
|
{
|
|
|
|
|
toJID = to.getAddress();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg.setPacketID(message.getMessageUID());
|
|
|
|
|
msg.setTo(toJID);
|
|
|
|
|
msg.setPacketID(message.getMessageUID());
|
|
|
|
|
msg.setTo(toJID);
|
|
|
|
|
|
|
|
|
|
for (PacketExtension ext : extensions)
|
|
|
|
|
{
|
|
|
|
|
msg.addExtension(ext);
|
|
|
|
|
}
|
|
|
|
|
for (PacketExtension ext : extensions)
|
|
|
|
|
{
|
|
|
|
|
msg.addExtension(ext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (logger.isTraceEnabled())
|
|
|
|
|
logger.trace("Will send a message to:" + toJID
|
|
|
|
|
+ " chat.jid=" + chat.getParticipant()
|
|
|
|
|
+ " chat.tid=" + chat.getThreadID());
|
|
|
|
|
if (logger.isTraceEnabled())
|
|
|
|
|
logger.trace("Will send a message to:" + toJID
|
|
|
|
|
+ " chat.jid=" + toJID);
|
|
|
|
|
|
|
|
|
|
MessageDeliveredEvent msgDeliveryPendingEvt
|
|
|
|
|
= new MessageDeliveredEvent(message, to, toResource);
|
|
|
|
|
MessageDeliveredEvent msgDeliveryPendingEvt
|
|
|
|
|
= new MessageDeliveredEvent(message, to, toResource);
|
|
|
|
|
|
|
|
|
|
msgDeliveryPendingEvt
|
|
|
|
|
= messageDeliveryPendingTransform(msgDeliveryPendingEvt);
|
|
|
|
|
msgDeliveryPendingEvt
|
|
|
|
|
= messageDeliveryPendingTransform(msgDeliveryPendingEvt);
|
|
|
|
|
|
|
|
|
|
if (msgDeliveryPendingEvt == null)
|
|
|
|
|
return null;
|
|
|
|
|
if (msgDeliveryPendingEvt == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
String content = msgDeliveryPendingEvt
|
|
|
|
|
.getSourceMessage().getContent();
|
|
|
|
|
String content = msgDeliveryPendingEvt
|
|
|
|
|
.getSourceMessage().getContent();
|
|
|
|
|
|
|
|
|
|
if(message.getContentType().equals(HTML_MIME_TYPE))
|
|
|
|
|
{
|
|
|
|
|
msg.setBody(Html2Text.extractText(content));
|
|
|
|
|
if(message.getContentType().equals(HTML_MIME_TYPE))
|
|
|
|
|
{
|
|
|
|
|
msg.setBody(Html2Text.extractText(content));
|
|
|
|
|
|
|
|
|
|
// Check if the other user supports XHTML messages
|
|
|
|
|
// make sure we use our discovery manager as it caches calls
|
|
|
|
|
if(jabberProvider.isFeatureListSupported(
|
|
|
|
|
chat.getParticipant(),
|
|
|
|
|
HTML_NAMESPACE))
|
|
|
|
|
{
|
|
|
|
|
// Add the XHTML text to the message
|
|
|
|
|
XHTMLManager.addBody(msg,
|
|
|
|
|
OPEN_BODY_TAG + content + CLOSE_BODY_TAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
// 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))
|
|
|
|
|
{
|
|
|
|
|
// this is plain text so keep it as it is.
|
|
|
|
|
msg.setBody(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());
|
|
|
|
|
//msg.addExtension(new Version());
|
|
|
|
|
|
|
|
|
|
if(msgDeliveryPendingEvt.isMessageEncrypted())
|
|
|
|
|
{
|
|
|
|
|
msg.addExtension(new CarbonPacketExtension.PrivateExtension());
|
|
|
|
|
}
|
|
|
|
|
if(msgDeliveryPendingEvt.isMessageEncrypted())
|
|
|
|
|
{
|
|
|
|
|
msg.addExtension(new CarbonPacketExtension.PrivateExtension());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MessageEventManager.
|
|
|
|
|
addNotificationsRequests(msg, true, false, false, true);
|
|
|
|
|
MessageEventManager.
|
|
|
|
|
addNotificationsRequests(msg, true, false, false, true);
|
|
|
|
|
|
|
|
|
|
chat.sendMessage(msg);
|
|
|
|
|
String threadID = getThreadIDForAddress(toJID);
|
|
|
|
|
if(threadID == null)
|
|
|
|
|
threadID = nextThreadID();
|
|
|
|
|
|
|
|
|
|
putJidForAddress(to.getAddress(), toJID, chat);
|
|
|
|
|
msg.setThread(threadID);
|
|
|
|
|
|
|
|
|
|
MessageDeliveredEvent msgDeliveredEvt
|
|
|
|
|
= new MessageDeliveredEvent(message, to, toResource);
|
|
|
|
|
jabberProvider.getConnection().sendPacket(msg);
|
|
|
|
|
|
|
|
|
|
// msgDeliveredEvt = messageDeliveredTransform(msgDeliveredEvt);
|
|
|
|
|
putJidForAddress(toJID, threadID);
|
|
|
|
|
|
|
|
|
|
return msgDeliveredEvt;
|
|
|
|
|
}
|
|
|
|
|
catch (XMPPException ex)
|
|
|
|
|
{
|
|
|
|
|
logger.error("message not sent", ex);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
MessageDeliveredEvent msgDeliveredEvt
|
|
|
|
|
= new MessageDeliveredEvent(message, to, toResource);
|
|
|
|
|
|
|
|
|
|
// msgDeliveredEvt = messageDeliveredTransform(msgDeliveredEvt);
|
|
|
|
|
|
|
|
|
|
return msgDeliveredEvt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -1009,27 +957,7 @@ public void processPacket(Packet packet)
|
|
|
|
|
fireMessageEvent(ev);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//cache the jid (resource included) of the contact that's sending us
|
|
|
|
|
//a message so that all following messages would go to the resource
|
|
|
|
|
//that they contacted us from.
|
|
|
|
|
String address = userBareID;
|
|
|
|
|
if(isPrivateMessaging)
|
|
|
|
|
{
|
|
|
|
|
address = JabberActivator.getResources().getI18NString(
|
|
|
|
|
"service.gui.FROM",
|
|
|
|
|
new String[]{
|
|
|
|
|
StringUtils.parseResource(msg.getFrom()),
|
|
|
|
|
userBareID} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Chat chat =
|
|
|
|
|
jabberProvider.getConnection().getChatManager()
|
|
|
|
|
.getThreadChat(msg.getThread());
|
|
|
|
|
putJidForAddress(address, userFullId, chat);
|
|
|
|
|
|
|
|
|
|
if (logger.isTraceEnabled())
|
|
|
|
|
logger.trace("just mapped: " + userBareID
|
|
|
|
|
+ " to " + msg.getFrom());
|
|
|
|
|
putJidForAddress(userFullId, msg.getThread());
|
|
|
|
|
|
|
|
|
|
// In the second condition we filter all group chat messages,
|
|
|
|
|
// because they are managed by the multi user chat operation set.
|
|
|
|
|
@ -1247,6 +1175,11 @@ private String createMailboxDescription(MailboxIQ mailboxIQ)
|
|
|
|
|
return message.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getRecentJIDForAddress(String address)
|
|
|
|
|
{
|
|
|
|
|
return recentJIDForAddress.get(address);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receives incoming MailNotification Packets
|
|
|
|
|
*/
|
|
|
|
|
@ -1358,4 +1291,16 @@ public void addMessageFilters(PacketFilter filter)
|
|
|
|
|
{
|
|
|
|
|
this.packetFilters.add(filter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the next unique thread id. Each thread id made up of a short
|
|
|
|
|
* alphanumeric prefix along with a unique numeric value.
|
|
|
|
|
*
|
|
|
|
|
* @return the next thread id.
|
|
|
|
|
*/
|
|
|
|
|
private static synchronized String nextThreadID() {
|
|
|
|
|
return prefix + Long.toString(id++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|