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}