001/**
002 *
003 * Copyright 2014-2020 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;
018
019import java.security.cert.CertificateException;
020import java.util.Collections;
021import java.util.List;
022
023import org.jivesoftware.smack.c2s.XmppClientToServerTransport.LookupConnectionEndpointsFailed;
024import org.jivesoftware.smack.filter.StanzaFilter;
025import org.jivesoftware.smack.util.StringUtils;
026import org.jivesoftware.smack.util.rce.RemoteConnectionEndpoint;
027import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
028import org.jivesoftware.smack.util.rce.RemoteConnectionException;
029
030import org.jxmpp.jid.Jid;
031
032/**
033 * Smack uses SmackExceptions for errors that are not defined by any XMPP specification.
034 *
035 * @author Florian Schmaus
036 */
037public abstract class SmackException extends Exception {
038
039    /**
040     *
041     */
042    private static final long serialVersionUID = 1844674365368214458L;
043
044    /**
045     * Creates a new SmackException with the Throwable that was the root cause of the exception.
046     *
047     * @param wrappedThrowable the root cause of the exception.
048     */
049    protected SmackException(Throwable wrappedThrowable) {
050        super(wrappedThrowable);
051    }
052
053    protected SmackException(String message) {
054        super(message);
055    }
056
057    protected SmackException(String message, Throwable wrappedThrowable) {
058        super(message, wrappedThrowable);
059    }
060
061    protected SmackException() {
062    }
063
064    /**
065     * Exception thrown always when there was no response to an request within the stanza reply timeout of the used
066     * connection instance. You can modify (e.g. increase) the stanza reply timeout with
067     * {@link XMPPConnection#setReplyTimeout(long)}.
068     */
069    public static final class NoResponseException extends SmackException {
070        /**
071         *
072         */
073        private static final long serialVersionUID = -6523363748984543636L;
074
075        private final StanzaFilter filter;
076
077        private NoResponseException(String message) {
078            this(message, null);
079        }
080
081        private NoResponseException(String message, StanzaFilter filter) {
082            super(message);
083            this.filter = filter;
084        }
085
086        /**
087         * Get the filter that was used to collect the response.
088         *
089         * @return the used filter or <code>null</code>.
090         */
091        public StanzaFilter getFilter() {
092            return filter;
093        }
094
095        public static NoResponseException newWith(XMPPConnection connection, String waitingFor) {
096            final StringBuilder sb = getWaitingFor(connection);
097            sb.append(" While waiting for ").append(waitingFor);
098            sb.append(" [").append(connection).append(']');
099            return new NoResponseException(sb.toString());
100        }
101
102        public static NoResponseException newWith(long timeout,
103                        StanzaCollector collector, boolean stanzaCollectorCancelled) {
104            return newWith(timeout, collector.getStanzaFilter(), stanzaCollectorCancelled);
105        }
106
107        public static NoResponseException newWith(XMPPConnection connection, StanzaFilter filter) {
108            return newWith(connection.getReplyTimeout(), filter, false);
109        }
110
111        public static NoResponseException newWith(long timeout, StanzaFilter filter, boolean stanzaCollectorCancelled) {
112            final StringBuilder sb = getWaitingFor(timeout);
113            if (stanzaCollectorCancelled) {
114                sb.append(" StanzaCollector has been cancelled.");
115            }
116            sb.append(" Waited for response using: ");
117            if (filter != null) {
118                sb.append(filter.toString());
119            }
120            else {
121                sb.append("No filter used or filter was 'null'");
122            }
123            sb.append('.');
124            return new NoResponseException(sb.toString(), filter);
125        }
126
127        private static StringBuilder getWaitingFor(XMPPConnection connection) {
128            return getWaitingFor(connection.getReplyTimeout());
129        }
130
131        private static StringBuilder getWaitingFor(final long replyTimeout) {
132            final StringBuilder sb = new StringBuilder(256);
133            sb.append("No response received within reply timeout. Timeout was "
134                            + replyTimeout + "ms (~"
135                            + replyTimeout / 1000 + "s).");
136            return sb;
137        }
138    }
139
140    public static class NotLoggedInException extends SmackException {
141
142        /**
143         *
144         */
145        private static final long serialVersionUID = 3216216839100019278L;
146
147        public NotLoggedInException() {
148            super("Client is not logged in");
149        }
150    }
151
152    public static class AlreadyLoggedInException extends SmackException {
153
154        /**
155         *
156         */
157        private static final long serialVersionUID = 5011416918049935231L;
158
159        public AlreadyLoggedInException() {
160            super("Client is already logged in");
161        }
162    }
163
164    public static class AlreadyConnectedException extends SmackException {
165
166        /**
167         *
168         */
169        private static final long serialVersionUID = 5011416918049135231L;
170
171        public AlreadyConnectedException() {
172            super("Client is already connected");
173        }
174    }
175
176    public static class NotConnectedException extends SmackException {
177
178        /**
179         *
180         */
181        private static final long serialVersionUID = 9197980400776001173L;
182
183        public NotConnectedException() {
184            this(null);
185        }
186
187        public NotConnectedException(String optionalHint) {
188            super("Client is not, or no longer, connected."
189                            + (optionalHint != null ? ' ' + optionalHint : ""));
190        }
191
192        public NotConnectedException(XMPPConnection connection, String details) {
193            super("The connection " + connection.toString() + " is no longer connected. "
194                            + details);
195        }
196
197        public NotConnectedException(XMPPConnection connection, StanzaFilter stanzaFilter) {
198            super("The connection " + connection
199                            + " is no longer connected while waiting for response with " + stanzaFilter);
200        }
201
202        public NotConnectedException(XMPPConnection connection, StanzaFilter stanzaFilter,
203                        Exception connectionException) {
204            super("The connection " + connection + " is no longer connected while waiting for response with "
205                            + stanzaFilter + " because of " + connectionException, connectionException);
206        }
207    }
208
209    public static class IllegalStateChangeException extends SmackException {
210
211        /**
212         *
213         */
214        private static final long serialVersionUID = -1766023961577168927L;
215
216        public IllegalStateChangeException() {
217        }
218    }
219
220    public abstract static class SecurityRequiredException extends SmackException {
221
222        /**
223         *
224         */
225        private static final long serialVersionUID = 384291845029773545L;
226
227        public SecurityRequiredException(String message) {
228            super(message);
229        }
230    }
231
232    public static class SecurityRequiredByClientException extends SecurityRequiredException {
233        /**
234         *
235         */
236        private static final long serialVersionUID = 2395325821201543159L;
237
238        public SecurityRequiredByClientException() {
239            super("SSL/TLS required by client but not supported by server");
240        }
241    }
242
243    public static class SecurityRequiredByServerException extends SecurityRequiredException {
244        /**
245         *
246         */
247        private static final long serialVersionUID = 8268148813117631819L;
248
249        public SecurityRequiredByServerException() {
250            super("SSL/TLS required by server but disabled in client");
251        }
252    }
253
254    public static class SecurityNotPossibleException extends SmackException {
255
256        /**
257         *
258         */
259        private static final long serialVersionUID = -6836090872690331336L;
260
261        public SecurityNotPossibleException(String message) {
262            super(message);
263        }
264    }
265
266    public abstract static class ConnectionException extends SmackException {
267
268        private static final long serialVersionUID = 1L;
269
270        protected ConnectionException(Throwable wrappedThrowable) {
271            super(wrappedThrowable);
272        }
273
274        protected ConnectionException(String message) {
275            super(message);
276        }
277
278    }
279
280    public static final class GenericConnectionException extends ConnectionException {
281
282        private static final long serialVersionUID = 1L;
283
284        /**
285         * Deprecated, do not use.
286         *
287         * @param wrappedThrowable the wrapped throwable.
288         */
289        @Deprecated
290        public GenericConnectionException(Throwable wrappedThrowable) {
291            super(wrappedThrowable);
292        }
293    }
294
295    /**
296     * This exception is thrown if Smack is unable to connect to all hosts of a given XMPP
297     * service. The connection exceptions can be retrieved with
298     * {@link EndpointConnectionException#getConnectionExceptions()}, which will have the exception causing the
299     * connection failure set and retrievable with {@link RemoteConnectionException#getException()}.
300     */
301    public static final class EndpointConnectionException extends ConnectionException {
302
303        /**
304         *
305         */
306        private static final long serialVersionUID = 1;
307
308        private final List<RemoteConnectionEndpointLookupFailure> lookupFailures;
309        private final List<? extends RemoteConnectionException<?>> connectionExceptions;
310
311        private EndpointConnectionException(String message, List<RemoteConnectionEndpointLookupFailure> lookupFailures,
312                        List<? extends RemoteConnectionException<?>> connectionExceptions) {
313            super(message);
314            // At least one list must contain an entry.
315            assert !lookupFailures.isEmpty() || !connectionExceptions.isEmpty();
316            this.lookupFailures = lookupFailures;
317            this.connectionExceptions = connectionExceptions;
318        }
319
320        public static EndpointConnectionException from(List<RemoteConnectionEndpointLookupFailure> lookupFailures,
321                        List<? extends RemoteConnectionException<?>> connectionExceptions) {
322            StringBuilder sb = new StringBuilder(256);
323
324            if (!lookupFailures.isEmpty()) {
325                sb.append("Could not lookup the following endpoints: ");
326                StringUtils.appendTo(lookupFailures, sb);
327            }
328
329            if (!connectionExceptions.isEmpty()) {
330                sb.append("The following addresses failed: ");
331                StringUtils.appendTo(connectionExceptions, sb, rce -> sb.append(rce.getErrorMessage()));
332            }
333
334            return new EndpointConnectionException(sb.toString(), lookupFailures, connectionExceptions);
335        }
336
337        public List<RemoteConnectionEndpointLookupFailure> getLookupFailures() {
338            return lookupFailures;
339        }
340
341        public List<? extends RemoteConnectionException<? extends RemoteConnectionEndpoint>> getConnectionExceptions() {
342            return connectionExceptions;
343        }
344    }
345
346    public static final class NoEndpointsDiscoveredException extends ConnectionException {
347
348        private static final long serialVersionUID = 1L;
349
350        private final List<LookupConnectionEndpointsFailed> lookupFailures;
351
352        private NoEndpointsDiscoveredException(String message, List<LookupConnectionEndpointsFailed> lookupFailures) {
353            super(message);
354            this.lookupFailures = Collections.unmodifiableList(lookupFailures);
355        }
356
357        public List<LookupConnectionEndpointsFailed> getLookupFailures() {
358            return lookupFailures;
359        }
360
361        public static NoEndpointsDiscoveredException from(List<LookupConnectionEndpointsFailed> lookupFailures) {
362            StringBuilder sb = new StringBuilder();
363
364            if (lookupFailures.isEmpty()) {
365                sb.append("No endpoint lookup finished within the timeout");
366            } else {
367                sb.append("No endpoints could be discovered due the following lookup failures: ");
368                StringUtils.appendTo(lookupFailures, sb);
369            }
370
371            return new NoEndpointsDiscoveredException(sb.toString(), lookupFailures);
372        }
373    }
374
375    public static class FeatureNotSupportedException extends SmackException {
376
377        /**
378         *
379         */
380        private static final long serialVersionUID = 4713404802621452016L;
381
382        private final String feature;
383        private final Jid jid;
384
385        public FeatureNotSupportedException(String feature) {
386            this(feature, null);
387        }
388
389        public FeatureNotSupportedException(String feature, Jid jid) {
390            super(feature + " not supported" + (jid == null ? "" : " by '" + jid + "'"));
391            this.jid = jid;
392            this.feature = feature;
393        }
394
395        /**
396         * Get the feature which is not supported.
397         *
398         * @return the feature which is not supported
399         */
400        public String getFeature() {
401            return feature;
402        }
403
404        /**
405         * Get JID which does not support the feature. The JID can be null in cases when there are
406         * multiple JIDs queried for this feature.
407         *
408         * @return the JID which does not support the feature, or null
409         */
410        public Jid getJid() {
411            return jid;
412        }
413    }
414
415    public static class ResourceBindingNotOfferedException extends SmackException {
416
417        /**
418         *
419         */
420        private static final long serialVersionUID = 2346934138253437571L;
421
422        public ResourceBindingNotOfferedException() {
423            super("Resource binding was not offered by server");
424        }
425    }
426
427    /**
428     * A Smack exception wrapping another exception. Note that usage of this class is consider bad practice. This class
429     * will eventually be marked deprecated and removed.
430     */
431    public static class SmackWrappedException extends SmackException {
432
433        /**
434         *
435         */
436        private static final long serialVersionUID = 1L;
437
438        public SmackWrappedException(Exception exception) {
439            super(exception);
440        }
441
442        public SmackWrappedException(String message, Exception exception) {
443            super(message, exception);
444        }
445    }
446
447    /**
448     * A Smack exception wrapping a text message. Note that usage of this class is consider bad practice. This class
449     * will eventually be marked deprecated and removed.
450     */
451    public static class SmackMessageException extends SmackException {
452
453        /**
454         *
455         */
456        private static final long serialVersionUID = 1L;
457
458        public SmackMessageException(String message) {
459            super(message);
460        }
461    }
462
463    public static class SmackSaslException extends SmackException {
464
465        /**
466         *
467         */
468        private static final long serialVersionUID = 1L;
469
470        public SmackSaslException(Exception exception) {
471            super(exception);
472        }
473
474        public SmackSaslException(String message) {
475            super(message);
476        }
477
478        public SmackSaslException(String message, Exception exception) {
479            super(message, exception);
480        }
481    }
482
483    public static class SmackCertificateException extends SmackException {
484
485        private static final long serialVersionUID = 1L;
486
487        private final CertificateException certificateException;
488
489        public SmackCertificateException(CertificateException certificateException) {
490            this.certificateException = certificateException;
491        }
492
493        public CertificateException getCertificateException() {
494            return certificateException;
495        }
496    }
497}