/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev$
* Last modified by $Author$
* $Date$
*/
package tigase.xmpp;
import tigase.db.AuthorizationException;
import tigase.db.TigaseDBException;
import tigase.db.AuthRepository;
import tigase.db.UserRepository;
import tigase.server.xmppsession.SessionManagerHandler;
import tigase.util.TigaseStringprepException;
import tigase.xml.Element;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Describe class XMPPResourceConnection here.
*
*
* Created: Wed Feb 8 22:30:37 2006
*
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev$
*/
public class XMPPResourceConnection extends RepositoryAccess {
/**
* Private logger for class instances.
*/
private static final Logger log = Logger.getLogger(XMPPResourceConnection.class
.getName());
/**
* Constant <code>PRESENCE_KEY</code> is a key in temporary session data where
* the last presence sent by the user to server is stored, either initial
* presence or off-line presence before disconnecting.
*/
public static final String PRESENCE_KEY = "user-presence";
public static final String CLOSING_KEY = "closing-conn";
public static final String AUTHENTICATION_TIMEOUT_KEY = "authentication-timeout";
public static final String ERROR_KEY = "error-key";
private long authenticationTime = 0;
/**
* This variable is to keep relates XMPPIOService ID only.
*/
private JID connectionId = null;
private long creationTime = 0;
private String defLang = "en";
private long id_counter = 0;
private long packets_counter = 0;
/**
* Value of <code>System.currentTimeMillis()</code> from the time when this
* session last active from user side.
*/
private long lastAccessed = 0;
private SessionManagerHandler loginHandler = null;
private XMPPSession parentSession = null;
private int priority = 0;
/**
* Session resource - part of user's JID for this session
*/
private String resource = null;
/**
* Session temporary data. All data stored in this <code>Map</code> disappear
* when session finishes.
*/
private Map<String, Object> sessionData = null;
/**
* <code>sessionId</code> keeps XMPP stream session ID given at connection
* initialization time.
*/
private String sessionId = null;
private JID userJid = null;
public void incPacketsCounter() {
++packets_counter;
}
public long getPacketsCounter() {
return packets_counter;
}
/**
* Creates a new <code>XMPPResourceConnection</code> instance.
*
*
* @param connectionId
* @param rep
* @param authRepo
* @param loginHandler
*/
public XMPPResourceConnection(JID connectionId, UserRepository rep,
AuthRepository authRepo, SessionManagerHandler loginHandler) {
super(rep, authRepo);
long currTime = System.currentTimeMillis();
this.connectionId = connectionId;
this.loginHandler = loginHandler;
this.creationTime = currTime;
this.lastAccessed = currTime;
sessionData = new ConcurrentHashMap<String, Object>(4, 0.9f);
}
/**
* Method description
*
*
* @return
*
* @throws NotAuthorizedException
*/
public List<XMPPResourceConnection> getActiveSessions() throws NotAuthorizedException {
if (!isAuthorized()) {
throw new NotAuthorizedException(NOT_AUTHORIZED_MSG);
} // end of if (username == null)
return parentSession.getActiveResources();
}
/**
* Method description
*
*
* @return
*
*/
public JID[] getAllResourcesJIDs() {
return (parentSession == null) ? null : parentSession.getJIDs();
}
/**
* Method description
*
*
* @return
*/
public long getAuthTime() {
return authenticationTime - creationTime;
}
/**
* Returns user JID but without <em>resource</em> part. This is real user ID
* not session ID. To retrieve session ID - full JID refer to
* <code>getJID()</code> method.<br/>
* If session has not been authorized yet this method throws
* <code>NotAuthorizedException</code>.
*
* @return a <code>String</code> value of user ID - this is user JID without
* resource part. To obtain full user JID please refer to
* <code>getJID</code> method.
* @exception NotAuthorizedException
* when this session has not been authorized yet and some parts
* of user JID are not known yet.
* @see #getJID()
*/
@Override
public final BareJID getBareJID() throws NotAuthorizedException {
if (!isAuthorized()) {
throw new NotAuthorizedException(NOT_AUTHORIZED_MSG);
} // end of if (username == null)
return userJid.getBareJID();
}
/**
*
* @param key
* @return
*/
public Object getCommonSessionData(String key) {
return (parentSession == null) ? null : parentSession.getCommonSessionData(key);
}
/**
* Gets the value of connectionId
*
* @return the value of connectionId
* @throws NoConnectionIdException
*/
public JID getConnectionId() throws NoConnectionIdException {
lastAccessed = System.currentTimeMillis();
if (this.connectionId == null) {
throw new NoConnectionIdException(
"Connection ID not set for this session. "
+ "This is probably the SM session to handle traffic addressed to the server itself."
+ " Or maybe it's a bug.");
}
return this.connectionId;
}
/**
* Method description
*
*
* @param jid
*
* @return
*
* @throws NoConnectionIdException
*/
public JID getConnectionId(JID jid) throws NoConnectionIdException {
return (((parentSession == null) || (jid == null)) ? this.getConnectionId()
: parentSession.getResourceConnection(jid).getConnectionId());
}
/**
* Method description
*
*
* @return
*/
public long getCreationTime() {
return creationTime;
}
/**
* Method description
*
*
* @return
*/
public String getDefLang() {
return this.defLang;
}
/**
* Returns full user JID for this session or throws
* <code>NotAuthorizedException</code> if session is not authorized yet and
* therefore user name and resource is not known yet.
*
* @return a <code>String</code> value of calculated user full JID for this
* session including resource name.
* @throws NotAuthorizedException
*/
public final JID getJID() throws NotAuthorizedException {
if (!isAuthorized()) {
throw new NotAuthorizedException(NOT_AUTHORIZED_MSG);
} // end of if (username == null)
return userJid;
}
/**
* Gets the value of lastAccessed
*
* @return the value of lastAccessed
*/
public long getLastAccessed() {
return this.lastAccessed;
}
/**
* Method description
*
*
* @return
*/
public XMPPSession getParentSession() {
return parentSession;
}
/**
* Returns last presence packet with the user presence status or
* <code>null</code> if the user has not yet sent an initial presence.
*
* @return an <code>Element</code> with last presence status received from the
* user.
*/
public Element getPresence() {
return (Element) getSessionData(PRESENCE_KEY);
}
/**
* Method description
*
*
* @return
*/
public int getPriority() {
return priority;
}
/**
* Gets the value of resource
*
* @return the value of resource
*/
public String getResource() {
return this.resource;
}
/**
* Method description
*
*
* @return
*/
public JID getSMComponentId() {
return loginHandler.getComponentId();
}
/**
* Retrieves session data. This method gives access to temporary session data
* only. You can retrieve earlier saved data giving key ID to receive needed
* value. Please see <code>putSessionData</code> description for more details.
*
* @param key
* a <code>String</code> value of stored data ID.
* @return a <code>Object</code> value of data for given key.
* @see #putSessionData(String, Object)
*/
public final Object getSessionData(final String key) {
lastAccessed = System.currentTimeMillis();
return sessionData.get(key);
}
/**
* Gets the value of sessionId
*
* @return the value of sessionId
*/
public String getSessionId() {
return this.sessionId;
}
/**
* To get the user bare JID please use <code>getBareJID</code> method, to
* check the whether the user with given BareJID is owner of the session
* please use method <code>isUserId(...)</code>. From now one the user session
* may handle more than a single userId, hence getting just userId is not
* enough to check whether the user Id belongs to the session.
*
*
* @return
*
* @throws NotAuthorizedException
*
* @deprecated
*/
@Deprecated
public BareJID getUserId() throws NotAuthorizedException {
return this.getBareJID();
}
/**
* Method description
*
*
* @return
*
* @throws NotAuthorizedException
*/
@Override
public final String getUserName() throws NotAuthorizedException {
if (!isAuthorized()) {
throw new NotAuthorizedException(NOT_AUTHORIZED_MSG);
} // end of if (username == null)
return parentSession.getUserName();
}
// ~--- methods --------------------------------------------------------------
/**
* Returns full user JID for this session without throwing the
* <code>NotAuthorizedException</code> exception if session is not authorized
* yet and therefore user name and resource is not known yet. Please note this
* method is for logging using only to avoid excessive use of try/catch for
* debugging code. It may return null.
*
* @return a <code>String</code> value of calculated user full JID for this
* session including resource name.
*/
public final JID getjid() {
return userJid;
}
// ~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public boolean isAuthorized() {
return super.isAuthorized() && (parentSession != null);
}
/**
* Method description
*
*
* @param outDomain
* @param includeComponents
*
* @return
*/
public boolean isLocalDomain(String outDomain, boolean includeComponents) {
return loginHandler.isLocalDomain(outDomain, includeComponents);
}
/**
* Method description
*
*
* @return
*/
public boolean isResourceSet() {
return this.resource != null;
}
/**
* Returns information whether this is a server (SessionManager) session or
* normal user session. The server session is used to handle packets addressed
* to the server itself (local domain name).
*
* @return a <code>boolean</code> value of <code>true</code> if this is the
* server session and <code>false</code> otherwise.
*/
public boolean isServerSession() {
return false;
}
/**
* Method description
*
*
* @param bareJID
*
* @return
*
* @throws NotAuthorizedException
*/
public boolean isUserId(BareJID bareJID) throws NotAuthorizedException {
if (!isAuthorized()) {
throw new NotAuthorizedException(NOT_AUTHORIZED_MSG);
} // end of if (username == null)
return userJid.getBareJID().equals(bareJID);
}
// ~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param user
* @param digest
* @param id
* @param alg
*
* @return
*
* @throws AuthorizationException
* @throws NotAuthorizedException
* @throws TigaseDBException
* @throws TigaseStringprepException
*/
@Deprecated
public final Authorization
loginDigest(String user, String digest, String id, String alg)
throws NotAuthorizedException, AuthorizationException, TigaseDBException,
TigaseStringprepException {
BareJID userId = BareJID.bareJIDInstance(user, getDomain().getVhost().getDomain());
Authorization result = super.loginDigest(userId, digest, id, alg);
if (result == Authorization.AUTHORIZED) {
loginHandler.handleLogin(userId, this);
} // end of if (result == Authorization.AUTHORIZED)
return result;
}
/**
* Method description
*
*
* @param props
*
* @return
*
* @throws AuthorizationException
* @throws NotAuthorizedException
* @throws TigaseDBException
*/
@Override
public final Authorization loginOther(Map<String, Object> props)
throws NotAuthorizedException, AuthorizationException, TigaseDBException {
Authorization result = super.loginOther(props);
if (result == Authorization.AUTHORIZED) {
BareJID user = (BareJID) props.get(AuthRepository.USER_ID_KEY);
if (log.isLoggable(Level.FINEST)) {
log.finest("UserAuthRepository.USER_ID_KEY: " + user);
}
loginHandler.handleLogin(user, this);
} // end of if (result == Authorization.AUTHORIZED)
return result;
}
/**
* Method description
*
*
* @param user
* @param password
*
* @return
*
* @throws AuthorizationException
* @throws NotAuthorizedException
* @throws TigaseDBException
* @throws TigaseStringprepException
*/
@Deprecated
public final Authorization loginPlain(String user, String password)
throws NotAuthorizedException, AuthorizationException, TigaseDBException,
TigaseStringprepException {
BareJID userId = BareJID.bareJIDInstance(user, getDomain().getVhost().getDomain());
Authorization result = super.loginPlain(userId, password);
if (result == Authorization.AUTHORIZED) {
loginHandler.handleLogin(userId, this);
} // end of if (result == Authorization.AUTHORIZED)
return result;
}
/**
* Method description
*
*
* @throws NotAuthorizedException
*/
@Override
public final void logout() throws NotAuthorizedException {
loginHandler.handleLogout(getBareJID(), this);
streamClosed();
super.logout();
}
// public boolean isDummy() {
// return dummy;
// }
// public void setDummy(boolean dummy) {
// this.dummy = dummy;
// }
/**
* Method description
*
*
* @return
*/
public String nextStanzaId() {
return "tig" + (++id_counter);
}
/**
* Method description
*
*
* @param key
* @param value
*/
public void putCommonSessionData(String key, Object value) {
if (parentSession != null) {
parentSession.putCommonSessionData(key, value);
}
}
// public void setOnHold() {
// onHold = true;
// }
// public boolean isOnHold() {
// return onHold;
// }
/**
* Saves given session data. Data are saved to temporary storage only and are
* accessible during this session life only and only from this session
* instance.<br/>
* Any <code>Object</code> can be stored and retrieved through
* <code>getSessionData(...)</code>.<br/>
* To access permanent storage to keep data between session instances you must
* use one of <code>get/setData...(...)</code> methods familly. They gives you
* access to hierachical permanent data base. Permanent data base however can
* be accessed after successuf authorization while session storage is availble
* all the time.
*
* @param key
* a <code>String</code> value of stored data key ID.
* @param value
* a <code>Object</code> value of data stored in session.
* @see #getSessionData(String)
*/
public final void putSessionData(final String key, final Object value) {
lastAccessed = System.currentTimeMillis();
sessionData.put(key, value);
}
/**
* Method description
*
*
* @param authProps
* @throws TigaseDBException
*/
@Override
public void queryAuth(Map<String, Object> authProps) throws TigaseDBException {
super.queryAuth(authProps);
}
/**
* Method description
*
*
* @param key
*
* @return
*/
public Object removeCommonSessionData(String key) {
return (parentSession == null) ? null : parentSession.removeCommonSessionData(key);
}
/**
* Method description
*
*
* @param parent
*/
public void removeParentSession(final XMPPSession parent) {
synchronized (this) {
parentSession = null;
}
}
/**
* Method description
*
*
* @param key
*/
public final void removeSessionData(final String key) {
lastAccessed = System.currentTimeMillis();
sessionData.remove(key);
}
/**
* Method description
*
*
* @param lang
*/
public void setDefLang(String lang) {
this.defLang = lang;
}
/**
* Sets the value of lastAccessed
*
* @param argLastAccessed
* Value to assign to this.lastAccessed
*/
public void setLastAccessed(final long argLastAccessed) {
this.lastAccessed = argLastAccessed;
}
/**
* Method description
*
*
* @param parent
*
* @throws TigaseStringprepException
*/
public void setParentSession(final XMPPSession parent) throws TigaseStringprepException {
synchronized (this) {
if (parent != null) {
userJid =
JID.jidInstance(parent.getUserName(), domain.getVhost().getDomain(),
((resource != null) ? resource : sessionId));
}
this.parentSession = parent;
}
}
/**
* Method description
*
*
* @param packet
*/
public void setPresence(Element packet) {
putSessionData(PRESENCE_KEY, packet);
// Parse resource priority:
String pr_str = packet.getCData("/presence/priority");
if (pr_str != null) {
int pr = 1;
try {
pr = Integer.decode(pr_str);
} catch (NumberFormatException e) {
if (log.isLoggable(Level.FINER)) {
log.finer("Incorrect priority value: " + pr_str + ", setting 1 as default.");
}
pr = 1;
}
setPriority(pr);
}
loginHandler.handlePresenceSet(this);
}
/**
* Method description
*
*
* @param priority
*/
public void setPriority(final int priority) {
this.priority = priority;
}
/**
* Sets the connection resource
*
* @param argResource
* Value to assign to this.resource
* @throws NotAuthorizedException
* @throws TigaseStringprepException
*/
public void setResource(final String argResource) throws NotAuthorizedException,
TigaseStringprepException {
if (!isAuthorized()) {
throw new NotAuthorizedException(NOT_AUTHORIZED_MSG);
} // end of if (username == null)
this.resource = argResource;
// This is really unlikely a parent session would be null here but it may
// happen when the user disconnects just after sending resource bind.
// Due to asynchronous nature of packets processing in the Tigase the
// the authorization might be canceled while resource bind packet still
// waits in the queue.....
// This has already happened....
if (parentSession != null) {
parentSession.addResourceConnection(this);
}
userJid = userJid.copyWithResource((resource == null) ? sessionId : resource);
loginHandler.handleResourceBind(this);
}
/**
* Sets the value of sessionId
*
* @param argSessionId
* Value to assign to this.sessionId
*/
public void setSessionId(final String argSessionId) {
this.sessionId = argSessionId;
}
// ~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
public void streamClosed() {
synchronized (this) {
if (parentSession != null) {
parentSession.streamClosed(this);
parentSession = null;
}
} // end of if (parentSession != null)
resource = null;
sessionId = null;
}
/**
* Method description
*
*
* @return
*/
@Override
public String toString() {
return "user_jid=" + userJid + ", packets=" + packets_counter + ", connectioId=" + connectionId + ", domain="
+ domain.getVhost().getDomain() + ", authState=" + getAuthState().name()
+ ", isAnon=" + isAnonymous();
}
/**
* Method description
*
*
* @param name_param
*
* @return
*
* @throws NotAuthorizedException
* @throws TigaseDBException
* @throws TigaseStringprepException
*/
@Override
public Authorization unregister(String name_param) throws NotAuthorizedException,
TigaseDBException, TigaseStringprepException {
Authorization auth_res = super.unregister(name_param);
// if (auth_res == Authorization.AUTHORIZED) {
// List<XMPPResourceConnection> res_conn =
// parentSession.getActiveResources();
// for (XMPPResourceConnection res: res_conn) {
// if (res != this) {
// res.logout();
// } // end of if (res != this)
// } // end of for (XMPPResourceConnection res: res_conn)
// logout();
// } // end of if (res == Authorization.AUTHORIZED)
return auth_res;
}
@Override
protected void login() {
authenticationTime = System.currentTimeMillis();
}
} // XMPPResourceConnection
// ~ Formatted in Sun Code Convention
// ~ Formatted by Jindent --- http://www.jindent.com