/*
* 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.irc;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* An IRC implementation of the ProtocolProviderService.
*
* @author Loic Kempf
* @author Stephane Remy
*/
public class ProtocolProviderServiceIrcImpl
implements ProtocolProviderService
{
private static final Logger logger
= Logger.getLogger(ProtocolProviderServiceIrcImpl.class);
/**
* The irc server.
*/
private IrcStack ircStack;
/**
* The id of the account that this protocol provider represents.
*/
private AccountID accountID = null;
/**
* We use this to lock access to initialization.
*/
private Object initializationLock = new Object();
/**
* The hashtable with the operation sets that we support locally.
*/
private Hashtable supportedOperationSets = new Hashtable();
/**
* A list of listeners interested in changes in our registration state.
*/
private Vector registrationStateListeners = new Vector();
/**
* Indicates whether or not the provider is initialized and ready for use.
*/
private boolean isInitialized = false;
/**
* The icon corresponding to the irc protocol.
*/
private ProtocolIconIrcImpl ircIcon = new ProtocolIconIrcImpl();
/**
* The default constructor for the IRC protocol provider.
*/
public ProtocolProviderServiceIrcImpl()
{
logger.trace("Creating a irc provider.");
}
/**
* Keeps our current registration state.
*/
private RegistrationState currentRegistrationState
= RegistrationState.UNREGISTERED;
/**
* Initializes the service implementation, and puts it in a sate where it
* could operate with other services. It is strongly recommended that
* properties in this Map be mapped to property names as specified by
* AccountProperties.
*
* @param userID the user id of the IRC account we're currently
* initializing
* @param accountID the identifier of the account that this protocol
* provider represents.
*
* @see net.java.sip.communicator.service.protocol.AccountID
*/
protected void initialize(String userID, AccountID accountID)
{
synchronized(initializationLock)
{
this.accountID = accountID;
//Initialize the multi user chat support
OperationSetMultiUserChatIrcImpl multiUserChat
= new OperationSetMultiUserChatIrcImpl(this);
supportedOperationSets.put(
OperationSetMultiUserChat.class.getName(), multiUserChat);
this.ircStack = new IrcStack( this,
getAccountID().getUserID(),
getAccountID().getUserID(),
"SIP Communicator 1.0",
"");
isInitialized = true;
}
}
/**
* Registers the specified listener with this provider so that it would
* receive notifications on changes of its state or other properties such
* as its local address and display name.
*
* @param listener the listener to register.
*/
public void addRegistrationStateChangeListener(
RegistrationStateChangeListener listener)
{
synchronized(registrationStateListeners)
{
if (!registrationStateListeners.contains(listener))
registrationStateListeners.add(listener);
}
}
/**
* Removes the specified registration listener so that it won't receive
* further notifications when our registration state changes.
*
* @param listener the listener to remove.
*/
public void removeRegistrationStateChangeListener(
RegistrationStateChangeListener listener)
{
synchronized(registrationStateListeners)
{
registrationStateListeners.remove(listener);
}
}
/**
* Creates a RegistrationStateChangeEvent corresponding to the
* specified old and new states and notifies all currently registered
* listeners.
*
* @param oldState the state that the provider had before the change
* occurred
* @param newState the state that the provider is currently in.
* @param reasonCode a value corresponding to one of the REASON_XXX fields
* of the RegistrationStateChangeEvent class, indicating the reason for
* this state transition.
* @param reason a String further explaining the reason code or null if
* no such explanation is necessary.
*/
protected void fireRegistrationStateChanged(RegistrationState oldState,
RegistrationState newState,
int reasonCode,
String reason)
{
RegistrationStateChangeEvent event =
new RegistrationStateChangeEvent( this,
oldState,
newState,
reasonCode,
reason);
logger.debug("Dispatching " + event + " to "
+ registrationStateListeners.size()+ " listeners.");
Iterator listeners = null;
synchronized (registrationStateListeners)
{
listeners = new ArrayList(registrationStateListeners).iterator();
}
while (listeners.hasNext())
{
RegistrationStateChangeListener listener
= (RegistrationStateChangeListener) listeners.next();
listener.registrationStateChanged(event);
}
logger.trace("Done.");
}
/**
* Returns the AccountID that uniquely identifies the account represented
* by this instance of the ProtocolProviderService.
*
* @return the id of the account represented by this provider.
*/
public AccountID getAccountID()
{
return accountID;
}
/**
* Returns the operation set corresponding to the specified class or null
* if this operation set is not supported by the provider implementation.
*
* @param opsetClass the Class of the operation set that we're
* looking for.
* @return returns an OperationSet of the specified Class if
* the underlying implementation supports it or null otherwise.
*/
public OperationSet getOperationSet(Class opsetClass)
{
return (OperationSet) getSupportedOperationSets()
.get(opsetClass.getName());
}
/**
* Returns the short name of the protocol that the implementation of this
* provider is based upon (like SIP, Jabber, ICQ/AIM, or others for
* example).
*
* @return a String containing the short name of the protocol this
* service is implementing (most often that would be a name in
* ProtocolNames).
*/
public String getProtocolName()
{
return ProtocolNames.IRC;
}
/**
* Returns the state of the registration of this protocol provider with
* the corresponding registration service.
*
* @return ProviderRegistrationState
*/
public RegistrationState getRegistrationState()
{
return currentRegistrationState;
}
/**
* Returns an array containing all operation sets supported by the
* current implementation.
*
* @return a java.util.Map containing instance of all supported
* operation sets mapped against their class names (e.g.
* OperationSetPresence.class.getName()) .
*/
public Map getSupportedOperationSets()
{
//Copy the map so that the caller is not able to modify it.
return (Map) supportedOperationSets.clone();
}
/**
* Indicates whether or not this provider is registered
*
* @return true if the provider is currently registered and false
* otherwise.
*/
public boolean isRegistered()
{
return currentRegistrationState
.equals(RegistrationState.REGISTERED);
}
/**
* Starts the registration process.
*
* @param authority the security authority that will be used for
* resolving any security challenges that may be returned during the
* registration or at any moment while wer're registered.
* @throws OperationFailedException with the corresponding code it the
* registration fails for some reason (e.g. a networking error or an
* implementation problem).
*/
public void register(SecurityAuthority authority)
throws OperationFailedException
{
Map accountProperties = getAccountID().getAccountProperties();
String serverAddress = (String) accountProperties
.get(ProtocolProviderFactory.SERVER_ADDRESS);
String serverPort = (String) accountProperties
.get(ProtocolProviderFactory.SERVER_PORT);
if(serverPort == null || serverPort.equals(""))
{
serverPort = "6667";
}
//Verify whether a password has already been stored for this account
String serverPassword = IrcActivator.
getProtocolProviderFactory().loadPassword(getAccountID());
boolean autoNickChange = true;
boolean passwordRequired = true;
if(accountProperties
.get(ProtocolProviderFactory.AUTO_CHANGE_USER_NAME) != null)
{
autoNickChange = new Boolean((String)accountProperties
.get(ProtocolProviderFactory.AUTO_CHANGE_USER_NAME))
.booleanValue();
}
if(accountProperties
.get(ProtocolProviderFactory.PASSWORD_REQUIRED) != null)
{
passwordRequired = new Boolean((String)accountProperties
.get(ProtocolProviderFactory.PASSWORD_REQUIRED))
.booleanValue();
}
//if we don't - retrieve it from the user through the security authority
if (serverPassword == null && passwordRequired)
{
//create a default credentials object
UserCredentials credentials = new UserCredentials();
credentials.setUserName(getAccountID().getUserID());
//request a password from the user
credentials
= authority.obtainCredentials(ProtocolNames.IRC, credentials);
//extract the password the user passed us.
char[] pass = credentials.getPassword();
// the user didn't provide us a password (canceled the operation)
if (pass == null)
{
fireRegistrationStateChanged(
getRegistrationState(),
RegistrationState.UNREGISTERED,
RegistrationStateChangeEvent.REASON_USER_REQUEST, "");
return;
}
serverPassword = new String(pass);
//if the user indicated that the password should be saved, we'll ask
//the proto provider factory to store it for us.
if (credentials.isPasswordPersistent())
{
IrcActivator.getProtocolProviderFactory()
.storePassword(getAccountID(), serverPassword);
}
}
this.ircStack.connect( serverAddress,
Integer.parseInt(serverPort),
serverPassword,
autoNickChange);
}
/**
* Makes the service implementation close all open sockets and release
* any resources that it might have taken and prepare for
* shutdown/garbage collection.
*/
public void shutdown()
{
if(!isInitialized)
{
return;
}
logger.trace("Killing the Irc Protocol Provider.");
if(isRegistered())
{
try
{
//do the un-registration
synchronized(this.initializationLock)
{
unregister();
this.ircStack.dispose();
ircStack = null;
}
}
catch (OperationFailedException ex)
{
//we're shutting down so we need to silence the exception here
logger.error(
"Failed to properly unregister before shutting down. "
+ getAccountID()
, ex);
}
}
isInitialized = false;
}
/**
* Ends the registration of this protocol provider with the current
* registration service.
*
* @throws OperationFailedException with the corresponding code it the
* registration fails for some reason (e.g. a networking error or an
* implementation problem).
*/
public void unregister()
throws OperationFailedException
{
RegistrationState oldState = currentRegistrationState;
currentRegistrationState = RegistrationState.UNREGISTERED;
if (ircStack.isConnected())
ircStack.disconnect();
fireRegistrationStateChanged(
oldState
, currentRegistrationState
, RegistrationStateChangeEvent.REASON_USER_REQUEST
, null);
}
/**
* Returns the icon for this protocol.
*
* @return the icon for this protocol
*/
public ProtocolIcon getProtocolIcon()
{
return ircIcon;
}
/**
* Returns the IRC stack implementation.
*
* @return the IRC stack implementation.
*/
public IrcStack getIrcStack()
{
return ircStack;
}
/**
* Returns the current registration state of this protocol provider.
*
* @return the current registration state of this protocol provider
*/
protected RegistrationState getCurrentRegistrationState()
{
return currentRegistrationState;
}
/**
* Sets the current registration state of this protocol provider.
*
* @param currentRegistrationState the new registration state to set
*/
protected void setCurrentRegistrationState(
RegistrationState currentRegistrationState)
{
this.currentRegistrationState = currentRegistrationState;
}
}