Fix issue #613. Update jabber to newest version. Fix some errors with lockups when opening multichats from url argument (and saved chatrooms) and at same time receiving messages on that chat room.

cusax-fix
Damian Minkov 17 years ago
parent a9063b2878
commit b4be51c2f7

Binary file not shown.

@ -3,7 +3,10 @@ Bundle-Description: Smack Lib Provider
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: javax.net.ssl,
Import-Package: javax.net,
javax.net.ssl,
javax.security.auth.callback,
javax.security.sasl,
javax.xml.parsers,
javax.xml.transform,
javax.xml.transform.dom,

Binary file not shown.

@ -35,5 +35,22 @@
root="HKCR"
string="$INSTALL_PATH\run.exe %1"/>
<value name="URL Protocol"
keypath="xmpp"
root="HKCR"
string=""/>
<value name=""
keypath="xmpp"
root="HKCR"
string="URL: XMPP Protocol handler"/>
<value name=""
keypath="xmpp\DefaultIcon"
root="HKCR"
string="$INSTALL_PATH\sc-logo.ico"/>
<value name=""
keypath="xmpp\shell\open\command"
root="HKCR"
string="$INSTALL_PATH\run.exe %1"/>
</pack>
</registry>

@ -39,8 +39,20 @@ public class ChatWindowManager
* @param setSelected specifies whether we should bring the chat to front
* after creating it.
*/
public void openChat(ChatPanel chatPanel, boolean setSelected)
public void openChat(final ChatPanel chatPanel, final boolean setSelected)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
openChat(chatPanel, setSelected);
}
});
return;
}
synchronized (syncChat)
{
ChatWindow chatWindow = chatPanel.getChatWindow();
@ -154,7 +166,7 @@ public boolean isChatOpenedForChatRoom(ChatRoom chatRoom)
ChatRoomWrapper chatRoomWrapper
= (ChatRoomWrapper) descriptor;
if(chatRoomWrapper.getChatRoom().equals(chatRoom)
if(chatRoomWrapper.getChatRoomID().equals(chatRoom.getIdentifier())
&& getChat(chatSession).isShown())
{
return true;
@ -409,8 +421,8 @@ public ChatPanel getMultiChat(ChatRoomWrapper chatRoomWrapper)
}
else
return createChat(chatRoomWrapper);
}
}
}
/**
* Returns the chat panel corresponding to the given chat room.

@ -152,10 +152,11 @@ public boolean containsChatRoom(ChatRoomWrapper chatRoom)
*/
public ChatRoomWrapper findChatRoomWrapperForChatRoom(ChatRoom chatRoom)
{
// compare ids, cause saved chatrooms don't have ChatRoom object
// but Id's are the same
for (ChatRoomWrapper chatRoomWrapper : chatRoomsOrderedCopy)
{
if (chatRoomWrapper.getChatRoom() != null
&& chatRoomWrapper.getChatRoom().equals(chatRoom))
if (chatRoomWrapper.getChatRoomID().equals(chatRoom.getIdentifier()))
{
return chatRoomWrapper;
}

@ -691,7 +691,7 @@ public void leaveChatRoom(ChatRoomWrapper chatRoomWrapper)
GuiActivator.getUIService().getMainFrame(),
GuiActivator.getResources().getI18NString("service.gui.WARNING"),
GuiActivator.getResources().getI18NString(
"service.gui.CHAT_ROOM_LEAVE_NOT_CONNECTED="))
"service.gui.CHAT_ROOM_LEAVE_NOT_CONNECTED"))
.showDialog();
return;

@ -246,7 +246,16 @@ public ChatRoomWrapper findChatRoomWrapperFromChatRoom(ChatRoom chatRoom)
= provider.findChatRoomWrapperForChatRoom(chatRoom);
if (chatRoomWrapper != null)
{
// stored chatrooms has no chatroom, but their
// id is the same as the chatroom we are searching wrapper for
if(chatRoomWrapper.getChatRoom() == null)
{
chatRoomWrapper.setChatRoom(chatRoom);
}
return chatRoomWrapper;
}
}
}

