001/**
002 *
003 * Copyright 2017 Paul Schaub
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.smackx.jingle.transports.jingle_s5b.elements;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.jivesoftware.smack.util.StringUtils;
023import org.jivesoftware.smack.util.XmlStringBuilder;
024
025import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
026import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
027import org.jivesoftware.smackx.jingle.element.JingleContentTransportCandidate;
028import org.jivesoftware.smackx.jingle.element.JingleContentTransportInfo;
029
030/**
031 * Socks5Bytestream transport element.
032 */
033public class JingleS5BTransport extends JingleContentTransport {
034    public static final String NAMESPACE_V1 = "urn:xmpp:jingle:transports:s5b:1";
035    public static final String ATTR_DSTADDR = "dstaddr";
036    public static final String ATTR_MODE = "mode";
037    public static final String ATTR_SID = "sid";
038
039    private final String streamId;
040    private final String dstAddr;
041    private final Bytestream.Mode mode;
042
043    protected JingleS5BTransport(List<JingleContentTransportCandidate> candidates, JingleContentTransportInfo info, String streamId, String dstAddr, Bytestream.Mode mode) {
044        super(candidates, info);
045        StringUtils.requireNotNullNorEmpty(streamId, "sid MUST be neither null, nor empty.");
046        this.streamId = streamId;
047        this.dstAddr = dstAddr;
048        this.mode = mode;
049    }
050
051    public String getStreamId() {
052        return streamId;
053    }
054
055    public String getDestinationAddress() {
056        return dstAddr;
057    }
058
059    public Bytestream.Mode getMode() {
060        return mode == null ? Bytestream.Mode.tcp : mode;
061    }
062
063    @Override
064    public String getNamespace() {
065        return NAMESPACE_V1;
066    }
067
068    @Override
069    protected void addExtraAttributes(XmlStringBuilder xml) {
070        xml.optAttribute(ATTR_DSTADDR, dstAddr);
071        xml.optAttribute(ATTR_MODE, mode);
072        xml.attribute(ATTR_SID, streamId);
073    }
074
075    public boolean hasCandidate(String candidateId) {
076        return getCandidate(candidateId) != null;
077    }
078
079    public JingleS5BTransportCandidate getCandidate(String candidateId) {
080        for (JingleContentTransportCandidate c : candidates) {
081            JingleS5BTransportCandidate candidate = (JingleS5BTransportCandidate) c;
082            if (candidate.getCandidateId().equals(candidateId)) {
083                return candidate;
084            }
085        }
086        return null;
087    }
088
089    public static Builder getBuilder() {
090        return new Builder();
091    }
092
093    public static class Builder {
094        private String streamId;
095        private String dstAddr;
096        private Bytestream.Mode mode;
097        private final ArrayList<JingleContentTransportCandidate> candidates = new ArrayList<>();
098        private JingleContentTransportInfo info;
099
100        public Builder setStreamId(String sid) {
101            this.streamId = sid;
102            return this;
103        }
104
105        public Builder setDestinationAddress(String dstAddr) {
106            this.dstAddr = dstAddr;
107            return this;
108        }
109
110        public Builder setMode(Bytestream.Mode mode) {
111            this.mode = mode;
112            return this;
113        }
114
115        public Builder addTransportCandidate(JingleS5BTransportCandidate candidate) {
116            if (info != null) {
117                throw new IllegalStateException("Builder has already an info set. " +
118                        "The transport can only have either an info or transport candidates, not both.");
119            }
120            this.candidates.add(candidate);
121            return this;
122        }
123
124        public Builder setTransportInfo(JingleContentTransportInfo info) {
125            if (!candidates.isEmpty()) {
126                throw new IllegalStateException("Builder has already at least one candidate set. " +
127                        "The transport can only have either an info or transport candidates, not both.");
128            }
129            if (this.info != null) {
130                throw new IllegalStateException("Builder has already an info set.");
131            }
132            this.info = info;
133            return this;
134        }
135
136        public Builder setCandidateUsed(String candidateId) {
137            return setTransportInfo(new JingleS5BTransportInfo.CandidateUsed(candidateId));
138        }
139
140        public Builder setCandidateActivated(String candidateId) {
141            return setTransportInfo(new JingleS5BTransportInfo.CandidateActivated(candidateId));
142        }
143
144        public Builder setCandidateError() {
145            return setTransportInfo(JingleS5BTransportInfo.CandidateError.INSTANCE);
146        }
147
148        public Builder setProxyError() {
149            return setTransportInfo(JingleS5BTransportInfo.ProxyError.INSTANCE);
150        }
151
152        public JingleS5BTransport build() {
153            return new JingleS5BTransport(candidates, info, streamId, dstAddr, mode);
154        }
155    }
156}