1 /** 2 * $RCSfile$ 3 * $Revision$ 4 * $Date$ 5 * 6 * Copyright 2003-2006 Jive Software. 7 * 8 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 package org.jivesoftware.smackx.filetransfer; 21 22 import java.io.InputStream; 23 import java.io.OutputStream; 24 25 import org.jivesoftware.smack.Connection; 26 import org.jivesoftware.smack.XMPPException; 27 import org.jivesoftware.smack.filter.AndFilter; 28 import org.jivesoftware.smack.filter.FromContainsFilter; 29 import org.jivesoftware.smack.filter.PacketFilter; 30 import org.jivesoftware.smack.filter.PacketTypeFilter; 31 import org.jivesoftware.smack.packet.IQ; 32 import org.jivesoftware.smack.packet.Packet; 33 import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; 34 import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest; 35 import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; 36 import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; 37 import org.jivesoftware.smackx.packet.StreamInitiation; 38 39 /** 40 * The In-Band Bytestream file transfer method, or IBB for short, transfers the 41 * file over the same XML Stream used by XMPP. It is the fall-back mechanism in 42 * case the SOCKS5 bytestream method of transferring files is not available. 43 * 44 * @author Alexander Wenckus 45 * @author Henning Staib 46 * @see <a href="http://xmpp.org/extensions/xep-0047.html">XEP-0047: In-Band 47 * Bytestreams (IBB)</a> 48 */ 49 public class IBBTransferNegotiator extends StreamNegotiator { 50 51 private Connection connection; 52 53 private InBandBytestreamManager manager; 54 55 /** 56 * The default constructor for the In-Band Bytestream Negotiator. 57 * 58 * @param connection The connection which this negotiator works on. 59 */ 60 protected IBBTransferNegotiator(Connection connection) { 61 this.connection = connection; 62 this.manager = InBandBytestreamManager.getByteStreamManager(connection); 63 } 64 65 public OutputStream createOutgoingStream(String streamID, String initiator, 66 String target) throws XMPPException { 67 InBandBytestreamSession session = this.manager.establishSession(target, streamID); 68 session.setCloseBothStreamsEnabled(true); 69 return session.getOutputStream(); 70 } 71 72 public InputStream createIncomingStream(StreamInitiation initiation) 73 throws XMPPException { 74 /* 75 * In-Band Bytestream initiation listener must ignore next in-band 76 * bytestream request with given session ID 77 */ 78 this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID()); 79 80 Packet streamInitiation = initiateIncomingStream(this.connection, initiation); 81 return negotiateIncomingStream(streamInitiation); 82 } 83 84 public PacketFilter getInitiationPacketFilter(String from, String streamID) { 85 /* 86 * this method is always called prior to #negotiateIncomingStream() so 87 * the In-Band Bytestream initiation listener must ignore the next 88 * In-Band Bytestream request with the given session ID 89 */ 90 this.manager.ignoreBytestreamRequestOnce(streamID); 91 92 return new AndFilter(new FromContainsFilter(from), new IBBOpenSidFilter(streamID)); 93 } 94 95 public String[] getNamespaces() { 96 return new String[] { InBandBytestreamManager.NAMESPACE }; 97 } 98 99 InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException { 100 // build In-Band Bytestream request 101 InBandBytestreamRequest request = new ByteStreamRequest(this.manager, 102 (Open) streamInitiation); 103 104 // always accept the request 105 InBandBytestreamSession session = request.accept(); 106 session.setCloseBothStreamsEnabled(true); 107 return session.getInputStream(); 108 } 109 110 public void cleanup() { 111 } 112 113 /** 114 * This PacketFilter accepts an incoming In-Band Bytestream open request 115 * with a specified session ID. 116 */ 117 private static class IBBOpenSidFilter extends PacketTypeFilter { 118 119 private String sessionID; 120 121 public IBBOpenSidFilter(String sessionID) { 122 super(Open.class); 123 if (sessionID == null) { 124 throw new IllegalArgumentException("StreamID cannot be null"); 125 } 126 this.sessionID = sessionID; 127 } 128 129 public boolean accept(Packet packet) { 130 if (super.accept(packet)) { 131 Open bytestream = (Open) packet; 132 133 // packet must by of type SET and contains the given session ID 134 return this.sessionID.equals(bytestream.getSessionID()) 135 && IQ.Type.SET.equals(bytestream.getType()); 136 } 137 return false; 138 } 139 } 140 141 /** 142 * Derive from InBandBytestreamRequest to access protected constructor. 143 */ 144 private static class ByteStreamRequest extends InBandBytestreamRequest { 145 146 private ByteStreamRequest(InBandBytestreamManager manager, Open byteStreamRequest) { 147 super(manager, byteStreamRequest); 148 } 149 150 } 151 152 } 153