@ -20,6 +20,7 @@
import org.jivesoftware.smack.util.*;
import org.jivesoftware.smackx.*;
import org.jivesoftware.smackx.muc.*;
import org.jivesoftware.smackx.packet.DiscoverInfo;
/**
* Implements chat rooms for jabber. The class encapsulates instances of the
@ -1724,10 +1725,15 @@ public boolean isPersistent()
String roomName = multiUserChat.getRoom();
try
{
RoomInfo info = MultiUserChat.getRoomInfo(
provider.getConnection(), roomName);
// Do not use getRoomInfo, as it has bug and
// throws NPE
DiscoverInfo info =
ServiceDiscoveryManager.getInstanceFor(provider.getConnection()).
discoverInfo(roomName);
if (info != null)
persistent = info.isPersistent();
persistent = info.containsFeature("muc_persistent");
} catch (Exception ex)
{
logger.warn("could not get persistent state for room :" +

@ -257,14 +257,6 @@ List getContactDetails(String contactAddress)
+ this + " : " + exc.getMessage()
, exc);
}
Iterator i = result.iterator();
while (i.hasNext())
{
Object object = i.next();
logger.info("--------------- " + object.getClass() + " " + object);
}
}
retreivedDetails.put(contactAddress, result);

@ -10,6 +10,7 @@
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.media.*;
@ -48,6 +49,10 @@ public class JabberActivator
*/
private static ProtocolProviderFactoryJabberImpl jabberProviderFactory = null;
private UriHandlerJabberImpl uriHandlerImpl = null;
private static UIService uiService = null;
/**
* Called when this bundle is started so the Framework can perform the
* bundle-specific activities necessary to start this bundle.
@ -67,6 +72,13 @@ public void start(BundleContext context) throws Exception
jabberProviderFactory = new ProtocolProviderFactoryJabberImpl();
/*
* Install the UriHandler prior to registering the factory service in
* order to allow it to detect when the stored accounts are loaded
* (because they may be asynchronously loaded).
*/
uriHandlerImpl = new UriHandlerJabberImpl(jabberProviderFactory);
//reg the jabber account man.
jabberPpFactoryServReg = context.registerService(
ProtocolProviderFactory.class.getName(),
@ -153,5 +165,31 @@ public void stop(BundleContext context) throws Exception
{
jabberProviderFactory.stop();
jabberPpFactoryServReg.unregister();
if (uriHandlerImpl != null)
{
uriHandlerImpl.dispose();
uriHandlerImpl = null;
}
}
/**
* Returns a reference to the UIService implementation currently registered
* in the bundle context or null if no such implementation was found.
*
* @return a reference to a UIService implementation currently registered
* in the bundle context or null if no such implementation was found.
*/
public static UIService getUIService()
{
if(uiService == null)
{
ServiceReference uiServiceReference
= bundleContext.getServiceReference(
UIService.class.getName());
uiService = (UIService)bundleContext
.getService(uiServiceReference);
}
return uiService;
}
}

