001/**
002 *
003 * Copyright © 2014-2015 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.jxmpp.stringprep.simple;
018
019import java.util.Locale;
020
021import org.jxmpp.stringprep.XmppStringPrepUtil;
022import org.jxmpp.stringprep.XmppStringprep;
023import org.jxmpp.stringprep.XmppStringprepException;
024
025public final class SimpleXmppStringprep implements XmppStringprep {
026
027        private static SimpleXmppStringprep instance;
028
029        /**
030         * Setup Simple XMPP Stringprep as implementation to use.
031         */
032        public static void setup() {
033                XmppStringPrepUtil.setXmppStringprep(getInstance());
034        }
035
036        /**
037         * Get the Simple XMPP Stringprep singleton.
038         *
039         * @return the simple XMPP Stringprep singleton.
040         */
041        public static SimpleXmppStringprep getInstance() {
042                if (instance == null) {
043                        instance = new SimpleXmppStringprep();
044                }
045                return instance;
046        }
047
048        private SimpleXmppStringprep() {
049        }
050
051        /**
052         * From 6122bis-18 § 3.3.1 and PRECIS IdentifierClass which forbids U+0020
053         */
054        // @formatter:off
055        private static final char[] LOCALPART_FURTHER_EXCLUDED_CHARACTERS = new char[] {
056                '"',   // U+0022 (QUOTATION MARK) , i.e., "
057                '&',   // U+0026 (AMPERSAND), i.e., &
058                '\'',  // U+0027 (APOSTROPHE), i.e., '
059                '/',   // U+002F (SOLIDUS), i.e., /
060                ',',   // U+003A (COLON), i.e., :
061                '<',   // U+003C (LESS-THAN SIGN), i.e., <
062                '>',   // U+003E (GREATER-THAN SIGN), i.e., >
063                '@',   // U+0040 (COMMERCIAL AT), i.e., @
064                ' ',   // U+0020 (SPACE)
065        };
066        // @formatter:on
067
068        @Override
069        public String localprep(String string) throws XmppStringprepException {
070                string = simpleStringprep(string);
071                for (char charFromString : string.toCharArray()) {
072                        for (char forbiddenChar : LOCALPART_FURTHER_EXCLUDED_CHARACTERS) {
073                                if (charFromString == forbiddenChar) {
074                                        throw new XmppStringprepException(string, "Localpart must not contain '" + forbiddenChar + "'");
075                                }
076                        }
077                }
078                return string;
079        }
080
081        @Override
082        public String domainprep(String string) throws XmppStringprepException {
083                return simpleStringprep(string);
084        }
085
086        @Override
087        public String resourceprep(String string) throws XmppStringprepException {
088                // rfc6122-bis specifies that resourceprep uses saslprep-bis OpaqueString Profile which says that
089                // "Uppercase and titlecase characters MUST NOT be mapped to their lowercase equivalents."
090
091                // TODO apply Unicode Normalization Form C (NFC) with help of java.text.Normalize
092                // but unfortunately this is API is only available on Android API 9 or higher and Smack is currently API 8
093                return string;
094        }
095
096        private static String simpleStringprep(String string) {
097                String res = string.toLowerCase(Locale.US);
098                return res;
099        }
100}