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