Search in sources :

Example 1 with Tag

use of eu.siacs.conversations.xml.Tag in project Conversations by siacs.

the class XmppConnection method switchOverToTls.

private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
    tagReader.readTag();
    try {
        final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
        final InetAddress address = socket == null ? null : socket.getInetAddress();
        if (address == null) {
            throw new IOException("could not setup ssl");
        }
        final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
        if (sslSocket == null) {
            throw new IOException("could not initialize ssl socket");
        }
        SSLSocketHelper.setSecurity(sslSocket);
        if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
            Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
            throw new SecurityException();
        }
        tagReader.setInputStream(sslSocket.getInputStream());
        tagWriter.setOutputStream(sslSocket.getOutputStream());
        sendStartStream();
        Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS connection established");
        features.encryptionEnabled = true;
        final Tag tag = tagReader.readTag();
        if (tag != null && tag.isStart("stream")) {
            processStream();
        } else {
            throw new IOException("server didn't restart stream after STARTTLS");
        }
        sslSocket.close();
    } catch (final NoSuchAlgorithmException | KeyManagementException e1) {
        Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
        throw new SecurityException();
    }
}
Also used : SSLSocket(javax.net.ssl.SSLSocket) IOException(java.io.IOException) Tag(eu.siacs.conversations.xml.Tag) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) InetAddress(java.net.InetAddress) KeyManagementException(java.security.KeyManagementException)

Example 2 with Tag

use of eu.siacs.conversations.xml.Tag in project Conversations by siacs.

the class XmppConnection method sendStartTLS.

private void sendStartTLS() throws IOException {
    final Tag startTLS = Tag.empty("starttls");
    startTLS.setAttribute("xmlns", Namespace.TLS);
    tagWriter.writeTag(startTLS);
}
Also used : Tag(eu.siacs.conversations.xml.Tag)

Example 3 with Tag

use of eu.siacs.conversations.xml.Tag in project Conversations by siacs.

the class XmppConnection method processStream.

