/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.vysper.xmpp.server.response;
import java.util.List;
import org.apache.vysper.xml.fragment.XMLElement;
import org.apache.vysper.xml.fragment.XMLElementBuilder;
import org.apache.vysper.xmpp.addressing.Entity;
import org.apache.vysper.xmpp.authentication.SASLMechanism;
import org.apache.vysper.xmpp.modules.extension.xep0077_inbandreg.InBandRegistrationModule;
import org.apache.vysper.xmpp.protocol.NamespaceURIs;
import org.apache.vysper.xmpp.server.ServerFeatures;
import org.apache.vysper.xmpp.server.SessionContext;
import org.apache.vysper.xmpp.server.SessionState;
import org.apache.vysper.xmpp.server.XMPPVersion;
import org.apache.vysper.xmpp.stanza.Stanza;
import org.apache.vysper.xmpp.stanza.StanzaBuilder;
/**
*
* @author The Apache MINA Project (dev@mina.apache.org)
*/
public class ServerResponses {
public Stanza getStreamOpenerForError(boolean forClient, Entity from, XMPPVersion version, Stanza errorStanza) {
return getStreamOpener(forClient, from, null, version, errorStanza).build();
}
public Stanza getStreamOpenerForClient(Entity from, XMPPVersion version, SessionContext sessionContext) {
Stanza innerFeatureStanza;
if (sessionContext.getState() == SessionState.INITIATED)
innerFeatureStanza = getFeaturesForEncryption(sessionContext);
else if (sessionContext.getState() == SessionState.ENCRYPTED)
innerFeatureStanza = getFeaturesForAuthentication(sessionContext.getServerRuntimeContext()
.getServerFeatures().getAuthenticationMethods(), sessionContext);
else if (sessionContext.getState() == SessionState.AUTHENTICATED) {
sessionContext.setIsReopeningXMLStream();
innerFeatureStanza = getFeaturesForSession();
} else {
throw new IllegalStateException("unsupported state for responding with stream opener");
}
StanzaBuilder stanzaBuilder = getStreamOpener(true, from, sessionContext.getXMLLang(), version,
sessionContext.getSessionId(), innerFeatureStanza);
return stanzaBuilder.build();
}
public Stanza getStreamOpenerForServerAcceptor(Entity from, XMPPVersion version, SessionContext sessionContext, boolean tlsConfigured) {
XMLElement features = null;
// only include <features> if the other server support version 1.0
if(XMPPVersion.VERSION_1_0.equals(version)) {
XMLElementBuilder featureBuilder = new XMLElementBuilder("features", NamespaceURIs.HTTP_ETHERX_JABBER_ORG_STREAMS);
if (sessionContext.getState() == SessionState.INITIATED) {
if(tlsConfigured) {
featureBuilder.startInnerElement("starttls", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_TLS).endInnerElement();
}
featureBuilder.startInnerElement("dialback", NamespaceURIs.URN_XMPP_FEATURES_DIALBACK).endInnerElement();
} else if (sessionContext.getState() == SessionState.ENCRYPTED) {
featureBuilder.startInnerElement("dialback", NamespaceURIs.URN_XMPP_FEATURES_DIALBACK).endInnerElement();
} else {
throw new IllegalStateException("unsupported state for responding with stream opener");
}
features = featureBuilder.build();
}
StanzaBuilder stanzaBuilder = getStreamOpener(false, from, sessionContext.getXMLLang(), version,
sessionContext.getSessionId(), features);
stanzaBuilder.declareNamespace("db", NamespaceURIs.JABBER_SERVER_DIALBACK);
return stanzaBuilder.build();
}
public Stanza getStreamOpenerForServerConnector(Entity from, Entity to, XMPPVersion version, SessionContext sessionContext) {
StanzaBuilder stanzaBuilder = getStreamOpener(false, from, sessionContext.getXMLLang(), version,
null, null);
stanzaBuilder.addAttribute("to", to.getDomain());
stanzaBuilder.declareNamespace("db", NamespaceURIs.JABBER_SERVER_DIALBACK);
return stanzaBuilder.build();
}
public StanzaBuilder getStreamOpener(boolean forClient, Entity from, String xmlLang, XMPPVersion version,
Stanza innerStanza) {
return getStreamOpener(forClient, from, xmlLang, version, null, innerStanza);
}
public StanzaBuilder getStreamOpener(boolean forClient, Entity from, String xmlLang, XMPPVersion version,
String sessionId, XMLElement innerStanza) {
StanzaBuilder stanzaBuilder = new StanzaBuilder("stream", NamespaceURIs.HTTP_ETHERX_JABBER_ORG_STREAMS,
"stream").declareNamespace("", forClient ? NamespaceURIs.JABBER_CLIENT : NamespaceURIs.JABBER_SERVER)
.addAttribute("from", from.getFullQualifiedName());
if (xmlLang != null)
stanzaBuilder.addAttribute(NamespaceURIs.XML, "lang", xmlLang);
if (version != null)
stanzaBuilder.addAttribute("version", version.toString());
if (sessionId != null)
stanzaBuilder.addAttribute("id", sessionId);
if (innerStanza != null)
stanzaBuilder.addPreparedElement(innerStanza);
return stanzaBuilder;
}
public Stanza getFeaturesForEncryption(SessionContext sessionContext) {
StanzaBuilder stanzaBuilder = startFeatureStanza();
getFeatureStartTLS(sessionContext, stanzaBuilder);
final ServerFeatures serverFeatures = sessionContext.getServerRuntimeContext().getServerFeatures();
if (!serverFeatures.isStartTLSRequired()) {
// only add auth methods, if StartTLS is NOT REQUIRED (according to RFC6120.html#5.3.1
getFeaturesSASL(serverFeatures.getAuthenticationMethods(), stanzaBuilder);
}
return stanzaBuilder.build();
}
private void getFeatureStartTLS(SessionContext sessionContext, StanzaBuilder stanzaBuilder) {
stanzaBuilder.startInnerElement("starttls", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_TLS);
if (sessionContext.getServerRuntimeContext().getServerFeatures().isStartTLSRequired()) {
stanzaBuilder.startInnerElement("required", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_TLS)
.endInnerElement();
}
stanzaBuilder.endInnerElement();
}
public Stanza getFeaturesForAuthentication(List<SASLMechanism> authenticationMethods, SessionContext sessionContext) {
StanzaBuilder stanzaBuilder = startFeatureStanza();
getFeaturesSASL(authenticationMethods, stanzaBuilder);
if(sessionContext.getServerRuntimeContext().getModule(InBandRegistrationModule.class) != null) {
// In-band registration active, show as feature
stanzaBuilder.startInnerElement("register", NamespaceURIs.JABBER_ORG_FEATURES_IQ_REGISTER).endInnerElement();
}
return stanzaBuilder.build();
}
private void getFeaturesSASL(List<SASLMechanism> authenticationMethods, StanzaBuilder stanzaBuilder) {
stanzaBuilder.startInnerElement("mechanisms", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_SASL);
for (SASLMechanism authenticationMethod : authenticationMethods) {
stanzaBuilder.startInnerElement("mechanism", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_SASL).addText(
authenticationMethod.getName()).endInnerElement();
}
stanzaBuilder.endInnerElement();
}
public Stanza getFeaturesForSession() {
StanzaBuilder stanzaBuilder = startFeatureStanza();
stanzaBuilder.startInnerElement("bind", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_BIND).startInnerElement(
"required", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_BIND).endInnerElement();
stanzaBuilder.endInnerElement();
// session establishment is here for RFC3921 compatibility and is planed to be removed in revisions of this RFC.
stanzaBuilder.startInnerElement("session", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_SESSION)
.startInnerElement("required", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_SESSION).endInnerElement();
stanzaBuilder.endInnerElement();
return stanzaBuilder.build();
}
protected StanzaBuilder startFeatureStanza() {
StanzaBuilder stanzaBuilder = new StanzaBuilder("features", NamespaceURIs.HTTP_ETHERX_JABBER_ORG_STREAMS,
"stream");
return stanzaBuilder;
}
public Stanza getTLSProceed() {
StanzaBuilder stanzaBuilder = new StanzaBuilder("proceed", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_TLS);
return stanzaBuilder.build();
}
public Stanza getAuthAborted() {
StanzaBuilder stanzaBuilder = new StanzaBuilder("aborted", NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_TLS);
return stanzaBuilder.build();
}
}