001/** 002 * 003 * Copyright 2003-2006 Jive Software. 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.filetransfer; 018 019import java.io.InputStream; 020import java.io.OutputStream; 021 022import org.jivesoftware.smack.SmackException.NoResponseException; 023import org.jivesoftware.smack.SmackException.NotConnectedException; 024import org.jivesoftware.smack.XMPPConnection; 025import org.jivesoftware.smack.XMPPException.XMPPErrorException; 026import org.jivesoftware.smack.filter.AndFilter; 027import org.jivesoftware.smack.filter.FromMatchesFilter; 028import org.jivesoftware.smack.filter.PacketFilter; 029import org.jivesoftware.smack.filter.PacketTypeFilter; 030import org.jivesoftware.smack.packet.IQ; 031import org.jivesoftware.smack.packet.Packet; 032import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager; 033import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest; 034import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession; 035import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; 036import org.jivesoftware.smackx.si.packet.StreamInitiation; 037 038/** 039 * The In-Band Bytestream file transfer method, or IBB for short, transfers the 040 * file over the same XML Stream used by XMPP. It is the fall-back mechanism in 041 * case the SOCKS5 bytestream method of transferring files is not available. 042 * 043 * @author Alexander Wenckus 044 * @author Henning Staib 045 * @see <a href="http://xmpp.org/extensions/xep-0047.html">XEP-0047: In-Band 046 * Bytestreams (IBB)</a> 047 */ 048public class IBBTransferNegotiator extends StreamNegotiator { 049 050 private XMPPConnection connection; 051 052 private InBandBytestreamManager manager; 053 054 /** 055 * The default constructor for the In-Band Bytestream Negotiator. 056 * 057 * @param connection The connection which this negotiator works on. 058 */ 059 protected IBBTransferNegotiator(XMPPConnection connection) { 060 this.connection = connection; 061 this.manager = InBandBytestreamManager.getByteStreamManager(connection); 062 } 063 064 public OutputStream createOutgoingStream(String streamID, String initiator, 065 String target) throws NoResponseException, XMPPErrorException, NotConnectedException { 066 InBandBytestreamSession session = this.manager.establishSession(target, streamID); 067 session.setCloseBothStreamsEnabled(true); 068 return session.getOutputStream(); 069 } 070 071 public InputStream createIncomingStream(StreamInitiation initiation) 072 throws NoResponseException, XMPPErrorException, NotConnectedException { 073 /* 074 * In-Band Bytestream initiation listener must ignore next in-band bytestream request with 075 * given session ID 076 */ 077 this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID()); 078 079 Packet streamInitiation = initiateIncomingStream(this.connection, initiation); 080 return negotiateIncomingStream(streamInitiation); 081 } 082 083 public PacketFilter getInitiationPacketFilter(String from, String streamID) { 084 /* 085 * this method is always called prior to #negotiateIncomingStream() so 086 * the In-Band Bytestream initiation listener must ignore the next 087 * In-Band Bytestream request with the given session ID 088 */ 089 this.manager.ignoreBytestreamRequestOnce(streamID); 090 091 return new AndFilter(FromMatchesFilter.create(from), new IBBOpenSidFilter(streamID)); 092 } 093 094 public String[] getNamespaces() { 095 return new String[] { InBandBytestreamManager.NAMESPACE }; 096 } 097 098 InputStream negotiateIncomingStream(Packet streamInitiation) throws NotConnectedException { 099 // build In-Band Bytestream request 100 InBandBytestreamRequest request = new ByteStreamRequest(this.manager, 101 (Open) streamInitiation); 102 103 // always accept the request 104 InBandBytestreamSession session = request.accept(); 105 session.setCloseBothStreamsEnabled(true); 106 return session.getInputStream(); 107 } 108 109 public void cleanup() { 110 } 111 112 /** 113 * This PacketFilter accepts an incoming In-Band Bytestream open request 114 * with a specified session ID. 115 */ 116 private static class IBBOpenSidFilter extends PacketTypeFilter { 117 118 private String sessionID; 119 120 public IBBOpenSidFilter(String sessionID) { 121 super(Open.class); 122 if (sessionID == null) { 123 throw new IllegalArgumentException("StreamID cannot be null"); 124 } 125 this.sessionID = sessionID; 126 } 127 128 public boolean accept(Packet packet) { 129 if (super.accept(packet)) { 130 Open bytestream = (Open) packet; 131 132 // packet must by of type SET and contains the given session ID 133 return this.sessionID.equals(bytestream.getSessionID()) 134 && IQ.Type.SET.equals(bytestream.getType()); 135 } 136 return false; 137 } 138 } 139 140 /** 141 * Derive from InBandBytestreamRequest to access protected constructor. 142 */ 143 private static class ByteStreamRequest extends InBandBytestreamRequest { 144 145 private ByteStreamRequest(InBandBytestreamManager manager, Open byteStreamRequest) { 146 super(manager, byteStreamRequest); 147 } 148 149 } 150 151}