private void processStream() throws XmlPullParserException, IOException {
    final CountDownLatch streamCountDownLatch = new CountDownLatch(1);
    this.mStreamCountDownLatch = streamCountDownLatch;
    Tag nextTag = tagReader.readTag();
    while (nextTag != null && !nextTag.isEnd("stream")) {
        if (nextTag.isStart("error")) {
            processStreamError(nextTag);
        } else if (nextTag.isStart("features")) {
            processStreamFeatures(nextTag);
        } else if (nextTag.isStart("proceed")) {
            switchOverToTls();
        } else if (nextTag.isStart("success")) {
            final String challenge = tagReader.readElement(nextTag).getContent();
            try {
                saslMechanism.getResponse(challenge);
            } catch (final SaslMechanism.AuthenticationException e) {
                Log.e(Config.LOGTAG, String.valueOf(e));
                throw new StateChangingException(Account.State.UNAUTHORIZED);
            }
            Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": logged in");
            account.setKey(Account.PINNED_MECHANISM_KEY, String.valueOf(saslMechanism.getPriority()));
            tagReader.reset();
            sendStartStream();
            final Tag tag = tagReader.readTag();
            if (tag != null && tag.isStart("stream")) {
                processStream();
            } else {
                throw new StateChangingException(Account.State.STREAM_OPENING_ERROR);
            }
            break;
        } else if (nextTag.isStart("failure")) {
            final Element failure = tagReader.readElement(nextTag);
            if (Namespace.SASL.equals(failure.getNamespace())) {
                final String text = failure.findChildContent("text");
                if (failure.hasChild("account-disabled") && text != null) {
                    Matcher matcher = Patterns.AUTOLINK_WEB_URL.matcher(text);
                    if (matcher.find()) {
                        final HttpUrl url;
                        try {
                            url = HttpUrl.get(text.substring(matcher.start(), matcher.end()));
                            if (url.isHttps()) {
                                this.redirectionUrl = url;
                                throw new StateChangingException(Account.State.PAYMENT_REQUIRED);
                            }
                        } catch (IllegalArgumentException e) {
                            throw new StateChangingException(Account.State.UNAUTHORIZED);
                        }
                    }
                }
                throw new StateChangingException(Account.State.UNAUTHORIZED);
            } else if (Namespace.TLS.equals(failure.getNamespace())) {
                throw new StateChangingException(Account.State.TLS_ERROR);
            } else {
                throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
            }
        } else if (nextTag.isStart("challenge")) {
            final String challenge = tagReader.readElement(nextTag).getContent();
            final Element response = new Element("response", Namespace.SASL);
            try {
                response.setContent(saslMechanism.getResponse(challenge));
            } catch (final SaslMechanism.AuthenticationException e) {
                // TODO: Send auth abort tag.
                Log.e(Config.LOGTAG, e.toString());
            }
            tagWriter.writeElement(response);
        } else if (nextTag.isStart("enabled")) {
            final Element enabled = tagReader.readElement(nextTag);
            if ("true".equals(enabled.getAttribute("resume"))) {
                this.streamId = enabled.getAttribute("id");
                Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": stream management(" + smVersion + ") enabled (resumable)");
            } else {
                Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": stream management(" + smVersion + ") enabled");
            }
            this.stanzasReceived = 0;
            this.inSmacksSession = true;
            final RequestPacket r = new RequestPacket(smVersion);
            tagWriter.writeStanzaAsync(r);
        } else if (nextTag.isStart("resumed")) {
            this.inSmacksSession = true;
            this.isBound = true;
            this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
            lastPacketReceived = SystemClock.elapsedRealtime();
            final Element resumed = tagReader.readElement(nextTag);
            final String h = resumed.getAttribute("h");
            try {
                ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>();
                final boolean acknowledgedMessages;
                synchronized (this.mStanzaQueue) {
                    final int serverCount = Integer.parseInt(h);
                    if (serverCount < stanzasSent) {
                        Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": session resumed with lost packages");
                        stanzasSent = serverCount;
                    } else {
                        Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": session resumed");
                    }
                    acknowledgedMessages = acknowledgeStanzaUpTo(serverCount);
                    for (int i = 0; i < this.mStanzaQueue.size(); ++i) {
                        failedStanzas.add(mStanzaQueue.valueAt(i));
                    }
                    mStanzaQueue.clear();
                }
                if (acknowledgedMessages) {
                    mXmppConnectionService.updateConversationUi();
                }
                Log.d(Config.LOGTAG, "resending " + failedStanzas.size() + " stanzas");
                for (AbstractAcknowledgeableStanza packet : failedStanzas) {
                    if (packet instanceof MessagePacket) {
                        MessagePacket message = (MessagePacket) packet;
                        mXmppConnectionService.markMessage(account, message.getTo().asBareJid(), message.getId(), Message.STATUS_UNSEND);
                    }
                    sendPacket(packet);
                }
            } catch (final NumberFormatException ignored) {
            }
            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": online with resource " + account.getResource());
            changeStatus(Account.State.ONLINE);
        } else if (nextTag.isStart("r")) {
            tagReader.readElement(nextTag);
            if (Config.EXTENDED_SM_LOGGING) {
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": acknowledging stanza #" + this.stanzasReceived);
            }
            final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
            tagWriter.writeStanzaAsync(ack);
        } else if (nextTag.isStart("a")) {
            boolean accountUiNeedsRefresh = false;
            synchronized (NotificationService.CATCHUP_LOCK) {
                if (mWaitingForSmCatchup.compareAndSet(true, false)) {
                    final int messageCount = mSmCatchupMessageCounter.get();
                    final int pendingIQs = packetCallbacks.size();
                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": SM catchup complete (messages=" + messageCount + ", pending IQs=" + pendingIQs + ")");
                    accountUiNeedsRefresh = true;
                    if (messageCount > 0) {
                        mXmppConnectionService.getNotificationService().finishBacklog(true, account);
                    }
                }
            }
            if (accountUiNeedsRefresh) {
                mXmppConnectionService.updateAccountUi();
            }
            final Element ack = tagReader.readElement(nextTag);
            lastPacketReceived = SystemClock.elapsedRealtime();
            try {
                final boolean acknowledgedMessages;
                synchronized (this.mStanzaQueue) {
                    final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
                    acknowledgedMessages = acknowledgeStanzaUpTo(serverSequence);
                }
                if (acknowledgedMessages) {
                    mXmppConnectionService.updateConversationUi();
                }
            } catch (NumberFormatException | NullPointerException e) {
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server send ack without sequence number");
            }
        } else if (nextTag.isStart("failed")) {
            Element failed = tagReader.readElement(nextTag);
            try {
                final int serverCount = Integer.parseInt(failed.getAttribute("h"));
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resumption failed but server acknowledged stanza #" + serverCount);
                final boolean acknowledgedMessages;
                synchronized (this.mStanzaQueue) {
                    acknowledgedMessages = acknowledgeStanzaUpTo(serverCount);
                }
                if (acknowledgedMessages) {
                    mXmppConnectionService.updateConversationUi();
                }
            } catch (NumberFormatException | NullPointerException e) {
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": resumption failed");
            }
            resetStreamId();
            sendBindRequest();
        } else if (nextTag.isStart("iq")) {
            processIq(nextTag);
        } else if (nextTag.isStart("message")) {
            processMessage(nextTag);
        } else if (nextTag.isStart("presence")) {
            processPresence(nextTag);
        }
        nextTag = tagReader.readTag();
    }
    if (nextTag != null && nextTag.isEnd("stream")) {
        streamCountDownLatch.countDown();
    }
}
Also used : RequestPacket(eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket) Matcher(java.util.regex.Matcher) Element(eu.siacs.conversations.xml.Element) ArrayList(java.util.ArrayList) AckPacket(eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket) CountDownLatch(java.util.concurrent.CountDownLatch) HttpUrl(okhttp3.HttpUrl) MessagePacket(eu.siacs.conversations.xmpp.stanzas.MessagePacket) AbstractAcknowledgeableStanza(eu.siacs.conversations.xmpp.stanzas.AbstractAcknowledgeableStanza) Tag(eu.siacs.conversations.xml.Tag)

