📄 stanzahandler.java
字号:
/**
* $Revision: $
* $Date: $
*
* Copyright (C) 2008 Jive Software. 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, or a commercial license
* agreement with Jive.
*/
package org.jivesoftware.openfire.net;
import org.dom4j.Element;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.PacketRouter;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.*;
import java.io.IOException;
import java.io.StringReader;
/**
* A StanzaHandler is the main responsible for handling incoming stanzas. Some stanzas like startTLS
* are totally managed by this class. The rest of the stanzas are just forwarded to the router.
*
* @author Gaston Dombiak
*/
public abstract class StanzaHandler {
/**
* The utf-8 charset for decoding and encoding Jabber packet streams.
*/
protected static String CHARSET = "UTF-8";
protected Connection connection;
// DANIELE: Indicate if a session is already created
private boolean sessionCreated = false;
// Flag that indicates that the client requested to use TLS and TLS has been negotiated. Once the
// client sent a new initial stream header the value will return to false.
private boolean startedTLS = false;
// Flag that indicates that the client requested to be authenticated. Once the
// authentication process is over the value will return to false.
private boolean startedSASL = false;
/**
* SASL status based on the last SASL interaction
*/
private SASLAuthentication.Status saslStatus;
// DANIELE: Indicate if a stream:stream is arrived to complete compression
private boolean waitingCompressionACK = false;
/**
* Session associated with the socket reader.
*/
protected LocalSession session;
/**
* Server name for which we are attending clients.
*/
protected String serverName;
/**
* Router used to route incoming packets to the correct channels.
*/
private PacketRouter router;
/**
* Creates a dedicated reader for a socket.
*
* @param router the router for sending packets that were read.
* @param serverName the name of the server this socket is working for.
* @param connection the connection being read.
*/
public StanzaHandler(PacketRouter router, String serverName, Connection connection) {
this.serverName = serverName;
this.router = router;
this.connection = connection;
}
public void process(String stanza, XMPPPacketReader reader) throws Exception {
boolean initialStream = stanza.startsWith("<stream:stream") || stanza.startsWith("<flash:stream");
if (!sessionCreated || initialStream) {
if (!initialStream) {
// Ignore <?xml version="1.0"?>
return;
}
// Found an stream:stream tag...
if (!sessionCreated) {
sessionCreated = true;
MXParser parser = reader.getXPPParser();
parser.setInput(new StringReader(stanza));
createSession(parser);
}
else if (startedTLS) {
startedTLS = false;
tlsNegotiated();
}
else if (startedSASL && saslStatus == SASLAuthentication.Status.authenticated) {
startedSASL = false;
saslSuccessful();
}
else if (waitingCompressionACK) {
waitingCompressionACK = false;
compressionSuccessful();
}
return;
}
// Verify if end of stream was requested
if (stanza.equals("</stream:stream>")) {
session.close();
return;
}
// Ignore <?xml version="1.0"?> stanzas sent by clients
if (stanza.startsWith("<?xml")) {
return;
}
// Create DOM object from received stanza
Element doc = reader.read(new StringReader(stanza)).getRootElement();
if (doc == null) {
// No document found.
return;
}
String tag = doc.getName();
if ("starttls".equals(tag)) {
// Negotiate TLS
if (negotiateTLS()) {
startedTLS = true;
}
else {
connection.close();
session = null;
}
}
else if ("auth".equals(tag)) {
// User is trying to authenticate using SASL
startedSASL = true;
// Process authentication stanza
saslStatus = SASLAuthentication.handle(session, doc);
}
else if (startedSASL && "response".equals(tag)) {
// User is responding to SASL challenge. Process response
saslStatus = SASLAuthentication.handle(session, doc);
}
else if ("compress".equals(tag)) {
// Client is trying to initiate compression
if (compressClient(doc)) {
// Compression was successful so open a new stream and offer
// resource binding and session establishment (to client sessions only)
waitingCompressionACK = true;
}
}
else {
process(doc);
}
}
private void process(Element doc) throws UnauthorizedException {
if (doc == null) {
return;
}
// Ensure that connection was secured if TLS was required
if (connection.getTlsPolicy() == Connection.TLSPolicy.required &&
!connection.isSecure()) {
closeNeverSecuredConnection();
return;
}
String tag = doc.getName();
if ("message".equals(tag)) {
Message packet;
try {
packet = new Message(doc, !validateJIDs());
}
catch (IllegalArgumentException e) {
Log.debug("Rejecting packet. JID malformed", e);
// The original packet contains a malformed JID so answer with an error.
Message reply = new Message();
reply.setID(doc.attributeValue("id"));
reply.setTo(session.getAddress());
reply.getElement().addAttribute("from", doc.attributeValue("to"));
reply.setError(PacketError.Condition.jid_malformed);
session.process(reply);
return;
}
processMessage(packet);
}
else if ("presence".equals(tag)) {
Presence packet;
try {
packet = new Presence(doc, !validateJIDs());
}
catch (IllegalArgumentException e) {
Log.debug("Rejecting packet. JID malformed", e);
// The original packet contains a malformed JID so answer an error
Presence reply = new Presence();
reply.setID(doc.attributeValue("id"));
reply.setTo(session.getAddress());
reply.getElement().addAttribute("from", doc.attributeValue("to"));
reply.setError(PacketError.Condition.jid_malformed);
session.process(reply);
return;
}
// Check that the presence type is valid. If not then assume available type
try {
packet.getType();
}
catch (IllegalArgumentException e) {
Log.warn("Invalid presence type", e);
// The presence packet contains an invalid presence type so replace it with
// an available presence type
packet.setType(null);
}
// Check that the presence show is valid. If not then assume available show value
try {
packet.getShow();
}
catch (IllegalArgumentException e) {
Log.warn("Invalid presence show for -" + packet.toXML(), e);
// The presence packet contains an invalid presence show so replace it with
// an available presence show
packet.setShow(null);
}
if (session.getStatus() == Session.STATUS_CLOSED && packet.isAvailable()) {
// Ignore available presence packets sent from a closed session. A closed
// session may have buffered data pending to be processes so we want to ignore
// just Presences of type available
Log.warn("Ignoring available presence packet of closed session: " + packet);
return;
}
processPresence(packet);
}
else if ("iq".equals(tag)) {
IQ packet;
try {
packet = getIQ(doc);
}
catch (IllegalArgumentException e) {
Log.debug("Rejecting packet. JID malformed", e);
// The original packet contains a malformed JID so answer an error
IQ reply = new IQ();
if (!doc.elements().isEmpty()) {
reply.setChildElement(((Element)doc.elements().get(0)).createCopy());
}
reply.setID(doc.attributeValue("id"));
reply.setTo(session.getAddress());
if (doc.attributeValue("to") != null) {
reply.getElement().addAttribute("from", doc.attributeValue("to"));
}
reply.setError(PacketError.Condition.jid_malformed);
session.process(reply);
return;
}
processIQ(packet);
}
else {
if (!processUnknowPacket(doc)) {
Log.warn(LocaleUtils.getLocalizedString("admin.error.packet.tag") +
doc.asXML());
session.close();
}
}
}
private IQ getIQ(Element doc) {
Element query = doc.element("query");
if (query != null && "jabber:iq:roster".equals(query.getNamespaceURI())) {
return new Roster(doc);
}
else {
return new IQ(doc, !validateJIDs());
}
}
/**
* Process the received IQ packet. Registered
* {@link org.jivesoftware.openfire.interceptor.PacketInterceptor} will be invoked before
* and after the packet was routed.<p>
* <p/>
* Subclasses may redefine this method for different reasons such as modifying the sender
* of the packet to avoid spoofing, rejecting the packet or even process the packet in
* another thread.
*
* @param packet the received packet.
* @throws org.jivesoftware.openfire.auth.UnauthorizedException
* if service is not available to sender.
*/
protected void processIQ(IQ packet) throws UnauthorizedException {
router.route(packet);
session.incrementClientPacketCount();
}
/**
* Process the received Presence packet. Registered
* {@link org.jivesoftware.openfire.interceptor.PacketInterceptor} will be invoked before
* and after the packet was routed.<p>
* <p/>
* Subclasses may redefine this method for different reasons such as modifying the sender
* of the packet to avoid spoofing, rejecting the packet or even process the packet in
* another thread.
*
* @param packet the received packet.
* @throws org.jivesoftware.openfire.auth.UnauthorizedException
* if service is not available to sender.
*/
protected void processPresence(Presence packet) throws UnauthorizedException {
router.route(packet);
session.incrementClientPacketCount();
}
/**
* Process the received Message packet. Registered
* {@link org.jivesoftware.openfire.interceptor.PacketInterceptor} will be invoked before
* and after the packet was routed.<p>
* <p/>
* Subclasses may redefine this method for different reasons such as modifying the sender
* of the packet to avoid spoofing, rejecting the packet or even process the packet in
* another thread.
*
* @param packet the received packet.
* @throws org.jivesoftware.openfire.auth.UnauthorizedException
* if service is not available to sender.
*/
protected void processMessage(Message packet) throws UnauthorizedException {
router.route(packet);
session.incrementClientPacketCount();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -