/*
* Copyright 2006-2010 Daniel Henninger. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package net.sf.kraken.muc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.kraken.BaseTransport;
import net.sf.kraken.roster.TransportBuddy;
import net.sf.kraken.session.TransportSession;
import net.sf.kraken.type.NameSpace;
import org.apache.log4j.Logger;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.NotFoundException;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentManager;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.Presence;
import org.xmpp.packet.PacketError.Condition;
/**
* Base class for all MUC transports.
*
* This class should be implemented to provide support for MUC in a transport. It
* is attached to a BaseTransport implementation and the two work together to handle
* session management and such. Generally a good chunk of the work will still be done
* by the BaseTransport based pieces.
*
* @author Daniel Henninger
*/
public abstract class BaseMUCTransport<B extends TransportBuddy> implements Component {
static Logger Log = Logger.getLogger(BaseMUCTransport.class);
/**
* Create a new BaseMUCTransport instance.
*
* @param transport Transport to associate with this MUC transport.
*/
public BaseMUCTransport(BaseTransport<B> transport) {
this.transport = transport;
requestWatcher = new RequestWatcher();
timer.schedule(requestWatcher, requestCheckInterval, requestCheckInterval);
}
/* The transport we are associated with. */
public BaseTransport<B> transport;
/**
* Retrieves the transport we are associated with.
*
* @return Transport we are associated with.
*/
public BaseTransport<B> getTransport() {
return transport;
}
/**
* Retrieves the name of the MUC transport.
* @see org.xmpp.component.Component#getName()
*/
public String getName() {
return getTransport().getName();
}
/**
* Retrieves the description of the MUC transport.
* @see org.xmpp.component.Component#getDescription()
*/
public String getDescription() {
return getTransport().getDescription();
}
/**
* Returns the jid of the MUC transport.
*
* @return the jid of the MUC transport.
*/
public JID getJID() {
return this.jid;
}
/* List of IQ requests that are waiting on a response. */
ConcurrentHashMap<IQ,Date> pendingIQRequests = new ConcurrentHashMap<IQ,Date>();
/**
* Stores a pending request.
*
* @param packet IQ packet that we are storing.
*/
public void storePendingRequest(IQ packet) {
pendingIQRequests.put(packet, new Date());
}
/**
* Retrieves a pending request or null if no such request is found.
*
* @param from JID that the request was originally sent from.
* @param to JID that the request was originally sent to.
* @param namespace IQ namespace to identify request.
* @return Matching pending request or null.
*/
public IQ getPendingRequest(JID from, JID to, String namespace) {
for (IQ request : pendingIQRequests.keySet()) {
if (request.getTo().equals(to) && request.getFrom().toBareJID().equals(from.toBareJID())) {
Element child = request.getChildElement();
if (child != null) {
String xmlns = child.getNamespaceURI();
if (xmlns.equals(namespace)) {
pendingIQRequests.remove(request);
return request;
}
}
}
}
return null;
}
/**
* Retires a pending request.
*
* @param from JID that the request was originally sent from.
* @param to JID that the request was originally sent to.
* @param namespace IQ namespace to identify request.
*/
public void cancelPendingRequest(JID from, JID to, String namespace) {
for (IQ request : pendingIQRequests.keySet()) {
if (request.getTo().equals(to) && request.getFrom().toBareJID().equals(from.toBareJID())) {
Element child = request.getChildElement();
if (child != null) {
String xmlns = child.getNamespaceURI();
if (xmlns.equals(namespace)) {
IQ result = IQ.createResultIQ(request);
result.setError(PacketError.Condition.item_not_found);
getTransport().sendPacket(result);
pendingIQRequests.remove(request);
return;
}
}
}
}
}
/**
* Expires a pending request.
*
* @param request The request that will be expired
*/
public void expirePendingRequest(IQ request) {
IQ result = IQ.createResultIQ(request);
result.setError(PacketError.Condition.remote_server_timeout);
getTransport().sendPacket(result);
pendingIQRequests.remove(request);
}
/**
* Expires all pending requests that have timed out.
*/
public void checkPendingExpirations() {
Date now = new Date();
now.setTime(now.getTime() - requestTimeout);
for (IQ request : pendingIQRequests.keySet()) {
Date when = pendingIQRequests.get(request);
if (now.after(when)) {
expirePendingRequest(request);
}
}
}
/**
* Timer to check for IQ request expirations.
*/
private Timer timer = new Timer();
/**
* Interval at which requests are checked.
*/
private int requestCheckInterval = 10000; // 10 seconds
/**
* How long before requests time out.
*/
private int requestTimeout = 30000; // 30 seconds
/**
* The actual request checker task.
*/
private RequestWatcher requestWatcher;
/**
* Check for expired IQ requests.
*/
private class RequestWatcher extends TimerTask {
/**
* Expire any requests that have timed out.
*/
@Override
public void run() {
checkPendingExpirations();
}
}
/* Cached list of rooms from IRC */
final public ConcurrentHashMap<String,MUCTransportRoom> roomCache = new ConcurrentHashMap<String,MUCTransportRoom>();
/* Last room list update */
public Date roomCacheLastUpdate = null;
/* How long before the room cache is considered expired. */
private int roomCacheTimeout = 600000; // 10 minutes
/**
* Updates the room cache update timestamp.
*/
public void updateRoomCacheTimestamp() {
roomCacheLastUpdate = new Date();
}
/**
* Resets (clears) the room cache.
*/
public void clearRoomCache() {
roomCache.clear();
}
/**
* Retrieve cached information about a room.
*
* @param room Name of room to retrieve information about.
* @return MUCTransportRoom instance for room.
*/
public MUCTransportRoom getCachedRoom(String room) {
return roomCache.get(room.toLowerCase());
}
/**
* Stores an entry in the room cache.
*
* @param room MUCTransportRoom instance that we'll be storing.
*/
public void cacheRoom(MUCTransportRoom room) {
roomCache.put(room.getName().toLowerCase(), room);
}
/**
* Retrieves a list of all cached rooms.
*
* @return Collection of all cached rooms.
*/
public Collection<MUCTransportRoom> getCachedRooms() {
return roomCache.values();
}
/**
* Returns whether we need to trigger a list update.
*
* @return True or false if we have passed the timeout period.
*/
public Boolean isRoomCacheOutOfDate() {
Date timeout = new Date();
timeout.setTime(timeout.getTime() - roomCacheTimeout);
return (roomCacheLastUpdate == null || roomCacheLastUpdate.before(timeout));
}
/**
* Handles all incoming XMPP stanzas, passing them to individual
* packet type handlers.
*
* @param packet The packet to be processed.
*/
public void processPacket(Packet packet) {
try {
List<Packet> reply = new ArrayList<Packet>();
if (packet instanceof IQ) {
reply.addAll(processPacket((IQ)packet));
}
else if (packet instanceof Presence) {
reply.addAll(processPacket((Presence)packet));
}
else if (packet instanceof Message) {
reply.addAll(processPacket((Message)packet));
}
else {
Log.debug("Received an unhandled packet: " + packet.toString());
}
if (reply.size() > 0) {
for (Packet p : reply) {
this.sendPacket(p);
}
}
}
catch (Exception e) {
Log.warn("Error occured while processing packet:", e);
}
}
/**
* Handles all incoming message stanzas.
*
* @param packet The message packet to be processed.
* @return list of packets that will be sent back to the message sender.
*/
private List<Packet> processPacket(Message packet) {
Log.debug("Received message packet: "+packet.toXML());
List<Packet> reply = new ArrayList<Packet>();
JID from = packet.getFrom();
JID to = packet.getTo();
try {
TransportSession<B> session = getTransport().getSessionManager().getSession(from);
if (!session.isLoggedIn()) {
Message m = new Message();
m.setError(Condition.service_unavailable);
m.setTo(from);
m.setFrom(getJID());
m.setBody(LocaleUtils.getLocalizedString("gateway.base.notloggedin", "kraken", Arrays.asList(getTransport().getType().toString().toUpperCase())));
reply.add(m);
}
else if (to.getNode() == null) {
// Message to gateway itself. Throw away for now.
}
else {
MUCTransportSession mucSession = session.getMUCSessionManager().getSession(to.getNode());
if (packet.getBody() != null) {
if (to.getResource() == null) {
// Message to room
mucSession.sendMessage(packet.getBody());
}
else {
// Private message
mucSession.sendPrivateMessage(to.getResource(), packet.getBody());
}
}
else {
if (packet.getSubject() != null) {
// Set topic of room
mucSession.updateTopic(packet.getSubject());
}
}
}
}
catch (NotFoundException e) {
Log.debug("Unable to find session.");
Message m = new Message();
m.setError(Condition.service_unavailable);
m.setTo(from);
m.setFrom(getJID());
m.setBody(LocaleUtils.getLocalizedString("gateway.base.notloggedin", "kraken", Arrays.asList(getTransport().getType().toString().toUpperCase())));
reply.add(m);
}
return reply;
}
/**
* Handles all incoming presence stanzas.
*
* @param packet The presence packet to be processed.
* @return list of packets that will be sent back to the presence requester.
*/
private List<Packet> processPacket(Presence packet) {
Log.debug("Received presence packet: "+packet.toXML());
List<Packet> reply = new ArrayList<Packet>();
JID from = packet.getFrom();
JID to = packet.getTo();
if (packet.getType() == Presence.Type.error) {
// We don't want to do anything with this. Ignore it.
return reply;
}
try {
TransportSession<B> session = getTransport().getSessionManager().getSession(from);
if (!session.isLoggedIn()) {
Message m = new Message();
m.setError(Condition.service_unavailable);
m.setTo(from);
m.setFrom(getJID());
m.setBody(LocaleUtils.getLocalizedString("gateway.base.notloggedin", "kraken", Arrays.asList(getTransport().getType().toString().toUpperCase())));
reply.add(m);
}
else if (to.getNode() == null) {
// Ignore undirected presence.
}
else if (to.getResource() != null) {
// Presence to a specific resource.
if (packet.getType() == Presence.Type.unavailable) {
// Handle logout.
try {
MUCTransportSession mucSession = session.getMUCSessionManager().getSession(to.getNode());
mucSession.leaveRoom();
session.getMUCSessionManager().removeSession(to.getNode());
}
catch (NotFoundException e) {
// Not found? Well then no problem.
}
}
else {
// Handle login.
try {
MUCTransportSession mucSession = session.getMUCSessionManager().getSession(to.getNode());
// Active session.
mucSession.updateStatus(this.getTransport().getPresenceType(packet));
}
catch (NotFoundException e) {
// No current session, lets create one.
MUCTransportSession mucSession = createRoom(session, to.getNode(), to.getResource());
session.getMUCSessionManager().storeSession(to.getNode(), mucSession);
mucSession.enterRoom();
}
}
}
else {
// Presence to the room itself. Return error as per protocol.
Presence p = new Presence();
p.setError(Condition.jid_malformed);
p.setType(Presence.Type.error);
p.setTo(from);
p.setFrom(to);
reply.add(p);
}
}
catch (NotFoundException e) {
Log.debug("Unable to find session.");
Message m = new Message();
m.setError(Condition.service_unavailable);
m.setTo(from);
m.setFrom(getJID());
m.setBody(LocaleUtils.getLocalizedString("gateway.base.notloggedin", "kraken", Arrays.asList(getTransport().getType().toString().toUpperCase())));
reply.add(m);
}
return reply;
}
/**
* Handles all incoming iq stanzas.
*
* @param packet The iq packet to be processed.
* @return list of packets that will be sent back to the IQ requester.
*/
private List<Packet> processPacket(IQ packet) {
Log.debug("Received iq packet: "+packet.toXML());
List<Packet> reply = new ArrayList<Packet>();
if (packet.getType() == IQ.Type.error) {
// Lets not start a loop. Ignore.
return reply;
}
String xmlns = null;
Element child = (packet).getChildElement();
if (child != null) {
xmlns = child.getNamespaceURI();
}
if (xmlns == null) {
// No namespace defined.
Log.debug("No XMLNS:" + packet.toString());
IQ error = IQ.createResultIQ(packet);
error.setError(PacketError.Condition.bad_request);
reply.add(error);
return reply;
}
if (xmlns.equals(NameSpace.DISCO_INFO)) {
reply.addAll(handleDiscoInfo(packet));
}
else if (xmlns.equals(NameSpace.DISCO_ITEMS)) {
reply.addAll(handleDiscoItems(packet));
}
else if (xmlns.equals(NameSpace.MUC_ADMIN)) {
reply.addAll(handleMUCAdmin(packet));
}
else if (xmlns.equals(NameSpace.MUC_USER)) {
reply.addAll(handleMUCUser(packet));
}
else {
Log.debug("Unable to handle iq request: " + xmlns);
IQ error = IQ.createResultIQ(packet);
error.setError(PacketError.Condition.service_unavailable);
reply.add(error);
}
return reply;
}
/**
* Handle service discovery info request.
*
* @param packet An IQ packet in the disco info namespace.
* @return A list of IQ packets to be returned to the user.
*/
private List<Packet> handleDiscoInfo(IQ packet) {
List<Packet> reply = new ArrayList<Packet>();
JID from = packet.getFrom();
JID to = packet.getTo();
if (packet.getTo().getNode() == null) {
// Requested info from transport itself.
IQ result = IQ.createResultIQ(packet);
if (from.getNode() == null || getTransport().permissionManager.hasAccess(from)) {
Element response = DocumentHelper.createElement(QName.get("query", NameSpace.DISCO_INFO));
response.addElement("identity")
.addAttribute("category", "conference")
.addAttribute("type", "text")
.addAttribute("name", this.getDescription());
response.addElement("feature")
.addAttribute("var", NameSpace.DISCO_INFO);
response.addElement("feature")
.addAttribute("var", NameSpace.DISCO_ITEMS);
response.addElement("feature")
.addAttribute("var", NameSpace.MUC);
result.setChildElement(response);
}
else {
result.setError(PacketError.Condition.forbidden);
}
reply.add(result);
}
else {
// Ah, a request for information about a room.
IQ result = IQ.createResultIQ(packet);
try {
TransportSession<B> session = getTransport().getSessionManager().getSession(from);
if (session.isLoggedIn()) {
storePendingRequest(packet);
session.getRoomInfo(getTransport().convertJIDToID(to));
}
else {
// Not logged in? Not logged in then.
result.setError(PacketError.Condition.forbidden);
reply.add(result);
}
}
catch (NotFoundException e) {
// Not found? No active session then.
result.setError(PacketError.Condition.forbidden);
reply.add(result);
}
}
return reply;
}
/**
* Handle service discovery items request.
*
* @param packet An IQ packet in the disco items namespace.
* @return A list of IQ packets to be returned to the user.
*/
private List<Packet> handleDiscoItems(IQ packet) {
List<Packet> reply = new ArrayList<Packet>();
JID from = packet.getFrom();
JID to = packet.getTo();
if (packet.getTo().getNode() == null) {
// A request for a list of rooms
IQ result = IQ.createResultIQ(packet);
if (JiveGlobals.getBooleanProperty("plugin.gateway."+getTransport().getType()+".roomlist", false)) {
try {
TransportSession<B> session = getTransport().getSessionManager().getSession(from);
if (session.isLoggedIn()) {
storePendingRequest(packet);
session.getRooms();
}
}
catch (NotFoundException e) {
// Not found? No active session then.
result.setError(PacketError.Condition.forbidden);
reply.add(result);
}
}
else {
// Time to lie and tell them we have no rooms
sendRooms(from, new ArrayList<MUCTransportRoom>());
}
}
else {
// Ah, a request for members of a room.
IQ result = IQ.createResultIQ(packet);
try {
TransportSession<B> session = getTransport().getSessionManager().getSession(from);
if (session.isLoggedIn()) {
storePendingRequest(packet);
session.getRoomMembers(getTransport().convertJIDToID(to));
}
else {
// Not logged in? Not logged in then.
result.setError(PacketError.Condition.forbidden);
reply.add(result);
}
}
catch (NotFoundException e) {
// Not found? No active session then.
result.setError(PacketError.Condition.forbidden);
reply.add(result);
}
}
return reply;
}
/**
* Handle MUC admin requests.
*
* @param packet An IQ packet in the MUC admin namespace.
* @return A list of IQ packets to be returned to the user.
*/
private List<Packet> handleMUCAdmin(IQ packet) {
List<Packet> reply = new ArrayList<Packet>();
JID from = packet.getFrom();
JID to = packet.getTo();
Element query = (packet).getChildElement();
Element item = query.element("item");
String nick = item.attribute("nick").getText();
String role = item.attribute("role").getText();
try {
TransportSession<B> session = getTransport().getSessionManager().getSession(from);
if (session.isLoggedIn()) {
try {
MUCTransportSession mucSession = session.getMUCSessionManager().getSession(to.getNode());
if (packet.getTo().getNode() == null) {
// Targeted at a room.
}
else {
// Targeted at a specific user.
if (nick != null && role != null) {
if (role.equals("none")) {
// This is a kick
String reason = "";
Element reasonElem = item.element("reason");
if (reasonElem != null) {
reason = reasonElem.getText();
}
mucSession.kickUser(nick, reason);
}
}
}
}
catch (NotFoundException e) {
// Not found? No active session then.
}
}
}
catch (NotFoundException e) {
// Not found? No active session then.
}
return reply;
}
/**
* Handle MUC user requests.
*
* @param packet An IQ packet in the MUC admin namespace.
* @return A list of IQ packets to be returned to the user.
*/
private List<Packet> handleMUCUser(IQ packet) {
List<Packet> reply = new ArrayList<Packet>();
// JID from = packet.getFrom();
// JID to = packet.getTo();
if (packet.getTo().getNode() == null) {
// Targeted at a room.
}
else {
// Targeted at a specific user.
}
return reply;
}
/**
* Sends a list of rooms as a response to a service discovery request.
*
* @param to JID we will be sending the response to.
* @param rooms List of MUCTransportRoom objects to send as a response.
*/
public void sendRooms(JID to, Collection<MUCTransportRoom> rooms) {
IQ request = getPendingRequest(to, this.getJID(), NameSpace.DISCO_ITEMS);
if (request != null) {
IQ result = IQ.createResultIQ(request);
Element response = DocumentHelper.createElement(QName.get("query", NameSpace.DISCO_ITEMS));
for (MUCTransportRoom room : rooms) {
Element item = response.addElement("item");
item.addAttribute("jid", room.getJid().toBareJID());
item.addAttribute("name", room.getName());
}
result.setChildElement(response);
this.sendPacket(result);
}
}
/**
* Sends information about a room as a response to a service discovery request.
*
* @param to JID we will be sending the response to.
* @param roomjid JID of the room info was requested about.
* @param room A MUCTransportRoom object containing information to return as a response.
*/
public void sendRoomInfo(JID to, JID roomjid, MUCTransportRoom room) {
IQ request = getPendingRequest(to, roomjid, NameSpace.DISCO_INFO);
if (request != null) {
IQ result = IQ.createResultIQ(request);
Element response = DocumentHelper.createElement(QName.get("query", NameSpace.DISCO_INFO));
response.addElement("identity")
.addAttribute("category", "conference")
.addAttribute("type", "text")
.addAttribute("name", room.getName());
response.addElement("feature")
.addAttribute("var", NameSpace.MUC);
response.addElement("feature")
.addAttribute("var", NameSpace.DISCO_INFO);
response.addElement("feature")
.addAttribute("var", NameSpace.DISCO_ITEMS);
if (room.getPassword_protected()) {
response.addElement("feature")
.addAttribute("var", "muc_passwordprotected");
}
if (room.getHidden()) {
response.addElement("feature")
.addAttribute("var", "muc_hidden");
}
if (room.getTemporary()) {
response.addElement("feature")
.addAttribute("var", "muc_temporary");
}
if (room.getOpen()) {
response.addElement("feature")
.addAttribute("var", "muc_open");
}
if (!room.getModerated()) {
response.addElement("feature")
.addAttribute("var", "muc_unmoderated");
}
if (!room.getAnonymous()) {
response.addElement("feature")
.addAttribute("var", "muc_nonanonymous");
}
Element form = DocumentHelper.createElement(QName.get("x", NameSpace.XDATA));
form.addAttribute("type", "result");
form.addElement("field")
.addAttribute("var", "FORM_TYPE")
.addAttribute("type", "hidden")
.addElement("value")
.addCDATA("http://jabber.org/protocol/muc#roominfo");
if (room.getContact() != null) {
form.addElement("field")
.addAttribute("var", "muc#roominfo_contactjid")
.addAttribute("label", "Contact Addresses")
.addElement("value")
.addCDATA(room.getContact().toString());
}
if (room.getName() != null) {
form.addElement("field")
.addAttribute("var", "muc#roominfo_description")
.addAttribute("label", "Short Description of Room")
.addElement("value")
.addCDATA(room.getName());
}
if (room.getLanguage() != null) {
form.addElement("field")
.addAttribute("var", "muc#roominfo_lang")
.addAttribute("label", "Natural Language for Room Discussions")
.addElement("value")
.addCDATA(room.getLanguage());
}
if (room.getLog_location() != null) {
form.addElement("field")
.addAttribute("var", "muc#roominfo_logs")
.addAttribute("label", "URL for Archived Discussion Logs")
.addElement("value")
.addCDATA(room.getLog_location());
}
if (room.getOccupant_count() != null) {
form.addElement("field")
.addAttribute("var", "muc#roominfo_occupants")
.addAttribute("label", "Current Number of Occupants in Room")
.addElement("value")
.addCDATA(room.getOccupant_count().toString());
}
if (room.getTopic() != null) {
form.addElement("field")
.addAttribute("var", "muc#roominfo_subject")
.addAttribute("label", "Current Subject or Discussion Topic in Room")
.addElement("value")
.addCDATA(room.getTopic());
}
response.add(form);
result.setChildElement(response);
this.sendPacket(result);
}
}
/**
* Sends a list of rooms as a response to a service discovery request.
*
* @param to JID we will be sending the response to.
* @param roomjid JID of the room info was requested about.
* @param members List of MUCTransportRoomMember objects to send as a response.
*/
public void sendRoomMembers(JID to, JID roomjid, List<MUCTransportRoomMember> members) {
IQ request = getPendingRequest(to, roomjid, NameSpace.DISCO_ITEMS);
if (request != null) {
IQ result = IQ.createResultIQ(request);
Element response = DocumentHelper.createElement(QName.get("query", NameSpace.DISCO_ITEMS));
for (MUCTransportRoomMember member : members) {
Element item = response.addElement("item");
item.addAttribute("jid", member.getJid().toBareJID());
}
result.setChildElement(response);
this.sendPacket(result);
}
}
/**
* Sends a packet through the component manager as the component.
*
* @param packet Packet to be sent.
*/
public void sendPacket(Packet packet) {
Log.debug(getName()+": Sending packet: "+packet.toXML());
try {
this.componentManager.sendPacket(this, packet);
}
catch (Exception e) {
Log.warn("Failed to deliver packet: " + packet.toString());
}
}
/**
* Sends a simple message through he component manager.
*
* @param to Who the message is for.
* @param from Who the message is from.
* @param msg Message to be send.
* @param type Type of message to be sent.
*/
public void sendMessage(JID to, JID from, String msg, Message.Type type) {
Message m = new Message();
m.setType(type);
m.setFrom(from);
m.setTo(to);
m.setBody(msg);
sendPacket(m);
}
/**
* Sends a simple message through the component manager.
*
* @param to Who the message is for.
* @param from Who the message is from.
* @param msg Message to be send.
*/
public void sendMessage(JID to, JID from, String msg) {
sendMessage(to, from, msg, Message.Type.chat);
}
/**
* Initializes the MUC transport.
* @see org.xmpp.component.Component#initialize(org.xmpp.packet.JID, org.xmpp.component.ComponentManager)
*/
public void initialize(JID jid, ComponentManager componentManager) {
this.jid = jid;
this.componentManager = componentManager;
}
/**
* JID of the transport in question.
*/
public JID jid = null;
/**
* Component Manager associated with transport.
*/
public ComponentManager componentManager = null;
/**
* Starts the MUC transport.
* @see org.xmpp.component.Component#start()
*/
public void start() {
}
/**
* Stops the MUC transport.
* @see org.xmpp.component.Component#shutdown()
*/
public void shutdown() {
requestWatcher.cancel();
timer.cancel();
}
/**
* Converts a legacy chatroom (and optional username) to a JID.
*
* @param roomname Name of room to be converted.
* @param username Username to be converted to a JID.
* @return The legacy username as a JID.
*/
public JID convertIDToJID(String roomname, String username) {
if (JiveGlobals.getBooleanProperty("plugin.gateway.tweak.percenthack", false)) {
return new JID(roomname.replace('@', '%').replace(" ", ""), this.jid.getDomain(), username);
}
else {
return new JID(JID.escapeNode(roomname.replace(" ", "")), this.jid.getDomain(), username);
}
}
/**
* Handles creation of a connection to a room.
*
* This is expected to create the connection to a remote chat room and return the session
* that will maintain it.
*
* @param transportSession Transport session we are attaching to.
* @param roomname Name of room to connect to.
* @param nickname Nickname to use in room.
* @return Session instance that will handle the room interactions.
*/
public abstract MUCTransportSession createRoom(TransportSession<B> transportSession, String roomname, String nickname);
}