Example 4 with Tag

use of eu.siacs.conversations.xml.Tag in project Conversations by siacs.

the class XmppConnection method startXmpp.

/**
 * Starts xmpp protocol, call after connecting to socket
 *
 * @return true if server returns with valid xmpp, false otherwise
 */
private boolean startXmpp(Socket socket) throws Exception {
    if (Thread.currentThread().isInterrupted()) {
        throw new InterruptedException();
    }
    this.socket = socket;
    tagReader = new XmlReader();
    if (tagWriter != null) {
        tagWriter.forceClose();
    }
    tagWriter = new TagWriter();
    tagWriter.setOutputStream(socket.getOutputStream());
    tagReader.setInputStream(socket.getInputStream());
    tagWriter.beginDocument();
    sendStartStream();
    final Tag tag = tagReader.readTag();
    if (Thread.currentThread().isInterrupted()) {
        throw new InterruptedException();
    }
    if (socket instanceof SSLSocket) {
        SSLSocketHelper.log(account, (SSLSocket) socket);
    }
    return tag != null && tag.isStart("stream");
}
Also used : SSLSocket(javax.net.ssl.SSLSocket) TagWriter(eu.siacs.conversations.xml.TagWriter) XmlReader(eu.siacs.conversations.xml.XmlReader) Tag(eu.siacs.conversations.xml.Tag)

Example 5 with Tag

use of eu.siacs.conversations.xml.Tag in project Conversations by siacs.

the class XmppConnection method processPacket.

@NonNull
private Element processPacket(final Tag currentTag, final int packetType) throws IOException {
    final Element element;
    switch(packetType) {
        case PACKET_IQ:
            element = new IqPacket();
            break;
        case PACKET_MESSAGE:
            element = new MessagePacket();
            break;
        case PACKET_PRESENCE:
            element = new PresencePacket();
            break;
        default:
            throw new AssertionError("Should never encounter invalid type");
    }
    element.setAttributes(currentTag.getAttributes());
    Tag nextTag = tagReader.readTag();
    if (nextTag == null) {
        throw new IOException("interrupted mid tag");
    }
    while (!nextTag.isEnd(element.getName())) {
        if (!nextTag.isNo()) {
            element.addChild(tagReader.readElement(nextTag));
        }
        nextTag = tagReader.readTag();
        if (nextTag == null) {
            throw new IOException("interrupted mid tag");
        }
    }
    if (stanzasReceived == Integer.MAX_VALUE) {
        resetStreamId();
        throw new IOException("time to restart the session. cant handle >2 billion pcks");
    }
    if (inSmacksSession) {
        ++stanzasReceived;
    } else if (features.sm()) {
        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": not counting stanza(" + element.getClass().getSimpleName() + "). Not in smacks session.");
    }
    lastPacketReceived = SystemClock.elapsedRealtime();
    if (Config.BACKGROUND_STANZA_LOGGING && mXmppConnectionService.checkListeners()) {
        Log.d(Config.LOGTAG, "[background stanza] " + element);
    }
    if (element instanceof IqPacket && (((IqPacket) element).getType() == IqPacket.TYPE.SET) && element.hasChild("jingle", Namespace.JINGLE)) {
        return JinglePacket.upgrade((IqPacket) element);
    } else {
        return element;
    }
}
Also used : MessagePacket(eu.siacs.conversations.xmpp.stanzas.MessagePacket) Element(eu.siacs.conversations.xml.Element) Tag(eu.siacs.conversations.xml.Tag) IOException(java.io.IOException) PresencePacket(eu.siacs.conversations.xmpp.stanzas.PresencePacket) IqPacket(eu.siacs.conversations.xmpp.stanzas.IqPacket) NonNull(androidx.annotation.NonNull)

Aggregations

Tag (eu.siacs.conversations.xml.Tag)7 SSLSocket (javax.net.ssl.SSLSocket)3 Element (eu.siacs.conversations.xml.Element)2 MessagePacket (eu.siacs.conversations.xmpp.stanzas.MessagePacket)2 IOException (java.io.IOException)2 NonNull (androidx.annotation.NonNull)1 TagWriter (eu.siacs.conversations.xml.TagWriter)1 XmlReader (eu.siacs.conversations.xml.XmlReader)1 AbstractAcknowledgeableStanza (eu.siacs.conversations.xmpp.stanzas.AbstractAcknowledgeableStanza)1 IqPacket (eu.siacs.conversations.xmpp.stanzas.IqPacket)1 PresencePacket (eu.siacs.conversations.xmpp.stanzas.PresencePacket)1 AckPacket (eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket)1 RequestPacket (eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket)1 InetAddress (java.net.InetAddress)1 Socket (java.net.Socket)1 KeyManagementException (java.security.KeyManagementException)1 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)1 ArrayList (java.util.ArrayList)1 CountDownLatch (java.util.concurrent.CountDownLatch)1 Matcher (java.util.regex.Matcher)1