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}