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