Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OF-2745: Prevent idle but active occupants from being removed from a MUC room #2357

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ private void handle(IQ packet) {
// Communication is blocked
if (IQ.Type.set == packet.getType() || IQ.Type.get == packet.getType()) {
// Answer that the service is unavailable
Log.trace("Responding with 'service-unavailable' as IQ request blocked by privacy list, to: {}", packet);
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
}
return;
Expand All @@ -384,6 +385,7 @@ private void handle(IQ packet) {
if (handler == null) {
if (recipientJID == null) {
// Answer an error since the server can't handle the requested namespace
Log.trace("Responding with 'service-unavailable' since the server can't handle the requested namespace, to: {}", packet);
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
}
else if (recipientJID.getNode() == null ||
Expand All @@ -393,7 +395,7 @@ else if (recipientJID.getNode() == null ||
}
else {
// JID is of the form <node@domain>
// Answer an error since the server can't handle packets sent to a node
Log.trace("Responding with 'service-unavailable' since the server can't handle packets sent to a node, to: {}", packet);
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
}
}
Expand All @@ -408,13 +410,15 @@ else if (recipientJID.getNode() == null ||
// If the 'to' address specifies a bare JID <localpart@domainpart> or full JID <localpart@domainpart/resourcepart> where the domainpart of the JID matches a configured domain that is serviced by the server itself, the server MUST proceed as follows.
// If the user account identified by the 'to' attribute does not exist, how the stanza is processed depends on the stanza type.
if (packet.isRequest() && recipientJID != null && recipientJID.getNode() != null
&& !XMPPServer.getInstance().isRemote(recipientJID)
&& !userManager.isRegisteredUser(recipientJID, false)
&& !UserManager.isPotentialFutureLocalUser(recipientJID)
&& sessionManager.getSession(recipientJID) == null
&& !(recipientJID.asBareJID().equals(packet.getFrom().asBareJID()) && sessionManager.isPreAuthenticatedSession(packet.getFrom())) // A pre-authenticated session queries the server about itself.
)
{
// For an IQ stanza, the server MUST return a <service-unavailable/> stanza error to the sender.
Log.trace("Responding with 'service-unavailable' since there's no such local user that matches the addressee, to: {}", packet);
sendErrorPacket(packet, PacketError.Condition.service_unavailable);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ private void bounce(Message message) {
return;
}
try {
log.trace( "Bouncing a message stanza." );
log.trace( "Bouncing a message stanza: {}", message);

// Generate a rejection response to the sender
final Message errorResponse = message.createCopy();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2008 Jive Software, 2022 Ignite Realtime Foundation. All rights reserved.
* Copyright (C) 2005-2008 Jive Software, 2022-2023 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -97,6 +97,7 @@ public void storeOffline(Message message) {
Log.debug("Avoid generating an error in response to a stanza that itself is an error (to avoid the chance of entering an endless back-and-forth of exchanging errors). Suppress sending an {} error in response to: {}", PacketError.Condition.service_unavailable, message);
return;
}
Log.trace("Responding with 'service-unavailable' as the message was blocked by a privacy list, to: {}", message);
Message result = message.createCopy();
result.setTo(message.getFrom());
result.setFrom(message.getTo());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ private void bounce(Presence presence) {
return;
}
try {
Log.trace( "Bouncing a presence stanza." );
Log.trace( "Bouncing a presence stanza: {}", presence);

// Generate a rejection response to the sender
final Presence errorResponse = presence.createCopy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,13 @@ public void process(Packet packet) throws PacketException {
// For an IQ stanza, the server MUST return a <service-unavailable/> stanza error to the sender.
if (performNoSuchUserCheck()
&& iq.isRequest() && recipientJID != null && recipientJID.getNode() != null
&& !XMPPServer.getInstance().isRemote(recipientJID)
&& !UserManager.getInstance().isRegisteredUser(recipientJID, false)
&& !UserManager.isPotentialFutureLocalUser(recipientJID) && sessionManager.getSession(recipientJID) == null
&& !(recipientJID.asBareJID().equals(packet.getFrom().asBareJID()) && sessionManager.isPreAuthenticatedSession(packet.getFrom())) // A pre-authenticated session queries the server about itself.
)
{
Log.trace("Responding with 'service-unavailable' since the intended recipient isn't a local user ('no-such-user' check) to: {}", iq);
// For an IQ stanza, the server MUST return a <service-unavailable/> stanza error to the sender.
IQ response = IQ.createResultIQ(iq);
response.setChildElement(iq.getChildElement().createCopy());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2008 Jive Software. All rights reserved.
* Copyright (C) 2005-2008 Jive Software, Ignite Realtime Foundation 2023. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,8 @@
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
Expand All @@ -45,6 +47,7 @@
public class IQPrivacyHandler extends IQHandler
implements ServerFeaturesProvider, UserEventListener {

private static final Logger Log = LoggerFactory.getLogger(IQPrivacyHandler.class);
private IQHandlerInfo info;
private PrivacyListManager manager = PrivacyListManager.getInstance();
private PrivacyListProvider provider = PrivacyListProvider.getInstance();
Expand All @@ -60,6 +63,7 @@ public IQ handleIQ(IQ packet) throws UnauthorizedException {
JID from = packet.getFrom();
if (from.getNode() == null || !UserManager.getInstance().isRegisteredUser(from, false)) {
// Service is unavailable for anonymous users
Log.trace("Responding with 'service-unavailable': service unavailable to anonymous users: {}", packet);
IQ result = IQ.createResultIQ(packet);
result.setChildElement(packet.getChildElement().createCopy());
result.setError(PacketError.Condition.service_unavailable);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
* Copyright (C) 2004-2008 Jive Software, 2023 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,8 @@
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.disco.ServerFeaturesProvider;
import org.jivesoftware.openfire.user.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.PacketError;

Expand Down Expand Up @@ -58,6 +60,8 @@
*/
public class IQPrivateHandler extends IQHandler implements ServerFeaturesProvider {

private static final Logger Log = LoggerFactory.getLogger(IQPrivacyHandler.class);

public static final String NAMESPACE = "jabber:iq:private";

private IQHandlerInfo info;
Expand All @@ -76,6 +80,7 @@ public IQ handleIQ(IQ packet) throws UnauthorizedException, PacketException {
Element dataElement = child.elementIterator().next();

if ( !UserManager.getInstance().isRegisteredUser( packet.getFrom(), false ) ) {
Log.trace("Responding with 'service-unavailable': service unavailable to non-local, unregistered users: {}", packet);
replyPacket.setChildElement(packet.getChildElement().createCopy());
replyPacket.setError(PacketError.Condition.service_unavailable);
replyPacket.getError().setText( "Service available only to locally registered users." );
Expand All @@ -96,6 +101,7 @@ public IQ handleIQ(IQ packet) throws UnauthorizedException, PacketException {
if (privateStorage.isEnabled()) {
privateStorage.add(packet.getFrom().getNode(), dataElement);
} else {
Log.trace("Responding with 'service-unavailable': private storage is not enabled: {}", packet);
replyPacket.setChildElement(packet.getChildElement().createCopy());
replyPacket.setError(PacketError.Condition.service_unavailable);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2008 Jive Software, 2022 Ignite Realtime Foundation. All rights reserved.
* Copyright (C) 2005-2008 Jive Software, 2022-2023 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -233,6 +233,7 @@ private void processIQ(IQ iq) {
}
} else {
// Answer an error since the server can't handle the requested namespace
Log.trace("Responding with 'service-unavailable': the server can't handle the requested namespace: {}", iq);
reply.setError(PacketError.Condition.service_unavailable);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ else if (this.iqHandlers != null) {
XMPPServer.getInstance().getPacketRouter().route(reply);
}
} catch (final UnauthorizedException e) {
Log.trace("Responding with 'service-unavailable' due to authorization exception, to: {}", iq, e);
final IQ reply = IQ.createResultIQ(iq);
reply.setType(IQ.Type.error);
reply.setError(PacketError.Condition.service_unavailable);
Expand Down Expand Up @@ -1735,7 +1736,6 @@ private void pingOccupant(@Nonnull final OccupantManager.Occupant occupant)
pingRequest.setFrom( occupant.getRoomName() + "@" + getServiceDomain() );
pingRequest.setTo( occupant.getRealJID() );
pingRequest.setID( UUID.randomUUID().toString() ); // Generate unique ID, to prevent duplicate cache entries.
XMPPServer.getInstance().getPacketRouter().route(pingRequest);
PINGS_SENT.put(pingRequest.getID(), pingRequest.getTo());

// Schedule a check to see if the ping was answered, kicking the occupant if it wasn't.
Expand All @@ -1744,6 +1744,8 @@ private void pingOccupant(@Nonnull final OccupantManager.Occupant occupant)
final CheckPingResponseTask task = new CheckPingResponseTask(occupant, pingRequest.getID());
occupant.setPendingPingTask(task);
TaskEngine.getInstance().schedule(task, timeoutMs);

XMPPServer.getInstance().getPacketRouter().route(pingRequest);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ public boolean isEnabled() {
public IQ handleIQ(IQ packet) {
// Do nothing if server is not enabled
if (!ENABLED.getValue()) {
Log.trace("Responding with 'service-unavailable' since to service is not enabled, to: {}", packet);
IQ reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.service_unavailable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ private void process(IQ iq) {
IQ reply = iqDiscoInfoHandler.handleIQ(iq);
router.route(reply);
} else {
Log.trace("Responding with 'service-unavailable' due to unavailable Disco Info Handler to: {}", iq);
sendServiceUnavailablePacket(iq);
return;
}
Expand All @@ -220,13 +221,15 @@ else if ("http://jabber.org/protocol/disco#items".equals(namespace)) {
IQ reply = iqDiscoItemsHandler.handleIQ(iq);
router.route(reply);
} else {
Log.trace("Responding with 'service-unavailable' due to unavailable Disco Items Handler to: {}", iq);
sendServiceUnavailablePacket(iq);
return;
}

}
else {
// Unknown namespace requested so return error to sender
Log.trace("Responding with 'service-unavailable' due to unknown namespace in request to: {}", iq);
sendServiceUnavailablePacket(iq);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,12 +426,14 @@ public void process(Packet packet) {
result.setTo(message.getFrom());
result.setFrom(message.getTo());
result.setError(PacketError.Condition.service_unavailable);
Log.trace("Responding with 'service-unavailable' as message cannot be processed to: {}", packet);
XMPPServer.getInstance().getPacketRouter().route(result);
} else if (packet instanceof IQ) {
// For IQ stanzas of type "get" or "set", the server MUST return an error, which SHOULD be <service-unavailable/>.
// IQ stanzas of other types MUST be silently dropped by the server.
IQ iq = (IQ) packet;
if (iq.getType() == IQ.Type.get || iq.getType() == IQ.Type.set) {
Log.trace("Responding with 'service-unavailable' as IQ request cannot be processed to: {}", packet);
IQ result = IQ.createResultIQ(iq);
result.setError(PacketError.Condition.service_unavailable);
XMPPServer.getInstance().getPacketRouter().route(result);
Expand Down
Loading