blob: 9e07fc379af105332b3c288178595a49b6e2e975 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/**
2 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14package org.jivesoftware.smackx.bytestreams.socks5.packet;
15
16import java.util.ArrayList;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.List;
20
21import org.jivesoftware.smack.packet.IQ;
22import org.jivesoftware.smack.packet.PacketExtension;
23
24/**
25 * A packet representing part of a SOCKS5 Bytestream negotiation.
26 *
27 * @author Alexander Wenckus
28 */
29public class Bytestream extends IQ {
30
31 private String sessionID;
32
33 private Mode mode = Mode.tcp;
34
35 private final List<StreamHost> streamHosts = new ArrayList<StreamHost>();
36
37 private StreamHostUsed usedHost;
38
39 private Activate toActivate;
40
41 /**
42 * The default constructor
43 */
44 public Bytestream() {
45 super();
46 }
47
48 /**
49 * A constructor where the session ID can be specified.
50 *
51 * @param SID The session ID related to the negotiation.
52 * @see #setSessionID(String)
53 */
54 public Bytestream(final String SID) {
55 super();
56 setSessionID(SID);
57 }
58
59 /**
60 * Set the session ID related to the bytestream. The session ID is a unique identifier used to
61 * differentiate between stream negotiations.
62 *
63 * @param sessionID the unique session ID that identifies the transfer.
64 */
65 public void setSessionID(final String sessionID) {
66 this.sessionID = sessionID;
67 }
68
69 /**
70 * Returns the session ID related to the bytestream negotiation.
71 *
72 * @return Returns the session ID related to the bytestream negotiation.
73 * @see #setSessionID(String)
74 */
75 public String getSessionID() {
76 return sessionID;
77 }
78
79 /**
80 * Set the transport mode. This should be put in the initiation of the interaction.
81 *
82 * @param mode the transport mode, either UDP or TCP
83 * @see Mode
84 */
85 public void setMode(final Mode mode) {
86 this.mode = mode;
87 }
88
89 /**
90 * Returns the transport mode.
91 *
92 * @return Returns the transport mode.
93 * @see #setMode(Mode)
94 */
95 public Mode getMode() {
96 return mode;
97 }
98
99 /**
100 * Adds a potential stream host that the remote user can connect to to receive the file.
101 *
102 * @param JID The JID of the stream host.
103 * @param address The internet address of the stream host.
104 * @return The added stream host.
105 */
106 public StreamHost addStreamHost(final String JID, final String address) {
107 return addStreamHost(JID, address, 0);
108 }
109
110 /**
111 * Adds a potential stream host that the remote user can connect to to receive the file.
112 *
113 * @param JID The JID of the stream host.
114 * @param address The internet address of the stream host.
115 * @param port The port on which the remote host is seeking connections.
116 * @return The added stream host.
117 */
118 public StreamHost addStreamHost(final String JID, final String address, final int port) {
119 StreamHost host = new StreamHost(JID, address);
120 host.setPort(port);
121 addStreamHost(host);
122
123 return host;
124 }
125
126 /**
127 * Adds a potential stream host that the remote user can transfer the file through.
128 *
129 * @param host The potential stream host.
130 */
131 public void addStreamHost(final StreamHost host) {
132 streamHosts.add(host);
133 }
134
135 /**
136 * Returns the list of stream hosts contained in the packet.
137 *
138 * @return Returns the list of stream hosts contained in the packet.
139 */
140 public Collection<StreamHost> getStreamHosts() {
141 return Collections.unmodifiableCollection(streamHosts);
142 }
143
144 /**
145 * Returns the stream host related to the given JID, or null if there is none.
146 *
147 * @param JID The JID of the desired stream host.
148 * @return Returns the stream host related to the given JID, or null if there is none.
149 */
150 public StreamHost getStreamHost(final String JID) {
151 if (JID == null) {
152 return null;
153 }
154 for (StreamHost host : streamHosts) {
155 if (host.getJID().equals(JID)) {
156 return host;
157 }
158 }
159
160 return null;
161 }
162
163 /**
164 * Returns the count of stream hosts contained in this packet.
165 *
166 * @return Returns the count of stream hosts contained in this packet.
167 */
168 public int countStreamHosts() {
169 return streamHosts.size();
170 }
171
172 /**
173 * Upon connecting to the stream host the target of the stream replies to the initiator with the
174 * JID of the SOCKS5 host that they used.
175 *
176 * @param JID The JID of the used host.
177 */
178 public void setUsedHost(final String JID) {
179 this.usedHost = new StreamHostUsed(JID);
180 }
181
182 /**
183 * Returns the SOCKS5 host connected to by the remote user.
184 *
185 * @return Returns the SOCKS5 host connected to by the remote user.
186 */
187 public StreamHostUsed getUsedHost() {
188 return usedHost;
189 }
190
191 /**
192 * Returns the activate element of the packet sent to the proxy host to verify the identity of
193 * the initiator and match them to the appropriate stream.
194 *
195 * @return Returns the activate element of the packet sent to the proxy host to verify the
196 * identity of the initiator and match them to the appropriate stream.
197 */
198 public Activate getToActivate() {
199 return toActivate;
200 }
201
202 /**
203 * Upon the response from the target of the used host the activate packet is sent to the SOCKS5
204 * proxy. The proxy will activate the stream or return an error after verifying the identity of
205 * the initiator, using the activate packet.
206 *
207 * @param targetID The JID of the target of the file transfer.
208 */
209 public void setToActivate(final String targetID) {
210 this.toActivate = new Activate(targetID);
211 }
212
213 public String getChildElementXML() {
214 StringBuilder buf = new StringBuilder();
215
216 buf.append("<query xmlns=\"http://jabber.org/protocol/bytestreams\"");
217 if (this.getType().equals(IQ.Type.SET)) {
218 if (getSessionID() != null) {
219 buf.append(" sid=\"").append(getSessionID()).append("\"");
220 }
221 if (getMode() != null) {
222 buf.append(" mode = \"").append(getMode()).append("\"");
223 }
224 buf.append(">");
225 if (getToActivate() == null) {
226 for (StreamHost streamHost : getStreamHosts()) {
227 buf.append(streamHost.toXML());
228 }
229 }
230 else {
231 buf.append(getToActivate().toXML());
232 }
233 }
234 else if (this.getType().equals(IQ.Type.RESULT)) {
235 buf.append(">");
236 if (getUsedHost() != null) {
237 buf.append(getUsedHost().toXML());
238 }
239 // A result from the server can also contain stream hosts
240 else if (countStreamHosts() > 0) {
241 for (StreamHost host : streamHosts) {
242 buf.append(host.toXML());
243 }
244 }
245 }
246 else if (this.getType().equals(IQ.Type.GET)) {
247 return buf.append("/>").toString();
248 }
249 else {
250 return null;
251 }
252 buf.append("</query>");
253
254 return buf.toString();
255 }
256
257 /**
258 * Packet extension that represents a potential SOCKS5 proxy for the file transfer. Stream hosts
259 * are forwarded to the target of the file transfer who then chooses and connects to one.
260 *
261 * @author Alexander Wenckus
262 */
263 public static class StreamHost implements PacketExtension {
264
265 public static String NAMESPACE = "";
266
267 public static String ELEMENTNAME = "streamhost";
268
269 private final String JID;
270
271 private final String addy;
272
273 private int port = 0;
274
275 /**
276 * Default constructor.
277 *
278 * @param JID The JID of the stream host.
279 * @param address The internet address of the stream host.
280 */
281 public StreamHost(final String JID, final String address) {
282 this.JID = JID;
283 this.addy = address;
284 }
285
286 /**
287 * Returns the JID of the stream host.
288 *
289 * @return Returns the JID of the stream host.
290 */
291 public String getJID() {
292 return JID;
293 }
294
295 /**
296 * Returns the internet address of the stream host.
297 *
298 * @return Returns the internet address of the stream host.
299 */
300 public String getAddress() {
301 return addy;
302 }
303
304 /**
305 * Sets the port of the stream host.
306 *
307 * @param port The port on which the potential stream host would accept the connection.
308 */
309 public void setPort(final int port) {
310 this.port = port;
311 }
312
313 /**
314 * Returns the port on which the potential stream host would accept the connection.
315 *
316 * @return Returns the port on which the potential stream host would accept the connection.
317 */
318 public int getPort() {
319 return port;
320 }
321
322 public String getNamespace() {
323 return NAMESPACE;
324 }
325
326 public String getElementName() {
327 return ELEMENTNAME;
328 }
329
330 public String toXML() {
331 StringBuilder buf = new StringBuilder();
332
333 buf.append("<").append(getElementName()).append(" ");
334 buf.append("jid=\"").append(getJID()).append("\" ");
335 buf.append("host=\"").append(getAddress()).append("\" ");
336 if (getPort() != 0) {
337 buf.append("port=\"").append(getPort()).append("\"");
338 }
339 else {
340 buf.append("zeroconf=\"_jabber.bytestreams\"");
341 }
342 buf.append("/>");
343
344 return buf.toString();
345 }
346 }
347
348 /**
349 * After selected a SOCKS5 stream host and successfully connecting, the target of the file
350 * transfer returns a byte stream packet with the stream host used extension.
351 *
352 * @author Alexander Wenckus
353 */
354 public static class StreamHostUsed implements PacketExtension {
355
356 public String NAMESPACE = "";
357
358 public static String ELEMENTNAME = "streamhost-used";
359
360 private final String JID;
361
362 /**
363 * Default constructor.
364 *
365 * @param JID The JID of the selected stream host.
366 */
367 public StreamHostUsed(final String JID) {
368 this.JID = JID;
369 }
370
371 /**
372 * Returns the JID of the selected stream host.
373 *
374 * @return Returns the JID of the selected stream host.
375 */
376 public String getJID() {
377 return JID;
378 }
379
380 public String getNamespace() {
381 return NAMESPACE;
382 }
383
384 public String getElementName() {
385 return ELEMENTNAME;
386 }
387
388 public String toXML() {
389 StringBuilder buf = new StringBuilder();
390 buf.append("<").append(getElementName()).append(" ");
391 buf.append("jid=\"").append(getJID()).append("\" ");
392 buf.append("/>");
393 return buf.toString();
394 }
395 }
396
397 /**
398 * The packet sent by the stream initiator to the stream proxy to activate the connection.
399 *
400 * @author Alexander Wenckus
401 */
402 public static class Activate implements PacketExtension {
403
404 public String NAMESPACE = "";
405
406 public static String ELEMENTNAME = "activate";
407
408 private final String target;
409
410 /**
411 * Default constructor specifying the target of the stream.
412 *
413 * @param target The target of the stream.
414 */
415 public Activate(final String target) {
416 this.target = target;
417 }
418
419 /**
420 * Returns the target of the activation.
421 *
422 * @return Returns the target of the activation.
423 */
424 public String getTarget() {
425 return target;
426 }
427
428 public String getNamespace() {
429 return NAMESPACE;
430 }
431
432 public String getElementName() {
433 return ELEMENTNAME;
434 }
435
436 public String toXML() {
437 StringBuilder buf = new StringBuilder();
438 buf.append("<").append(getElementName()).append(">");
439 buf.append(getTarget());
440 buf.append("</").append(getElementName()).append(">");
441 return buf.toString();
442 }
443 }
444
445 /**
446 * The stream can be either a TCP stream or a UDP stream.
447 *
448 * @author Alexander Wenckus
449 */
450 public enum Mode {
451
452 /**
453 * A TCP based stream.
454 */
455 tcp,
456
457 /**
458 * A UDP based stream.
459 */
460 udp;
461
462 public static Mode fromName(String name) {
463 Mode mode;
464 try {
465 mode = Mode.valueOf(name);
466 }
467 catch (Exception ex) {
468 mode = tcp;
469 }
470
471 return mode;
472 }
473 }
474}