• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
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  */
14 package org.jivesoftware.smackx.filetransfer;
15 
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19 import java.io.PushbackInputStream;
20 
21 import org.jivesoftware.smack.Connection;
22 import org.jivesoftware.smack.XMPPException;
23 import org.jivesoftware.smack.filter.AndFilter;
24 import org.jivesoftware.smack.filter.FromMatchesFilter;
25 import org.jivesoftware.smack.filter.PacketFilter;
26 import org.jivesoftware.smack.filter.PacketTypeFilter;
27 import org.jivesoftware.smack.packet.IQ;
28 import org.jivesoftware.smack.packet.Packet;
29 import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
30 import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamRequest;
31 import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession;
32 import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
33 import org.jivesoftware.smackx.packet.StreamInitiation;
34 
35 /**
36  * Negotiates a SOCKS5 Bytestream to be used for file transfers. The implementation is based on the
37  * {@link Socks5BytestreamManager} and the {@link Socks5BytestreamRequest}.
38  *
39  * @author Henning Staib
40  * @see <a href="http://xmpp.org/extensions/xep-0065.html">XEP-0065: SOCKS5 Bytestreams</a>
41  */
42 public class Socks5TransferNegotiator extends StreamNegotiator {
43 
44     private Connection connection;
45 
46     private Socks5BytestreamManager manager;
47 
Socks5TransferNegotiator(Connection connection)48     Socks5TransferNegotiator(Connection connection) {
49         this.connection = connection;
50         this.manager = Socks5BytestreamManager.getBytestreamManager(this.connection);
51     }
52 
53     @Override
createOutgoingStream(String streamID, String initiator, String target)54     public OutputStream createOutgoingStream(String streamID, String initiator, String target)
55                     throws XMPPException {
56         try {
57             return this.manager.establishSession(target, streamID).getOutputStream();
58         }
59         catch (IOException e) {
60             throw new XMPPException("error establishing SOCKS5 Bytestream", e);
61         }
62         catch (InterruptedException e) {
63             throw new XMPPException("error establishing SOCKS5 Bytestream", e);
64         }
65     }
66 
67     @Override
createIncomingStream(StreamInitiation initiation)68     public InputStream createIncomingStream(StreamInitiation initiation) throws XMPPException,
69                     InterruptedException {
70         /*
71          * SOCKS5 initiation listener must ignore next SOCKS5 Bytestream request with given session
72          * ID
73          */
74         this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID());
75 
76         Packet streamInitiation = initiateIncomingStream(this.connection, initiation);
77         return negotiateIncomingStream(streamInitiation);
78     }
79 
80     @Override
getInitiationPacketFilter(final String from, String streamID)81     public PacketFilter getInitiationPacketFilter(final String from, String streamID) {
82         /*
83          * this method is always called prior to #negotiateIncomingStream() so the SOCKS5
84          * InitiationListener must ignore the next SOCKS5 Bytestream request with the given session
85          * ID
86          */
87         this.manager.ignoreBytestreamRequestOnce(streamID);
88 
89         return new AndFilter(new FromMatchesFilter(from), new BytestreamSIDFilter(streamID));
90     }
91 
92     @Override
getNamespaces()93     public String[] getNamespaces() {
94         return new String[] { Socks5BytestreamManager.NAMESPACE };
95     }
96 
97     @Override
negotiateIncomingStream(Packet streamInitiation)98     InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException,
99                     InterruptedException {
100         // build SOCKS5 Bytestream request
101         Socks5BytestreamRequest request = new ByteStreamRequest(this.manager,
102                         (Bytestream) streamInitiation);
103 
104         // always accept the request
105         Socks5BytestreamSession session = request.accept();
106 
107         // test input stream
108         try {
109             PushbackInputStream stream = new PushbackInputStream(session.getInputStream());
110             int firstByte = stream.read();
111             stream.unread(firstByte);
112             return stream;
113         }
114         catch (IOException e) {
115             throw new XMPPException("Error establishing input stream", e);
116         }
117     }
118 
119     @Override
cleanup()120     public void cleanup() {
121         /* do nothing */
122     }
123 
124     /**
125      * This PacketFilter accepts an incoming SOCKS5 Bytestream request with a specified session ID.
126      */
127     private static class BytestreamSIDFilter extends PacketTypeFilter {
128 
129         private String sessionID;
130 
BytestreamSIDFilter(String sessionID)131         public BytestreamSIDFilter(String sessionID) {
132             super(Bytestream.class);
133             if (sessionID == null) {
134                 throw new IllegalArgumentException("StreamID cannot be null");
135             }
136             this.sessionID = sessionID;
137         }
138 
139         @Override
accept(Packet packet)140         public boolean accept(Packet packet) {
141             if (super.accept(packet)) {
142                 Bytestream bytestream = (Bytestream) packet;
143 
144                 // packet must by of type SET and contains the given session ID
145                 return this.sessionID.equals(bytestream.getSessionID())
146                                 && IQ.Type.SET.equals(bytestream.getType());
147             }
148             return false;
149         }
150 
151     }
152 
153     /**
154      * Derive from Socks5BytestreamRequest to access protected constructor.
155      */
156     private static class ByteStreamRequest extends Socks5BytestreamRequest {
157 
ByteStreamRequest(Socks5BytestreamManager manager, Bytestream byteStreamRequest)158         private ByteStreamRequest(Socks5BytestreamManager manager, Bytestream byteStreamRequest) {
159             super(manager, byteStreamRequest);
160         }
161 
162     }
163 
164 }
165