@ -13,7 +13,6 @@
import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.*;
import org.jivesoftware.smackx.muc.*;
@ -114,7 +113,7 @@ public void removeInvitationListener(ChatRoomInvitationListener listener)
invitationListeners.remove(listener);
}
}
/**
* Adds a listener that will be notified of changes in our status in a chat
* room such as us being kicked, banned or dropped.
@ -236,7 +235,7 @@ public ChatRoom createChatRoom(String roomName, Hashtable roomProperties)
private ChatRoom createLocalChatRoomInstance(MultiUserChat muc)
{
synchronized(chatRoomCache)
{
{
ChatRoomJabberImpl chatRoom
= new ChatRoomJabberImpl(muc, jabberProvider);
cacheChatRoom(chatRoom);
@ -246,7 +245,7 @@ private ChatRoom createLocalChatRoomInstance(MultiUserChat muc)
// ChatRoomInvitationRejectionListener.
muc.addInvitationRejectionListener(
new SmackInvitationRejectionListener(chatRoom));
return chatRoom;
}
}
@ -264,7 +263,7 @@ private ChatRoom createLocalChatRoomInstance(MultiUserChat muc)
* @throws OperationNotSupportedException if the server does not support
* multi user chat
*/
public ChatRoom findRoom(String roomName)
public synchronized ChatRoom findRoom(String roomName)
throws OperationFailedException, OperationNotSupportedException
{
//make sure we are connected and multichat is supported.
@ -279,59 +278,23 @@ public ChatRoom findRoom(String roomName)
try
{
RoomInfo infos = MultiUserChat.getRoomInfo(
getXmppConnection(), canonicalRoomName);
if (infos.getRoom().equals(canonicalRoomName))
{
MultiUserChat muc =
// throws Exception if room does not exist
// do not use MultiUserChat.getRoomInfo as there is a bug which
// throws NPE
ServiceDiscoveryManager.getInstanceFor(getXmppConnection()).
discoverInfo(canonicalRoomName);
MultiUserChat muc =
new MultiUserChat(getXmppConnection(), canonicalRoomName);
room = new ChatRoomJabberImpl(muc, jabberProvider);
chatRoomCache.put(canonicalRoomName, room);
return room;
}
}
catch (XMPPException xe)
{
return null;
}
catch (NullPointerException ne)
{
// caused by some bug in smack, we will try another method
}
room = new ChatRoomJabberImpl(muc, jabberProvider);
chatRoomCache.put(canonicalRoomName, room);
try
{
// getHostedRooms will let us if the room doesnt exists
// by raising an XMPPException with
// XMPPError.Condition.item_not_found as error condition.
// if we get anything else, we can conclude so we create
// the MultiUserChat instance and the failure point will be
// join method
Collection<HostedRoom> co =
MultiUserChat.getHostedRooms(
getXmppConnection(), canonicalRoomName);
}
catch (XMPPException xe)
return room;
} catch (XMPPException e)
{
if (xe.getXMPPError().getCondition().equals(
XMPPError.Condition.item_not_found.toString()))
{
return null;
}
else
{
MultiUserChat muc =
new MultiUserChat(
getXmppConnection(), canonicalRoomName);
room = new ChatRoomJabberImpl(muc,
jabberProvider);
chatRoomCache.put(canonicalRoomName, room);
return room;
}
// room not found
return null;
}
return null;
}
/**
@ -342,7 +305,7 @@ public ChatRoom findRoom(String roomName)
* a given connection.
*/
public List<ChatRoom> getCurrentlyJoinedChatRooms()
{
{
synchronized(chatRoomCache)
{
List joinedRooms
@ -415,9 +378,9 @@ public List<String> getExistingChatRooms()
OperationNotSupportedException
{
assertSupportedAndConnected();
List list = new LinkedList();
//first retrieve all conference service names available on this server
Iterator<String> serviceNames = null;
try
@ -484,7 +447,7 @@ public boolean isMultiChatSupportedByContact(Contact contact)
if(contact.getProtocolProvider()
.getOperationSet(OperationSetMultiUserChat.class) != null)
return true;
return false;
}
@ -650,11 +613,11 @@ public List<String> getCurrentlyJoinedChatRooms(ChatRoomMember chatRoomMember)
return (List) joinedRoomsIter;
}
/**
* Delivers a <tt>LocalUserChatRoomPresenceChangeEvent</tt> to all
* registered <tt>LocalUserChatRoomPresenceListener</tt>s.
*
*
* @param chatRoom the <tt>ChatRoom</tt> which has been joined, left, etc.
* @param eventType the type of this event; one of LOCAL_USER_JOINED,
* LOCAL_USER_LEFT, etc.
@ -668,7 +631,7 @@ public void fireLocalUserPresenceEvent(ChatRoom chatRoom, String eventType,
chatRoom,
eventType,
reason);
Iterator listeners = null;
synchronized (presenceListeners)
{
@ -679,7 +642,7 @@ public void fireLocalUserPresenceEvent(ChatRoom chatRoom, String eventType,
{
LocalUserChatRoomPresenceListener listener
= (LocalUserChatRoomPresenceListener) listeners.next();
listener.localUserPresenceChanged(evt);
}
}
@ -687,11 +650,11 @@ public void fireLocalUserPresenceEvent(ChatRoom chatRoom, String eventType,
/**
* Delivers a <tt>ChatRoomInvitationReceivedEvent</tt> to all
* registered <tt>ChatRoomInvitationListener</tt>s.
*
*
* @param targetChatRoom the room that invitation refers to
* @param inviter the inviter that sent the invitation
* @param reason the reason why the inviter sent the invitation
* @param password the password to use when joining the room
* @param password the password to use when joining the room
*/
public void fireInvitationEvent(
ChatRoom targetChatRoom,
@ -704,11 +667,11 @@ public void fireInvitationEvent(
inviter,
reason,
password);
ChatRoomInvitationReceivedEvent evt
= new ChatRoomInvitationReceivedEvent(this, invitation,
new Date(System.currentTimeMillis()));
Iterator listeners = null;
synchronized (invitationListeners)
{
@ -723,11 +686,11 @@ public void fireInvitationEvent(
listener.invitationReceived(evt);
}
}
/**
* Delivers a <tt>ChatRoomInvitationRejectedEvent</tt> to all
* registered <tt>ChatRoomInvitationRejectionListener</tt>s.
*
*
* @param sourceChatRoom the room that invitation refers to
* @param invitee the name of the invitee that rejected the invitation
* @param reason the reason of the rejection
@ -740,18 +703,18 @@ public void fireInvitationRejectedEvent(ChatRoom sourceChatRoom,
= new ChatRoomInvitationRejectedEvent(
this, sourceChatRoom, invitee, reason,
new Date(System.currentTimeMillis()));
Iterator listeners = null;
synchronized (invitationRejectionListeners)
{
listeners = new ArrayList(invitationRejectionListeners).iterator();
}
while (listeners.hasNext())
{
ChatRoomInvitationRejectionListener listener
= (ChatRoomInvitationRejectionListener) listeners.next();
listener.invitationRejected(evt);
}
}
@ -765,11 +728,11 @@ private class SmackInvitationListener
{
/**
* Called when the an invitation to join a MUC room is received.<p>
*
*
* If the room is password-protected, the invitee will receive a
* password to use to join the room. If the room is members-only, the
* the invitee may be added to the member list.
*
*
* @param conn the XMPPConnection that received the invitation.
* @param room the room that invitation refers to.
* @param inviter the inviter that sent the invitation.
@ -808,7 +771,7 @@ public void invitationReceived(XMPPConnection conn,
}
}
}
/**
* A listener that is fired anytime an invitee declines or rejects an
* invitation.
@ -817,39 +780,39 @@ private class SmackInvitationRejectionListener
implements InvitationRejectionListener
{
private ChatRoom chatRoom;
/**
* Creates an instance of <tt>SmackInvitationRejectionListener</tt> and
* passes to it the chat room for which it will listen for rejection
* events.
*
*
* @param chatRoom
*/
public SmackInvitationRejectionListener(ChatRoom chatRoom)
{
this.chatRoom = chatRoom;
}
/**
* Called when the invitee declines the invitation.
*
*
* @param invitee the invitee that declined the invitation.
* (e.g. hecate@shakespeare.lit).
* @param reason the reason why the invitee declined the invitation.
*/
public void invitationDeclined(String invitee, String reason)
{
{
fireInvitationRejectedEvent(chatRoom, invitee, reason);
}
}
/**
* Our listener that will tell us when we're registered to jabber and the
* smack MultiUserChat is ready to accept us as a listener.
*/
private class RegistrationStateListener
implements RegistrationStateChangeListener
{
{
/**
* The method is called by a ProtocolProvider implementation whenver
* a change in the registration state of the corresponding provider had
@ -862,14 +825,14 @@ public void registrationStateChanged(RegistrationStateChangeEvent evt)
if (evt.getNewState() == RegistrationState.REGISTERED)
{
logger.debug("adding an Invitation listener to the smack muc");
MultiUserChat.addInvitationListener(
jabberProvider.getConnection(),
new SmackInvitationListener());
}
}
}
/**
* Updates corresponding chat room members when a contact has been modified
* in our contact list.
@ -924,7 +887,7 @@ public void subscriptionResolved(SubscriptionEvent evt)
/**
* Finds all chat room members, which name corresponds to the name of the
* given contact and updates their contact references.
*
*
* @param contact the contact we're looking correspondences for.
*/
private void updateChatRoomMembers(Contact contact)

@ -358,7 +358,36 @@ private void connectAndLogin(SecurityAuthority authority, int reasonCode)
if(accountResource == null || accountResource.equals(""))
accountResource = "sip-comm";
connection.login(userID, password, accountResource);
SASLAuthentication.supportSASLMechanism("PLAIN", 0);
try
{
connection.login(userID, password, accountResource);
} catch (XMPPException e1)
{
// after updating to new smack lib
// login mechanisum changed
// this is a way to avoid the problem
try
{
// server disconnect us after such un error
// cleanup
try
{
connection.disconnect();
} catch (Exception e)
{}
// and connect again
connection.connect();
// logging in to google need and service name
connection.login(userID + "@" + serviceName,
password, accountResource);
} catch (XMPPException e2)
{
// if it happens once again throw the original exception
throw e1;
}
}
if(connection.isAuthenticated())
{

@ -543,7 +543,8 @@ public void removeGroup(ContactGroupJabberImpl groupToRemove)
while (iter.hasNext())
{
ContactJabberImpl item = (ContactJabberImpl) iter.next();
roster.removeEntry(item.getSourceEntry());
if(item.isPersistent())
roster.removeEntry(item.getSourceEntry());
}
}
catch (XMPPException ex)

@ -0,0 +1,689 @@
/*
* 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.impl.protocol.jabber;
import java.util.*;
import java.util.regex.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.argdelegation.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* The jabber implementation of the URI handler. This class handles xmpp URIs by
* trying to establish a chat with them or add you to a chatroom.
*
* @author Emil Ivov
* @author Damian Minkov
*/
public class UriHandlerJabberImpl
implements UriHandler, ServiceListener, AccountManagerListener
{
private static final Logger logger =
Logger.getLogger(UriHandlerJabberImpl.class);
/**
* The protocol provider factory that created us.
*/
private final ProtocolProviderFactory protoFactory;
/**
* A reference to the OSGi registration we create with this handler.
*/
private ServiceRegistration ourServiceRegistration = null;
/**
* The object that we are using to synchronize our service registration.
*/
private final Object registrationLock = new Object();
/**
* The <code>AccountManager</code> which loads the stored accounts of
* {@link #protoFactory} and to be monitored when the mentioned loading is
* complete so that any pending {@link #uris} can be handled
*/
private AccountManager accountManager;
/**
* The indicator (and its synchronization lock) which determines whether the
* stored accounts of {@link #protoFactory} have already been loaded.
* <p>
* Before the loading of the stored accounts (even if there're none) of the
* <code>protoFactory</code> is complete, no handling of URIs is to be
* performed because there's neither information which account to handle the
* URI in case there're stored accounts available nor ground for warning the
* user a registered account is necessary to handle URIs at all in case
* there're no stored accounts.
* </p>
*/
private final boolean[] storedAccountsAreLoaded = new boolean[1];
/**
* The list of URIs which have received requests for handling before the
* stored accounts of the {@link #protoFactory} have been loaded. They will
* be handled as soon as the mentioned loading completes.
*/
private List<String> uris;
/**
* Marks network fails in order to avoid endless loops.
*/
private boolean networkFailReceived = false;
/**
* Creates an instance of this uri handler, so that it would start handling
* URIs by passing them to the providers registered by <tt>protoFactory</tt>
* .
*
* @param parentProvider the provider that created us.
*
* @throws NullPointerException if <tt>protoFactory</tt> is <tt>null</tt>.
*/
public UriHandlerJabberImpl(ProtocolProviderFactory protoFactory)
throws NullPointerException
{
if (protoFactory == null)
{
throw new NullPointerException(
"The ProtocolProviderFactory that a UriHandler is created with "
+ " cannot be null.");
}
this.protoFactory = protoFactory;
hookStoredAccounts();
this.protoFactory.getBundleContext().addServiceListener(this);
/*
* Registering the UriHandler isn't strictly necessary if the
* requirement to register the protoFactory after creating this instance
* is met.
*/
registerHandlerService();
}
/**
* Disposes of this <code>UriHandler</code> by, for example, removing the
* listeners it has added in its constructor (in order to prevent memory
* leaks, for one).
*/
public void dispose()
{
protoFactory.getBundleContext().removeServiceListener(this);
unregisterHandlerService();
unhookStoredAccounts();
}
/**
* Sets up (if not set up already) listening for the loading of the stored
* accounts of {@link #protoFactory} in order to make it possible to
* discover when the prerequisites for handling URIs are met.
*/
private void hookStoredAccounts()
{
if (accountManager == null)
{
BundleContext bundleContext = protoFactory.getBundleContext();
accountManager =
(AccountManager) bundleContext.getService(bundleContext
.getServiceReference(AccountManager.class.getName()));
accountManager.addListener(this);
}
}
/**
* Reverts (if not reverted already) the setup performed by a previous chat
* to {@link #hookStoredAccounts()}.
*/
private void unhookStoredAccounts()
{
if (accountManager != null)
{
accountManager.removeListener(this);
accountManager = null;
}
}
/*
* (non-Javadoc)
*
* @see
* net.java.sip.communicator.service.protocol.event.AccountManagerListener
* #handleAccountManagerEvent
* (net.java.sip.communicator.service.protocol.event.AccountManagerEvent)
*/
public void handleAccountManagerEvent(AccountManagerEvent event)
{
/*
* When the loading of the stored accounts of protoFactory is complete,
* the prerequisites for handling URIs have been met so it's time to
* load any handling requests which have come before the loading and
* were thus delayed in uris.
*/
if ((AccountManagerEvent.STORED_ACCOUNTS_LOADED == event.getType())
&& (protoFactory == event.getFactory()))
{
List<String> uris = null;
synchronized (storedAccountsAreLoaded)
{
storedAccountsAreLoaded[0] = true;
if (this.uris != null)
{
uris = this.uris;
this.uris = null;
}
}
unhookStoredAccounts();
if (uris != null)
{
for (Iterator<String> uriIter = uris.iterator(); uriIter
.hasNext();)
{
handleUri(uriIter.next());
}
}
}
}
/**
* Registers this UriHandler with the bundle context so that it could start
* handling URIs
*/
public void registerHandlerService()
{
synchronized (registrationLock)
{
if (ourServiceRegistration != null)
{
// ... we are already registered (this is probably
// happening during startup)
return;
}
Hashtable<String, String> registrationProperties =
new Hashtable<String, String>();
registrationProperties.put(UriHandler.PROTOCOL_PROPERTY,
getProtocol());
ourServiceRegistration =
JabberActivator.bundleContext.registerService(UriHandler.class
.getName(), this, registrationProperties);
}
}
/**
* Unregisters this UriHandler from the bundle context.
*/
public void unregisterHandlerService()
{
synchronized (registrationLock)
{
if (ourServiceRegistration != null)
{
ourServiceRegistration.unregister();
ourServiceRegistration = null;
}
}
}
/**
* Returns the protocol that this handler is responsible for or "xmpp" in
* other words.
*
* @return the "xmpp" string to indicate that this handler is responsible for
* handling "xmpp" uris.
*/
public String getProtocol()
{
return "xmpp";
}
/**
* Parses the specified URI and creates a chat with the currently active
* im operation set.
*
* @param uri the xmpp URI that we have to handle.
*/
public void handleUri(String uri)
{
/*
* TODO If the requirement to register the factory service after
* creating this instance is broken, we'll end up not handling the URIs.
*/
synchronized (storedAccountsAreLoaded)
{
if (!storedAccountsAreLoaded[0])
{
if (uris == null)
{
uris = new LinkedList<String>();
}
uris.add(uri);
return;
}
}
ProtocolProviderService provider;
try
{
provider = selectHandlingProvider(uri);
}
catch (OperationFailedException exc)
{
// The operation has been canceled by the user. Bail out.
logger.trace("User canceled handling of uri " + uri);
return;
}
// if provider is null then we need to tell the user to create an
// account
if (provider == null)
{
showErrorMessage(
"You need to configure at least one XMPP account \n"
+ "to be able to call " + uri, null);
return;
}
if(!uri.contains("?"))
{
OperationSetPersistentPresence presenceOpSet =
(OperationSetPersistentPresence) provider
.getOperationSet(OperationSetPersistentPresence.class);
String contactId = uri.replaceFirst(getProtocol() + ":", "");
//todo check url!!
//Set the email pattern string
Pattern p = Pattern.compile(".+@.+\\.[a-z]+");
if(!p.matcher(contactId).matches())
{
showErrorMessage(
"Wrong contact id : " + uri, null);
return;
}
Contact contact = presenceOpSet.findContactByID(contactId);
if(contact == null)
{
Object result =
JabberActivator.getUIService().getPopupDialog().
showConfirmPopupDialog(
"Do you want to add the contact : " + contactId + " ?",
"Add contact",
PopupDialog.YES_NO_OPTION);
if(result.equals(PopupDialog.YES_OPTION))
{
ExportedWindow ex = JabberActivator.getUIService().
getExportedWindow(ExportedWindow.ADD_CONTACT_WINDOW,
new String[]{contactId});
ex.setVisible(true);
}
return;
}
JabberActivator.getUIService().
getChat(contact).setChatVisible(true);
}
else
{
String croom = uri.replaceFirst(getProtocol() + ":", "");
int ix = croom.indexOf("?");
String param = croom.substring(ix + 1, croom.length());
croom = croom.substring(0, ix);
if(param.equalsIgnoreCase("join"))
{
OperationSetMultiUserChat mchatOpSet =
(OperationSetMultiUserChat) provider
.getOperationSet(OperationSetMultiUserChat.class);
try
{
ChatRoom room = mchatOpSet.findRoom(croom);
if(room != null)
{
room.join();
}
}
catch (OperationFailedException exc)
{
// if we are not online we get this error
// will wait for it and then will try to handle once again
if(exc.getErrorCode() == OperationFailedException.NETWORK_FAILURE
&& !networkFailReceived)
{
networkFailReceived = true;
OperationSetPresence presenceOpSet =
(OperationSetPresence) provider
.getOperationSet(OperationSetPresence.class);
presenceOpSet.addProviderPresenceStatusListener(
new ProviderStatusListener(uri, presenceOpSet));
}
else
showErrorMessage("Error joining to " + croom, exc);
}
catch (OperationNotSupportedException exc)
{
showErrorMessage("Join to " + croom + ", not supported!", exc);
}
}
else
showErrorMessage(
"Unknown param : " + param, null);
}
}
/**
* Informs the user that they need to be registered before chatting and
* asks them whether they would like us to do it for them.
*
* @param uri the uri that the user would like us to chat with after registering.
* @param provider the provider that we may have to reregister.
*/
private void promptForRegistration(String uri,
ProtocolProviderService provider)
{
int answer =
JabberActivator
.getUIService()
.getPopupDialog()
.showConfirmPopupDialog(
"You need to be online in order to chat and your "
+ "account is currently offline. Do want to connect now?",
"Account is currently offline", PopupDialog.YES_NO_OPTION);
if (answer == PopupDialog.YES_OPTION)
{
new ProtocolRegistrationThread(uri, provider).start();
}
}
/**
* The point of implementing a service listener here is so that we would
* only register our own uri handling service and thus only handle URIs
* while the factory is available as an OSGi service. We remove ourselves
* when our factory unregisters its service reference.
*
* @param event the OSGi <tt>ServiceEvent</tt>
*/
public void serviceChanged(ServiceEvent event)
{
Object sourceService =
JabberActivator.bundleContext.
getService(event.getServiceReference());
// ignore anything but our protocol factory.
if (sourceService != protoFactory)
{
return;
}
switch (event.getType())
{
case ServiceEvent.REGISTERED:
// our factory has just been registered as a service ...
registerHandlerService();
break;
case ServiceEvent.UNREGISTERING:
// our factory just died - seppuku.
unregisterHandlerService();
break;
default:
// we don't care.
break;
}
}
/**
* Uses the <tt>UIService</tt> to show an error <tt>message</tt> and log and
* <tt>exception</tt>.
*
* @param message the message that we'd like to show to the user.
* @param exc the exception that we'd like to log
*/
private void showErrorMessage(String message, Exception exc)
{
JabberActivator.getUIService().getPopupDialog().showMessagePopupDialog(
message, "Failed to create chat!", PopupDialog.ERROR_MESSAGE);
logger.error(message, exc);
}
/**
* We use this class when launching a provider registration by ourselves in
* order to track for provider registration states and retry uri handling,
* once the provider is registered.
*
*/
private class ProtocolRegistrationThread
extends Thread
implements RegistrationStateChangeListener
{
private ProtocolProviderService handlerProvider = null;
/**
* The URI that we'd need to chat.
*/
private String uri = null;
/**
* Configures this thread register our parent provider and re-attempt
* connection to the specified <tt>uri</tt>.
*
* @param uri the uri that we need to handle.
* @param handlerProvider the provider that we are going to make
* register and that we are going to use to handle the
* <tt>uri</tt>.
*/
public ProtocolRegistrationThread(String uri,
ProtocolProviderService handlerProvider)
{
super("UriHandlerProviderRegistrationThread:uri=" + uri);
this.uri = uri;
this.handlerProvider = handlerProvider;
}
/**
* Starts the registration process, ads this class as a registration
* listener and then tries to rehandle the uri this thread was initiaded
* with.
*/
@Override
public void run()
{
handlerProvider.addRegistrationStateChangeListener(this);
try
{
handlerProvider.register(JabberActivator.getUIService()
.getDefaultSecurityAuthority(handlerProvider));
}
catch (OperationFailedException exc)
{
logger.error("Failed to manually register provider.");
logger.warn(exc.getMessage(), exc);
}
}
/**
* If the parent provider passes into the registration state, the method
* re-handles the URI that this thread was initiated with. The method
* would only rehandle the uri if the event shows successful
* registration. It would ignore intermediate states such as
* REGISTERING. Disconnection and failure events would simply cause this
* listener to remove itself from the list of registration listeners.
*
* @param evt the <tt>RegistrationStateChangeEvent</tt> that this thread
* was initiated with.
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
if (evt.getNewState() == RegistrationState.REGISTERED)
{
Thread uriRehandleThread = new Thread()
{
public void run()
{
handleUri(uri);
}
};
uriRehandleThread.setName("UriRehandleThread:uri=" + uri);
uriRehandleThread.start();
}
// we're only interested in a single event so we stop listening
// (unless this was a REGISTERING notification)
if (evt.getNewState() == RegistrationState.REGISTERING)
return;
handlerProvider.removeRegistrationStateChangeListener(this);
}
}
/**
* Returns the default provider that we are supposed to handle URIs through
* or null if there aren't any. Depending on the implementation this method
* may require user intervention so make sure you don't rely on a quick
* outcome when chatting it.
*
* @param uri the uri that we'd like to handle with the provider that we are
* about to select.
*
* @return the provider that we should handle URIs through.
*
* @throws OperationFailedException with code <tt>OPERATION_CANCELED</tt> if
* the users.
*/
public ProtocolProviderService selectHandlingProvider(String uri)
throws OperationFailedException
{
ArrayList<AccountID> registeredAccounts =
protoFactory.getRegisteredAccounts();
// if we don't have any providers - return null.
if (registeredAccounts.size() == 0)
{
return null;
}
// if we only have one provider - select it
if (registeredAccounts.size() == 1)
{
ServiceReference providerReference =
protoFactory.getProviderForAccount(registeredAccounts.get(0));
ProtocolProviderService provider =
(ProtocolProviderService) JabberActivator.bundleContext
.getService(providerReference);
return provider;
}
// otherwise - ask the user.
ArrayList<ProviderComboBoxEntry> providers =
new ArrayList<ProviderComboBoxEntry>();
for (AccountID accountID : registeredAccounts)
{
ServiceReference providerReference =
protoFactory.getProviderForAccount(accountID);
ProtocolProviderService provider =
(ProtocolProviderService) JabberActivator.bundleContext
.getService(providerReference);
providers.add(new ProviderComboBoxEntry(provider));
}
Object result =
JabberActivator.getUIService().getPopupDialog().showInputPopupDialog(
"Please select the account that you would like \n"
+ "to use to chat with " + uri, "Account Selection",
PopupDialog.OK_CANCEL_OPTION, providers.toArray(),
providers.get(0));
if (result == null)
{
throw new OperationFailedException("Operation cancelled",
OperationFailedException.OPERATION_CANCELED);
}
return ((ProviderComboBoxEntry) result).provider;
}
/**
* A class that we use to wrap providers before showing them to the user
* through a selection popup dialog from the UIService.
*/
private static class ProviderComboBoxEntry
{
public final ProtocolProviderService provider;
public ProviderComboBoxEntry(ProtocolProviderService provider)
{
this.provider = provider;
}
/**
* Returns a human readable <tt>String</tt> representing the provider
* encapsulated by this class.
*
* @return a human readable string representing the provider.
*/
@Override
public String toString()
{
return provider.getAccountID().getAccountAddress();
}
}
/**
* Waiting for the provider to bcome online and then handle the uri.
*/
private class ProviderStatusListener
implements ProviderPresenceStatusListener
{
private String uri;
private OperationSetPresence parentOpSet;
public ProviderStatusListener(String uri, OperationSetPresence parentOpSet)
{
this.uri = uri;
this.parentOpSet = parentOpSet;
}
public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt)
{
if(evt.getNewStatus().isOnline())
{
parentOpSet.removeProviderPresenceStatusListener(this);
handleUri(uri);
}
}
public void providerStatusMessageChanged(java.beans.PropertyChangeEvent evt)
{
}
}
}

@ -27,6 +27,8 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.protocol.jabberconstants,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.protocol.whiteboardobjects,
net.java.sip.communicator.service.argdelegation,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.media,
net.java.sip.communicator.service.media.event,
org.xmlpull.v1,

Loading…
Cancel
Save