/*
* Tigase Jabber/XMPP Server
* Copyright (C) 2004-2007 "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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://www.gnu.org/licenses/.
*
* $Rev: 1274 $
* Last modified by $Author: kobit $
* $Date: 2008-12-06 23:33:43 +0000 (Sat, 06 Dec 2008) $
*/
package tigase.server.xmppsession;
//import tigase.auth.TigaseConfiguration;
import java.security.Security;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Queue;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import tigase.auth.LoginHandler;
import tigase.auth.TigaseSaslProvider;
import tigase.conf.Configurable;
import tigase.db.DataOverwriteException;
import tigase.db.NonAuthUserRepository;
import tigase.db.RepositoryFactory;
import tigase.db.TigaseDBException;
import tigase.db.UserAuthRepository;
import tigase.db.UserNotFoundException;
import tigase.db.UserRepository;
import tigase.disco.ServiceEntity;
import tigase.disco.ServiceIdentity;
import tigase.disco.XMPPService;
import tigase.server.AbstractMessageReceiver;
import tigase.server.Command;
import tigase.server.Packet;
import tigase.server.Permissions;
import tigase.server.XMPPServer;
import tigase.stats.StatRecord;
import tigase.util.JIDUtils;
import tigase.xml.Element;
import tigase.xmpp.Authorization;
import tigase.xmpp.NotAuthorizedException;
import tigase.xmpp.PacketErrorTypeException;
import tigase.xmpp.ProcessorFactory;
import tigase.xmpp.StanzaType;
import tigase.xmpp.XMPPPostprocessorIfc;
import tigase.xmpp.XMPPPreprocessorIfc;
import tigase.xmpp.XMPPProcessorIfc;
import tigase.xmpp.XMPPResourceConnection;
import tigase.xmpp.XMPPSession;
import tigase.xmpp.XMPPStopListenerIfc;
import tigase.xmpp.ConnectionStatus;
import static tigase.server.xmppsession.SessionManagerConfig.*;
/**
* Class SessionManager
*
*
* Created: Tue Nov 22 07:07:11 2005
*
* @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a>
* @version $Rev: 1274 $
*/
public class SessionManager extends AbstractMessageReceiver
implements Configurable, XMPPService, LoginHandler {
/**
* Variable <code>log</code> is a class logger.
*/
private static final Logger log =
Logger.getLogger("tigase.server.xmppsession.SessionManager");
protected static final String SESSION_PACKETS = "session-packets";
private UserRepository user_repository = null;
private UserAuthRepository auth_repository = null;
private NonAuthUserRepository naUserRepository = null;
private PacketFilter filter = null;
//private String[] hostnames = {"localhost"};
private String[] admins = {"admin@localhost"};
private String[] trusted = {"admin@localhost"};
//private String[] anon_peers = {"admin@localhost"};
private Map<String, XMPPSession> sessionsByNodeId =
new ConcurrentSkipListMap<String, XMPPSession>();
private Map<String, XMPPResourceConnection> connectionsByFrom =
new ConcurrentSkipListMap<String, XMPPResourceConnection>();
private Map<String, XMPPPreprocessorIfc> preProcessors =
new ConcurrentSkipListMap<String, XMPPPreprocessorIfc>();
private Map<String, ProcessorThread> processors =
new ConcurrentSkipListMap<String, ProcessorThread>();
private Map<String, XMPPPostprocessorIfc> postProcessors =
new ConcurrentSkipListMap<String, XMPPPostprocessorIfc>();
private Map<String, XMPPStopListenerIfc> stopListeners =
new ConcurrentSkipListMap<String, XMPPStopListenerIfc>();
private Map<String, Map<String, Object>> plugin_config =
new ConcurrentSkipListMap<String, Map<String, Object>>();
//private Set<String> anonymous_domains = new HashSet<String>();
//private XMPPResourceConnection serverSession = null;
private ServiceEntity serviceEntity = null;
private long closedConnections = 0;
@Override
public void setName(String name) {
super.setName(name);
serviceEntity = new ServiceEntity(name, "sm", "Session manager");
serviceEntity.addIdentities(
new ServiceIdentity("component", "sm", "Session manager"));
}
// private void debug_packet(String msg, Packet packet, String to) {
// if (packet.getElemTo().equals(to)) {
// log.finest(msg + ", packet: " + packet.getStringData());
// }
// }
protected XMPPResourceConnection getXMPPResourceConnection(String connId) {
return connectionsByFrom.get(connId);
}
protected XMPPResourceConnection getXMPPResourceConnection(Packet p) {
XMPPResourceConnection conn = null;
if (p.getFrom() != null) {
conn = connectionsByFrom.get(p.getFrom());
if (conn != null) {
return conn.getConnectionStatus() == ConnectionStatus.TEMP ? null : conn;
}
}
// It might be a message _to_ some user on this server
// so let's look for established session for this user...
final String to = p.getElemTo();
if (to != null) {
log.finest("Searching for resource connection for: " + to);
conn = getResourceConnection(to);
if (conn != null && conn.getConnectionStatus() == ConnectionStatus.TEMP) {
conn = null;
}
} else {
// Hm, not sure what should I do now....
// Maybe I should treat it as message to admin....
log.info("Message without TO attribute set, don't know what to do wih this: "
+ p.getStringData());
} // end of else
return conn;
}
protected boolean isBrokenPacket(Packet p) {
if (!p.getFrom().equals(p.getElemFrom()) && (!p.isCommand()
|| (p.isCommand() && p.getCommand() == Command.OTHER))) {
// It doesn't look good, there should reaaly be a connection for
// this packet....
// returning error back...
log.info("Broken packet: " + p.toString());
try {
Packet error =
Authorization.SERVICE_UNAVAILABLE.getResponseMessage(p,
"Service not available.", true);
error.setTo(p.getFrom());
fastAddOutPacket(error);
} catch (PacketErrorTypeException e) {
log.warning("Packet processing exception: " + e);
}
return true;
}
return false;
}
public void processPacket(final Packet packet) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Received packet: " + packet.toString());
}
if (packet.isCommand() && processCommand(packet)) {
packet.processedBy("SessionManager");
// No more processing is needed for command packet
return;
} // end of if (pc.isCommand())
XMPPResourceConnection conn = getXMPPResourceConnection(packet);
if (conn == null && (isBrokenPacket(packet) ||
processAdminsOrDomains(packet))) {
return;
}
processPacket(packet, conn);
}
protected void processPacket(Packet packet, XMPPResourceConnection conn) {
packet.setTo(getComponentId());
if (log.isLoggable(Level.FINEST)) {
log.finest("processing packet: " + packet.toString() +
"connectionID: " + (conn != null ? conn.getConnectionId() : "null"));
}
Queue<Packet> results = new LinkedList<Packet>();
boolean stop = false;
if (!stop) {
if (filter.preprocess(packet, conn, naUserRepository, results)) {
packet.processedBy("filter-foward");
if (log.isLoggable(Level.FINEST)) {
log.finest("Packet preprocessed: " + packet.toString());
if (results.size() > 0) {
for (Packet p: results) {
log.finest("Preprocess result: " + p.toString());
}
}
}
addOutPackets(results);
return;
}
}
// Preprocess..., all preprocessors get all messages to look at.
// I am not sure if this is correct for now, let's try to do it this
// way and maybe change it later.
// If any of them returns true - it means processing should stop now.
// That is needed for preprocessors like privacy lists which should
// block certain packets.
if (!stop) {
for (XMPPPreprocessorIfc preproc: preProcessors.values()) {
stop |= preproc.preProcess(packet, conn, naUserRepository, results);
} // end of for (XMPPPreprocessorIfc preproc: preProcessors)
}
if (!stop) {
if (filter.forward(packet, conn, naUserRepository, results)) {
packet.processedBy("filter-foward");
if (log.isLoggable(Level.FINEST)) {
log.finest("Packet forwarded: " + packet.toString());
}
addOutPackets(results);
return;
}
}
if (!stop) {
walk(packet, conn, packet.getElement(), results);
}
if (!stop) {
for (XMPPPostprocessorIfc postproc: postProcessors.values()) {
postproc.postProcess(packet, conn, naUserRepository, results);
} // end of for (XMPPPostprocessorIfc postproc: postProcessors)
} // end of if (!stop)
if (!stop && !packet.wasProcessed() && !isLocalDomain(packet.getElemTo())
&& filter.process(packet, conn, naUserRepository, results)) {
packet.processedBy("filter-process");
}
setPermissions(conn, results);
addOutPackets(results);
if (!packet.wasProcessed()) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Packet not processed: " + packet.toString());
}
Packet error = null;
if (stop
|| (conn == null
&& packet.getElemFrom() != null && packet.getElemTo() != null
&& packet.getElemTo() != getComponentId()
&& (packet.getElemName().equals("iq")
|| packet.getElemName().equals("message")))) {
try {
error = Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet,
"Service not available.", true);
} catch (PacketErrorTypeException e) {
log.warning("Packet processing exception: " + e
+ ", packet: " + packet.toString());
}
} else {
if (packet.getElemFrom() != null || conn != null) {
try {
error = Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet,
"Feature not supported yet.", true);
} catch (PacketErrorTypeException e) {
log.warning("Packet processing exception: " + e
+ ", packet: " + packet.toString());
}
}
}
if (error != null) {
if (error.getElemTo() != null) {
conn = getResourceConnection(error.getElemTo());
} // end of if (error.getElemTo() != null)
if (conn != null) {
error.setTo(conn.getConnectionId());
} // end of if (conn != null)
addOutPacket(error);
}
} else {
log.finest("Packet processed by: " + packet.getProcessorsIds().toString());
} // end of else
}
private void setPermissions(XMPPResourceConnection conn,
Queue<Packet> results) {
Permissions perms = Permissions.NONE;
if (conn != null) {
perms = Permissions.LOCAL;
if (conn.isAuthorized()) {
perms = Permissions.AUTH;
if (conn.isAnonymous()) {
perms = Permissions.ANONYM;
} else {
try {
String id = conn.getUserId();
if (isTrusted(id)) {
perms = Permissions.TRUSTED;
}
if (isAdmin(id)) {
perms = Permissions.ADMIN;
}
} catch (NotAuthorizedException e) {
perms = Permissions.NONE;
}
}
}
}
for (Packet res: results) {
res.setPermissions(perms);
}
}
// protected String[] getVHosts() {
// return hostnames;
// }
private boolean isAdmin(String jid) {
for (String adm: admins) {
if (adm.equals(JIDUtils.getNodeID(jid))) {
return true;
}
}
return false;
}
private boolean isTrusted(String jid) {
for (String trust: trusted) {
if (trust.equals(JIDUtils.getNodeID(jid))) {
return true;
}
}
return isAdmin(jid);
}
protected boolean processAdminsOrDomains(Packet packet) {
final String to = packet.getElemTo();
if (isLocalDomain(to)) {
if (packet.getElemName().equals("message")) {
// Yes this packet is for admin....
log.finer("Packet for admin: " + packet.getStringData());
sendToAdmins(packet);
} else {
log.finer("Packet for hostname: " + packet.getStringData());
Packet host_pac =
new Packet(packet.getElement().clone());
host_pac.getElement().setAttribute("to", getComponentId());
host_pac.getElement().setAttribute(Packet.OLDTO, packet.getElemTo());
processPacket(host_pac);
}
return true;
} // end of if (isInRoutings(to))
return false;
}
protected void sendToAdmins(Packet packet) {
for (String admin: admins) {
log.finer("Sending packet to admin: " + admin);
Packet admin_pac =
new Packet(packet.getElement().clone());
admin_pac.getElement().setAttribute("to", admin);
processPacket(admin_pac);
}
}
protected XMPPSession getSession(String jid) {
return sessionsByNodeId.get(JIDUtils.getNodeID(jid));
}
private XMPPResourceConnection getResourceConnection(String jid) {
XMPPSession session = getSession(jid);
if (session != null) {
log.finest("Session not null, getting resource for jid: " + jid);
return session.getResourceConnection(jid);
} // end of if (session != null)
return null;
}
private String getConnectionId(String jid) {
XMPPResourceConnection res = getResourceConnection(jid);
if (res != null) {
return res.getConnectionId();
} // end of if (res != null)
return null;
}
private void walk(final Packet packet,
final XMPPResourceConnection connection, final Element elem,
final Queue<Packet> results) {
for (ProcessorThread proc_t: processors.values()) {
String xmlns = elem.getXMLNS();
if (xmlns == null) { xmlns = "jabber:client"; }
if (proc_t.processor.isSupporting(elem.getName(), xmlns)) {
if (log.isLoggable(Level.FINEST)) {
log.finest("XMPPProcessorIfc: "+proc_t.processor.getClass().getSimpleName()+
" ("+proc_t.processor.id()+")"+"\n Request: "+elem.toString()
+ (connection != null ? ", " + connection.getConnectionId() : " null"));
}
if (proc_t.addItem(packet, connection)) {
packet.processedBy(proc_t.processor.id());
} else {
log.warning("Can not add packet: " + packet.toString()
+ " to processor: " + proc_t.getName() + " internal queue");
}
} // end of if (proc.isSupporting(elem.getName(), elem.getXMLNS()))
} // end of for ()
Collection<Element> children = elem.getChildren();
if (children != null) {
for (Element child: children) {
walk(packet, connection, child, results);
} // end of for (Element child: children)
} // end of if (children != null)
}
protected XMPPResourceConnection createUserSession(String conn_id,
String domain, String user_jid) {
XMPPResourceConnection connection = new XMPPResourceConnection(conn_id,
user_repository, auth_repository, this, false);
connection.setDomain(domain);
// Dummy session ID, we might decide later to set real thing here
connection.setSessionId("session-id");
//connection.setAnonymousPeers(anon_peers);
connectionsByFrom.put(conn_id, connection);
registerNewSession(JIDUtils.getNodeID(user_jid), connection);
try {
connection.setResource(JIDUtils.getNodeResource(user_jid));
} catch (NotAuthorizedException e) {
log.warning("Something wrong with authorization: " + e
+ ", for user: " + user_jid);
}
return connection;
}
@Override
protected Integer getMaxQueueSize(int def) {
return def*10;
}
private boolean isAnonymousEnabled(String domain) {
return vHostManager != null ? vHostManager.isAnonymousEnabled(domain) :
false;
}
protected boolean processCommand(Packet pc) {
boolean processing_result = false;
log.finer(pc.getCommand().toString() + " command from: " + pc.getFrom());
//Element command = pc.getElement();
XMPPResourceConnection connection = connectionsByFrom.get(pc.getFrom());
switch (pc.getCommand()) {
case STREAM_OPENED:
// It might be existing opened stream after TLS/SASL authorization
// If not, it means this is new stream
if (connection == null) {
log.finer("Adding resource connection for: " + pc.getFrom());
final String hostname = Command.getFieldValue(pc, "hostname");
connection = new XMPPResourceConnection(pc.getFrom(),
user_repository, auth_repository, this,
isAnonymousEnabled(hostname));
if (hostname != null) {
log.finest("Setting hostname " + hostname
+ " for connection: " + connection.getConnectionId());
connection.setDomain(hostname);
} // end of if (hostname != null)
else {
connection.setDomain(getDefHostName());
} // end of if (hostname != null) else
//connection.setAnonymousPeers(anon_peers);
connectionsByFrom.put(pc.getFrom(), connection);
} else {
log.finest("Stream opened for existing session, authorized: "
+ connection.isAuthorized());
} // end of else
connection.setSessionId(Command.getFieldValue(pc, "session-id"));
connection.setDefLang(Command.getFieldValue(pc, "xml:lang"));
log.finest("Setting session-id " + connection.getSessionId()
+ " for connection: " + connection.getConnectionId());
processing_result = true;
break;
case GETFEATURES:
if (pc.getType() == StanzaType.get) {
List<Element> features =
getFeatures(connectionsByFrom.get(pc.getFrom()));
Packet result = pc.commandResult(null);
Command.setData(result, features);
addOutPacket(result);
} // end of if (pc.getType() == StanzaType.get)
processing_result = true;
break;
case STREAM_CLOSED:
closeConnection(pc.getFrom(), false);
processing_result = true;
break;
case BROADCAST_TO_ONLINE:
String from = pc.getFrom();
boolean trusted = false;
try {
trusted = (from != null && isTrusted(from))
|| (connection != null && isTrusted(connection.getUserId()));
} catch (NotAuthorizedException e) {
trusted = false;
}
try {
if (trusted) {
List<Element> packets = Command.getData(pc);
if (packets != null) {
for (XMPPResourceConnection conn: connectionsByFrom.values()) {
if (conn.isAuthorized()) {
try {
for (Element el_pack: packets) {
Element el_copy = el_pack.clone();
el_copy.setAttribute("to", conn.getJID());
Packet out_packet = new Packet(el_copy);
out_packet.setTo(conn.getConnectionId());
addOutPacket(out_packet);
}
} catch (NotAuthorizedException e) {
log.warning("Something wrong, connection is authenticated but "
+ "NoAuthorizedException is thrown.");
}
}
}
} else {
addOutPacket(Authorization.BAD_REQUEST.getResponseMessage(pc,
"Missing packets for broadcast.", true));
}
} else {
addOutPacket(Authorization.FORBIDDEN.getResponseMessage(pc,
"You don't have enough permission to brodcast packet.", true));
}
} catch (PacketErrorTypeException e) {
log.warning("Packet processing exception: " + e
+ ", packet: " + pc.toString());
}
processing_result = true;
break;
case USER_STATUS:
try {
if (isTrusted(pc.getElemFrom())
|| isTrusted(JIDUtils.getNodeHost(pc.getElemFrom()))) {
String av = Command.getFieldValue(pc, "available");
boolean available = !(av != null && av.equalsIgnoreCase("false"));
if (available) {
Packet presence = null;
Element p = pc.getElement().getChild("command").getChild("presence");
if (p != null) {
// + // use this hack to break XMLNS
// + Element el = new Element("presence");
// + el.setChildren(p.getChildren());
Element elem = p.clone();
elem.setXMLNS("jabber:client");
presence = new Packet(elem);
}
connection = connectionsByFrom.get(pc.getElemFrom());
if (connection == null) {
String user_jid = Command.getFieldValue(pc, "jid");
String hostname = JIDUtils.getNodeHost(user_jid);
connection = createUserSession(pc.getElemFrom(), hostname, user_jid);
connection.setSessionId("USER_STATUS");
user_repository.setData(JIDUtils.getNodeID(user_jid), "tokens",
"USER_STATUS", "USER_STATUS");
connection.loginToken("USER_STATUS", "USER_STATUS");
handleLogin(user_jid, connection);
connection.putSessionData("jingle", "active");
addOutPacket(pc.okResult((String)null, 0));
if (presence == null) {
presence =
new Packet(new Element("presence",
new Element[] {
new Element("priority", "-1"),
new Element("c",
new String[] {"node", "ver", "ext", "xmlns"},
new String[] {"http://www.google.com/xmpp/client/caps",
XMPPServer.getImplementationVersion(),
"voice-v1",
"http://jabber.org/protocol/caps"})},
null, null));
}
} else {
// addOutPacket(Authorization.CONFLICT.getResponseMessage(pc,
// "The user resource already exists.", true));
log.finest("USER_STATUS set to true for user who is already available: "
+ pc.toString());
}
if (presence != null) {
presence.setFrom(pc.getElemFrom());
presence.setTo(getComponentId());
addOutPacket(presence);
}
} else {
connection = connectionsByFrom.remove(pc.getElemFrom());
if (connection != null) {
closeSession(connection, false);
addOutPacket(pc.okResult((String)null, 0));
} else {
addOutPacket(Authorization.ITEM_NOT_FOUND.getResponseMessage(pc,
"The user resource you want to remove does not exist.", true));
log.info("Can not find resource connection for packet: " +
pc.toString());
}
}
} else {
try {
addOutPacket(Authorization.FORBIDDEN.getResponseMessage(pc,
"Only trusted entity can do it.", true));
} catch (PacketErrorTypeException e) {
log.warning("Packet error type when not expected: " + pc.toString());
}
}
} catch (Exception e) {
try {
addOutPacket(Authorization.UNDEFINED_CONDITION.getResponseMessage(pc,
"Unexpected error occured during the request: " + e, true));
} catch (Exception ex) { ex.printStackTrace(); }
log.log(Level.WARNING, "USER_STATUS session creation error: ", e);
}
processing_result = true;
break;
case REDIRECT:
if (connection != null) {
String action = Command.getFieldValue(pc, "action");
if (action.equals("close")) {
log.fine("Closing redirected connections: " + pc.getFrom());
sendAllOnHold(connection);
closeConnection(pc.getFrom(), true);
} else {
log.fine("Activating redirected connections: " + pc.getFrom());
}
} else {
log.fine("Redirect for non-existen connection: " + pc.toString());
}
processing_result = true;
break;
case OTHER:
log.info("Other command found: " + pc.getStrCommand());
break;
default:
break;
} // end of switch (pc.getCommand())
return processing_result;
}
@SuppressWarnings("unchecked")
protected void sendAllOnHold(XMPPResourceConnection conn) {
String remote_smId = (String)conn.getSessionData("redirect-to");
LinkedList<Packet> packets =
(LinkedList<Packet>)conn.getSessionData(SESSION_PACKETS);
if (remote_smId == null) {
log.finest("No address for remote SM to redirect packets, processing locally.");
if (packets != null) {
Packet sess_pack = null;
while ((sess_pack = packets.poll()) != null) {
processPacket(sess_pack);
}
}
return;
}
conn.setConnectionStatus(ConnectionStatus.REDIRECT);
if (packets != null) {
Packet sess_pack = null;
while ((sess_pack = packets.poll()) != null) {
sess_pack.setTo(remote_smId);
fastAddOutPacket(sess_pack);
}
}
}
protected void closeConnection(String connectionId, boolean closeOnly) {
log.fine("Stream closed from: " + connectionId);
++closedConnections;
XMPPResourceConnection connection = connectionsByFrom.remove(connectionId);
if (connection != null) {
closeSession(connection, closeOnly);
} else {
log.info("Can not find resource connection for packet: " + connectionId);
} // end of if (conn != null) else
}
private void closeSession(XMPPResourceConnection conn, boolean closeOnly) {
if (!closeOnly) {
Queue<Packet> results = new LinkedList<Packet>();
for (XMPPStopListenerIfc stopProc: stopListeners.values()) {
stopProc.stopped(conn, results, plugin_config.get(stopProc.id()));
} // end of for ()
addOutPackets(results);
}
try {
if (conn.isAuthorized()
|| (conn.getConnectionStatus() == ConnectionStatus.TEMP)) {
String userId = conn.getUserId();
log.info("Closing connection for: " + userId);
XMPPSession session = conn.getParentSession();
if (session != null) {
log.info("Found parent session for: " + userId);
if (session.getActiveResourcesSize() <= 1) {
session = sessionsByNodeId.remove(userId);
if (session == null) {
log.info("UPS can't remove session, not found in map: " + userId);
} else {
log.finer("Number of user sessions: " + sessionsByNodeId.size());
} // end of else
auth_repository.logout(userId);
} else {
StringBuilder sb = new StringBuilder();
for (XMPPResourceConnection res_con: session.getActiveResources()) {
sb.append(", res=" + res_con.getResource() + " ("
+ res_con.getConnectionStatus() + ")");
}
log.finer("Number of connections is "
+ session.getActiveResourcesSize() + " for the user: " + userId
+ sb.toString());
} // end of else
} // end of if (session.getActiveResourcesSize() == 0)
}
} catch (NotAuthorizedException e) {
log.info("Closed not authorized session: " + e);
} catch (Exception e) {
log.log(Level.WARNING, "Exception closing session... ", e);
}
conn.streamClosed();
}
protected boolean checkOutPacket(Packet packet) {
if (packet.getPermissions() == Permissions.ANONYM) {
if (packet.getElemTo() != null
&& !isLocalDomain(JIDUtils.getNodeHost(packet.getElemTo()))) {
try {
addPacket(Authorization.FORBIDDEN.getResponseMessage(packet,
"Anonymous user can only send local messages.", true));
} catch (PacketErrorTypeException e) {
log.log(Level.INFO, "Error for error packet: " + packet.toString(), e);
}
return false;
}
}
String oldto = packet.getAttribute(Packet.OLDTO);
if (oldto != null) {
packet.getElement().setAttribute("from", oldto);
packet.getElement().removeAttribute(Packet.OLDTO);
}
return true;
}
@Override
protected boolean addOutPacket(Packet packet) {
if (checkOutPacket(packet)) {
return super.addOutPacket(packet);
}
return false;
}
protected boolean fastAddOutPacket(Packet packet) {
return super.addOutPacket(packet);
}
@Override
protected boolean addOutPackets(Queue<Packet> packets) {
Packet packet = null;
while ((packet = packets.poll()) != null) {
addOutPacket(packet);
}
return true;
}
// private XMPPSession getXMPPSession(Packet p) {
// return connectionsByFrom.get(p.getFrom()).getParentSession();
// }
private List<Element> getFeatures(XMPPResourceConnection session) {
List<Element> results = new LinkedList<Element>();
for (ProcessorThread proc_t: processors.values()) {
Element[] features = proc_t.processor.supStreamFeatures(session);
if (features != null) {
results.addAll(Arrays.asList(features));
} // end of if (features != null)
} // end of for ()
return results;
}
@Override
public Map<String, Object> getDefaults(Map<String, Object> params) {
Map<String, Object> props = super.getDefaults(params);
SessionManagerConfig.getDefaults(props, params);
return props;
}
private void addPlugin(String comp_id) {
System.out.println("Loading plugin: " + comp_id + " ...");
XMPPProcessorIfc proc = ProcessorFactory.getProcessor(comp_id);
boolean loaded = false;
if (proc != null) {
ProcessorThread pt = new ProcessorThread(proc);
pt.setDaemon(true);
pt.setName(proc.id());
pt.start();
processors.put(comp_id, pt);
log.config("Added processor: " + proc.getClass().getSimpleName()
+ " for plugin id: " + comp_id);
loaded = true;
}
XMPPPreprocessorIfc preproc = ProcessorFactory.getPreprocessor(comp_id);
if (preproc != null) {
preProcessors.put(comp_id, preproc);
log.config("Added preprocessor: " + preproc.getClass().getSimpleName()
+ " for plugin id: " + comp_id);
loaded = true;
}
XMPPPostprocessorIfc postproc = ProcessorFactory.getPostprocessor(comp_id);
if (postproc != null) {
postProcessors.put(comp_id, postproc);
log.config("Added postprocessor: " + postproc.getClass().getSimpleName()
+ " for plugin id: " + comp_id);
loaded = true;
}
XMPPStopListenerIfc stoplist = ProcessorFactory.getStopListener(comp_id);
if (stoplist != null) {
stopListeners.put(comp_id, stoplist);
log.config("Added stopped processor: " + stoplist.getClass().getSimpleName()
+ " for plugin id: " + comp_id);
loaded = true;
}
if (!loaded) {
log.warning("No implementation found for plugin id: " + comp_id);
} // end of if (!loaded)
}
public void setProperties(Map<String, Object> props) {
super.setProperties(props);
Security.insertProviderAt(new TigaseSaslProvider(), 6);
filter = new PacketFilter();
Map<String, String> user_repo_params = new LinkedHashMap<String, String>();
Map<String, String> auth_repo_params = new LinkedHashMap<String, String>();
for (Map.Entry<String, Object> entry: props.entrySet()) {
if (entry.getKey().startsWith(USER_REPO_PARAMS_NODE)) {
// Split the key to configuration nodes separated with '/'
String[] nodes = entry.getKey().split("/");
// The plugin ID part may contain many IDs separated with comma ','
if (nodes.length > 1) {
user_repo_params.put(nodes[1], entry.getValue().toString());
}
}
if (entry.getKey().startsWith(AUTH_REPO_PARAMS_NODE)) {
// Split the key to configuration nodes separated with '/'
String[] nodes = entry.getKey().split("/");
// The plugin ID part may contain many IDs separated with comma ','
if (nodes.length > 1) {
auth_repo_params.put(nodes[1], entry.getValue().toString());
}
}
}
try {
String cls_name = (String)props.get(USER_REPO_CLASS_PROP_KEY);
String res_uri = (String)props.get(USER_REPO_URL_PROP_KEY);
user_repository = RepositoryFactory.getUserRepository(getName(),
cls_name, res_uri, user_repo_params);
log.config("Initialized " + cls_name + " as user repository: " + res_uri);
} catch (Exception e) {
log.log(Level.SEVERE, "Can't initialize user repository: ", e);
} // end of try-catch
try {
String cls_name = (String)props.get(AUTH_REPO_CLASS_PROP_KEY);
String res_uri = (String)props.get(AUTH_REPO_URL_PROP_KEY);
auth_repository = RepositoryFactory.getAuthRepository(getName(),
cls_name, res_uri, auth_repo_params);
log.config("Initialized " + cls_name + " as auth repository: " + res_uri);
} catch (Exception e) {
log.log(Level.SEVERE, "Can't initialize auth repository: ", e);
} // end of try-catch
naUserRepository = new NARepository(user_repository);
String[] plugins = (String[])props.get(PLUGINS_PROP_KEY);
processors.clear();
for (String comp_id: plugins) {
if (comp_id.equals("presence")) {
log.warning("Your configuration is outdated!"
+ " Note 'presence' and 'jaber:iq:roster' plugins are no longer exist."
+ " Use 'roster-presence' plugin instead, loading automaticly...");
comp_id = "roster-presence";
}
addPlugin(comp_id);
Map<String, Object> plugin_settings =
new ConcurrentSkipListMap<String, Object>();
for (Map.Entry<String, Object> entry: props.entrySet()) {
if (entry.getKey().startsWith(PLUGINS_CONF_PROP_KEY)) {
// Split the key to configuration nodes separated with '/'
String[] nodes = entry.getKey().split("/");
// The plugin ID part may contain many IDs separated with comma ','
if (nodes.length > 2) {
String[] ids = nodes[1].split(",");
Arrays.sort(ids);
if (Arrays.binarySearch(ids, comp_id) >= 0) {
plugin_settings.put(nodes[2], entry.getValue());
}
}
}
// if (entry.getKey().startsWith(PLUGINS_CONF_PROP_KEY + "/" + comp_id)) {
// plugin_settings.put(
// entry.getKey().substring((PLUGINS_CONF_PROP_KEY +
// "/" + comp_id + "/").length()), entry.getValue());
// }
}
if (plugin_settings.size() > 0) {
log.finest(plugin_settings.toString());
plugin_config.put(comp_id, plugin_settings);
}
} // end of for (String comp_id: plugins)
// hostnames = (String[])props.get(HOSTNAMES_PROP_KEY);
// clearRoutings();
// for (String host: hostnames) {
// addRouting(host);
//// XMPPResourceConnection conn = createUserSession(NULL_ROUTING, host, host);
//// conn.setDummy(true);
// } // end of for ()
// addRouting(getComponentId());
// anonymous_domains.clear();
// anonymous_domains.addAll(
// Arrays.asList((String[])props.get(ANONYMOUS_DOMAINS_PROP_KEY)));
// serverSession =
createUserSession(NULL_ROUTING, getDefHostName(),
getComponentId() + "/server");
admins = (String[])props.get(ADMINS_PROP_KEY);
trusted = (String[])props.get(TRUSTED_PROP_KEY);
//anon_peers = (String[])props.get(ANONYMOUS_PEERS_PROP_KEY);
//Arrays.sort(anon_peers);
}
public boolean handlesLocalDomains() {
return true;
}
protected void registerNewSession(String userId, XMPPResourceConnection conn) {
XMPPSession session = sessionsByNodeId.get(userId);
if (session == null) {
session = new XMPPSession(JIDUtils.getNodeNick(userId));
sessionsByNodeId.put(userId, session);
log.finest("Created new XMPPSession for: " + userId);
} // end of if (session == null)
session.addResourceConnection(conn);
}
public void handleLogin(String userName, XMPPResourceConnection conn) {
log.finest("handleLogin called for: " + userName);
String userId = JIDUtils.getNodeID(userName, conn.getDomain());
registerNewSession(userId, conn);
}
public void handleLogout(String userName, XMPPResourceConnection conn) {
String domain = conn.getDomain();
addOutPacket(Command.CLOSE.getPacket(getComponentId(),
conn.getConnectionId(), StanzaType.set, conn.nextStanzaId()));
String userId = JIDUtils.getNodeID(userName, domain);
XMPPSession session = sessionsByNodeId.get(userId);
if (session != null && session.getActiveResourcesSize() <= 1) {
sessionsByNodeId.remove(userId);
} // end of if (session.getActiveResourcesSize() == 0)
}
public Element getDiscoInfo(String node, String jid) {
if (jid != null && getName().equals(JIDUtils.getNodeNick(jid))) {
Element query = serviceEntity.getDiscoInfo(node);
for (ProcessorThread proc_t: processors.values()) {
Element[] discoFeatures = proc_t.processor.supDiscoFeatures(null);
if (discoFeatures != null) {
query.addChildren(Arrays.asList(discoFeatures));
} // end of if (discoFeatures != null)
}
return query;
}
return null;
}
public List<Element> getDiscoFeatures() {
List<Element> features = new LinkedList<Element>();
for (ProcessorThread proc_t: processors.values()) {
Element[] discoFeatures = proc_t.processor.supDiscoFeatures(null);
if (discoFeatures != null) {
features.addAll(Arrays.asList(discoFeatures));
} // end of if (discoFeatures != null)
}
return features;
}
public List<Element> getDiscoItems(String node, String jid) {
if (jid != null && getName().equals(JIDUtils.getNodeNick(jid))) {
return serviceEntity.getDiscoItems(node, jid);
} else {
// return
// Arrays.asList(serviceEntity.getDiscoItem(null, getName() + "." + jid));
return null;
}
}
public List<StatRecord> getStatistics() {
List<StatRecord> stats = super.getStatistics();
stats.add(new StatRecord(getName(), "Open connections", "int",
connectionsByFrom.size(), Level.FINE));
stats.add(new StatRecord(getName(), "Registered accounts", "long",
user_repository.getUsersCount(), Level.INFO));
stats.add(new StatRecord(getName(), "Open authorized sessions", "int",
sessionsByNodeId.size(), Level.INFO));
stats.add(new StatRecord(getName(), "Closed connections", "long",
closedConnections, Level.FINER));
return stats;
}
private class QueueItem {
Packet packet;
XMPPResourceConnection conn;
}
private class ProcessorThread extends Thread {
private boolean stopped = false;
private XMPPProcessorIfc processor = null;
private LinkedList<Packet> local_results = new LinkedList<Packet>();
private LinkedBlockingQueue<QueueItem> in_queue =
new LinkedBlockingQueue<QueueItem>(maxQueueSize);
public ProcessorThread(XMPPProcessorIfc processor) {
this.processor = processor;
}
public boolean addItem(Packet packet, XMPPResourceConnection conn) {
QueueItem item = new QueueItem();
item.packet = packet;
item.conn = conn;
return in_queue.offer(item);
}
public void run() {
QueueItem item = null;
while (! stopped) {
try {
item = in_queue.take();
if (item.conn != null) {
processor.process(item.packet, item.conn, naUserRepository,
local_results, plugin_config.get(processor.id()));
setPermissions(item.conn, local_results);
} else {
processor.process(item.packet, null, naUserRepository,
local_results, plugin_config.get(processor.id()));
}
addOutPackets(local_results);
} catch (Exception e) {
log.log(Level.SEVERE, "Exception during packet processing: "
+ item.packet.toString(), e);
}
}
}
}
private static class NARepository implements NonAuthUserRepository {
UserRepository rep = null;
NARepository(UserRepository userRep) {
rep = userRep;
}
private String calcNode(String base, String subnode) {
if (subnode == null) {
return base;
} // end of if (subnode == null)
return base + "/" + subnode;
}
public String getPublicData(String user, String subnode, String key,
String def) throws UserNotFoundException {
try {
return (rep.userExists(user) ?
rep.getData(user, calcNode(PUBLIC_DATA_NODE, subnode), key, def) :
null);
} catch (TigaseDBException e) {
log.log(Level.SEVERE, "Problem accessing repository data.", e);
return null;
} // end of try-catch
}
public String[] getPublicDataList(String user, String subnode, String key)
throws UserNotFoundException {
try {
return (rep.userExists(user) ?
rep.getDataList(user, calcNode(PUBLIC_DATA_NODE, subnode), key) :
null);
} catch (TigaseDBException e) {
log.log(Level.SEVERE, "Problem accessing repository data.", e);
return null;
} // end of try-catch
}
public void addOfflineDataList(String user, String subnode, String key,
String[] list) throws UserNotFoundException {
try {
if (rep.userExists(user)) {
rep.addDataList(user, calcNode(OFFLINE_DATA_NODE, subnode), key, list);
} else {
throw new UserNotFoundException("User: " + user
+ " has not been found inthe repository.");
}
} catch (UserNotFoundException e) {
log.warning("User not found in repository: " + user);
} catch (TigaseDBException e) {
log.log(Level.SEVERE, "Problem accessing repository data.", e);
} // end of try-catch
}
public void addOfflineData(String user, String subnode, String key,
String value) throws UserNotFoundException, DataOverwriteException {
String node = calcNode(OFFLINE_DATA_NODE, subnode);
try {
String data = rep.getData(user, node, key);
if (data == null) {
rep.setData(user, node, key, value);
} else {
throw new
DataOverwriteException("Not authorized attempt to overwrite data.");
} // end of if (data == null) else
} catch (TigaseDBException e) {
log.log(Level.SEVERE, "Problem accessing repository data.", e);
} // end of try-catch
}
}
}