001/**
002 *
003 * Copyright 2014 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smack.sasl.packet;
018
019import java.util.Map;
020
021import org.jivesoftware.smack.packet.AbstractError;
022import org.jivesoftware.smack.packet.PlainStreamElement;
023import org.jivesoftware.smack.sasl.SASLError;
024import org.jivesoftware.smack.util.Objects;
025import org.jivesoftware.smack.util.StringUtils;
026import org.jivesoftware.smack.util.XmlStringBuilder;
027
028public class SaslStreamElements {
029    public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
030
031    /**
032     * Initiating SASL authentication by select a mechanism.
033     */
034    public static class AuthMechanism implements PlainStreamElement {
035        public static final String ELEMENT = "auth";
036
037        private final String mechanism;
038        private final String authenticationText;
039
040        public AuthMechanism(String mechanism, String authenticationText) {
041            this.mechanism = Objects.requireNonNull(mechanism, "SASL mechanism shouldn't be null.");
042            this.authenticationText = StringUtils.requireNotNullOrEmpty(authenticationText,
043                            "SASL authenticationText must not be null or empty (RFC6120 6.4.2)");
044        }
045
046        @Override
047        public XmlStringBuilder toXML() {
048            XmlStringBuilder xml = new XmlStringBuilder();
049            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).attribute("mechanism", mechanism).rightAngleBracket();
050            xml.optAppend(authenticationText);
051            xml.closeElement(ELEMENT);
052            return xml;
053        }
054
055        public String getMechanism() {
056            return mechanism;
057        }
058
059        public String getAuthenticationText() {
060            return authenticationText;
061        }
062    }
063
064    /**
065     * A SASL challenge stream element.
066     */
067    public static class Challenge implements PlainStreamElement {
068        public static final String ELEMENT = "challenge";
069
070        private final String data;
071
072        public Challenge(String data) {
073            this.data = StringUtils.returnIfNotEmptyTrimmed(data);
074        }
075
076        @Override
077        public XmlStringBuilder toXML() {
078            XmlStringBuilder xml = new XmlStringBuilder().halfOpenElement(ELEMENT).xmlnsAttribute(
079                            NAMESPACE).rightAngleBracket();
080            xml.optAppend(data);
081            xml.closeElement(ELEMENT);
082            return xml;
083        }
084    }
085
086    /**
087     * A SASL response stream element.
088     */
089    public static class Response implements PlainStreamElement {
090        public static final String ELEMENT = "response";
091
092        private final String authenticationText;
093
094        public Response() {
095            authenticationText = null;
096        }
097
098        public Response(String authenticationText) {
099            this.authenticationText = StringUtils.returnIfNotEmptyTrimmed(authenticationText);
100        }
101
102        @Override
103        public XmlStringBuilder toXML() {
104            XmlStringBuilder xml = new XmlStringBuilder();
105            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
106            xml.optAppend(authenticationText);
107            xml.closeElement(ELEMENT);
108            return xml;
109        }
110
111        public String getAuthenticationText() {
112            return authenticationText;
113        }
114    }
115
116    /**
117     * A SASL success stream element.
118     */
119    public static class Success implements PlainStreamElement {
120        public static final String ELEMENT = "success";
121
122        final private String data;
123
124        /**
125         * Construct a new SASL success stream element with optional additional data for the SASL layer
126         * (RFC6120 6.3.10)
127         * 
128         * @param data additional data for the SASL layer or <code>null</code>
129         */
130        public Success(String data) {
131            this.data = StringUtils.returnIfNotEmptyTrimmed(data);
132        }
133
134        /**
135         * Returns additional data for the SASL layer or <code>null</code>.
136         * 
137         * @return additional data or <code>null</code>
138         */
139        public String getData() {
140            return data;
141        }
142
143        @Override
144        public XmlStringBuilder toXML() {
145            XmlStringBuilder xml = new XmlStringBuilder();
146            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
147            xml.optAppend(data);
148            xml.closeElement(ELEMENT);
149            return xml;
150        }
151    }
152
153    /**
154     * A SASL failure stream element, also called "SASL Error"
155     * @see <a href="http://xmpp.org/rfcs/rfc6120.html#sasl-errors">RFC 6120 6.5 SASL Errors</a>
156     */
157    public static class SASLFailure extends AbstractError implements PlainStreamElement {
158        public static final String ELEMENT = "failure";
159
160        private final SASLError saslError;
161        private final String saslErrorString;
162
163        public SASLFailure(String saslError) {
164            this(saslError, null);
165        }
166
167        public SASLFailure(String saslError, Map<String, String> descriptiveTexts) {
168            super(descriptiveTexts);
169            SASLError error = SASLError.fromString(saslError);
170            if (error == null) {
171                // RFC6120 6.5 states that unknown condition must be treat as generic authentication
172                // failure.
173                this.saslError = SASLError.not_authorized;
174            }
175            else {
176                this.saslError = error;
177            }
178            this.saslErrorString = saslError;
179        }
180
181        /**
182         * Get the SASL related error condition.
183         * 
184         * @return the SASL related error condition.
185         */
186        public SASLError getSASLError() {
187            return saslError;
188        }
189
190        /**
191         * @return the SASL error as String
192         */
193        public String getSASLErrorString() {
194            return saslErrorString;
195        }
196
197        @Override
198        public XmlStringBuilder toXML() {
199            XmlStringBuilder xml = new XmlStringBuilder();
200            xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
201            xml.emptyElement(saslErrorString);
202            addDescriptiveTextsAndExtensions(xml);
203            xml.closeElement(ELEMENT);
204            return xml;
205        }
206
207        @Override
208        public String toString() {
209            return toXML().toString();
210        }
211    }
212}