public static LocalClientSession createSession(String serverName, XmlPullParser xpp, Connection connection)
            throws XmlPullParserException
    {
        // Conduct error checking, the opening tag should be 'stream' in the 'etherx' namespace
        if (!xpp.getName().equals("stream")) {
            throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-stream"));
        }

        if (!xpp.getNamespace(xpp.getPrefix()).equals(ETHERX_NAMESPACE)) {
            throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-namespace"));
        }

        if (!isAllowed(connection))
        {
            // Client cannot connect from this IP address so end the stream and TCP connection.
            String hostAddress = "Unknown";
            try {
                hostAddress = connection.getHostAddress();
            } catch (UnknownHostException e) {
                // Do nothing
            }

            Log.debug("LocalClientSession: Closed connection to client attempting to connect from: {}", hostAddress);
            // Include the not-authorized error in the response and close the underlying connection.
            connection.close(new StreamError(StreamError.Condition.not_authorized));
            return null;
        }

        // Retrieve list of namespaces declared in current element (OF-2556)
        connection.setAdditionalNamespaces(XMPPPacketReader.getPrefixedNamespacesOnCurrentElement(xpp));

        final Locale language = Session.detectLanguage(xpp);
        final int[] version = Session.detectVersion(xpp);
        int majorVersion = version[0];
        int minorVersion = version[1];

        connection.setXMPPVersion(majorVersion, minorVersion);

        boolean hasCertificates = false;
        try {
            hasCertificates = !connection.getConfiguration().getIdentityStore().getAllCertificates().isEmpty();
        }
        catch (Exception e) {
            Log.error("Unable to load find any content in the identity store. This connection won't be able to support TLS.", e);
        }

        if (!hasCertificates && connection.getConfiguration().getTlsPolicy() == Connection.TLSPolicy.required) {
            Log.error("Client session rejected. TLS is required but no certificates were created.");
            return null;
        }

        // Create a ClientSession for this user.
        LocalClientSession session = SessionManager.getInstance().createClientSession(connection, language);

        // Build the start packet response
        final Element stream = DocumentHelper.createElement(QName.get("stream", "stream", "http://etherx.jabber.org/streams"));
        final Document document = DocumentHelper.createDocument(stream);
        document.setXMLEncoding(StandardCharsets.UTF_8.toString());
        stream.add(Namespace.get("jabber:client"));
        stream.addAttribute("from", serverName);
        stream.addAttribute("id", session.getStreamID().toString());
        stream.addAttribute(QName.get("lang", Namespace.XML_NAMESPACE), language.toLanguageTag());
        // Don't include version info if the version is 0.0.
        if (majorVersion != 0) {
            stream.addAttribute("version", majorVersion + "." + minorVersion);
        }

        // If this is a "Jabber" connection, the session is now initialized - do not include features but return to allow normal packet parsing.
        if (majorVersion != 0)
        {
            // Otherwise, this is at least XMPP 1.0 so we need to announce stream features.
            final Element features = DocumentHelper.createElement(QName.get("features", "stream", "http://etherx.jabber.org/streams"));
            document.getRootElement().add(features);

            try {
                if (!connection.isEncrypted() && connection.getConfiguration().getTlsPolicy() != Connection.TLSPolicy.disabled && !connection.getConfiguration().getIdentityStore().getAllCertificates().isEmpty()) {
                    final Element starttls = DocumentHelper.createElement(QName.get("starttls", "urn:ietf:params:xml:ns:xmpp-tls"));
                    if (connection.getConfiguration().getTlsPolicy() == Connection.TLSPolicy.required) {
                        starttls.addElement("required");
                    }
                    features.add(starttls);
                }
                if (!ConnectionSettings.Client.STREAM_LIMITS_ADVERTISEMENT_DISABLED.getValue()) {
                    final Element limits = DocumentHelper.createElement(QName.get("limits", "urn:xmpp:stream-limits:0"));
                    limits.addElement("max-bytes").addText(String.valueOf(XMLLightweightParser.XMPP_PARSER_BUFFER_SIZE.getValue()));
                    final Duration timeout = ConnectionSettings.Client.IDLE_TIMEOUT_PROPERTY.getValue();
                    if (!timeout.isNegative() && !timeout.isZero()) {
                        limits.addElement("idle-seconds").addText(String.valueOf(timeout.toSeconds()));
                    }
                    features.add(limits);
                }
            } catch (KeyStoreException e) {
                Log.warn("Unable to access the identity store for client connections. StartTLS is not being offered as a feature for this session.", e);
            }
            // Include available SASL Mechanisms
            final Element saslMechanisms = SASLAuthentication.getSASLMechanisms(session);
            if (saslMechanisms != null) {
                features.add(saslMechanisms);
            }
            // Include Stream features
            final List<Element> specificFeatures = session.getAvailableStreamFeatures();
            if (specificFeatures != null) {
                for (final Element feature : specificFeatures) {
                    features.add(feature);
                }
            }
        }

        connection.deliverRawText(StringUtils.asUnclosedStream(document));
        return session;
    }
Returns a newly created session between the server and a client. The session will
be created and returned only if correct name/prefix ('stream')
and namespace were provided by the client.

@param serverName the name of the server where the session is connecting to.
@param xpp the parser that is reading the provided XML through the connection.
@param connection the connection with the client.
@return a newly created session between the server and a client.
@throws org.xmlpull.v1.XmlPullParserException if an error occurs while parsing incoming data.
xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalClientSession.java