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.packet.Stanza;
027import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager;
028import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamRequest;
029import org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamSession;
030import org.jivesoftware.smackx.bytestreams.ibb.packet.DataPacketExtension;
031import org.jivesoftware.smackx.bytestreams.ibb.packet.Open;
032import org.jivesoftware.smackx.si.packet.StreamInitiation;
033
034/**
035 * The In-Band Bytestream file transfer method, or IBB for short, transfers the
036 * file over the same XML Stream used by XMPP. It is the fall-back mechanism in
037 * case the SOCKS5 bytestream method of transferring files is not available.
038 * 
039 * @author Alexander Wenckus
040 * @author Henning Staib
041 * @see <a href="http://xmpp.org/extensions/xep-0047.html">XEP-0047: In-Band
042 *      Bytestreams (IBB)</a>
043 */
044public class IBBTransferNegotiator extends StreamNegotiator {
045
046    private XMPPConnection connection;
047
048    private InBandBytestreamManager manager;
049
050    /**
051     * The default constructor for the In-Band Bytestream Negotiator.
052     * 
053     * @param connection The connection which this negotiator works on.
054     */
055    protected IBBTransferNegotiator(XMPPConnection connection) {
056        this.connection = connection;
057        this.manager = InBandBytestreamManager.getByteStreamManager(connection);
058    }
059
060    public OutputStream createOutgoingStream(String streamID, String initiator,
061                    String target) throws NoResponseException, XMPPErrorException, NotConnectedException {
062        InBandBytestreamSession session = this.manager.establishSession(target, streamID);
063        session.setCloseBothStreamsEnabled(true);
064        return session.getOutputStream();
065    }
066
067    public InputStream createIncomingStream(StreamInitiation initiation)
068                    throws NoResponseException, XMPPErrorException, NotConnectedException {
069        /*
070         * In-Band Bytestream initiation listener must ignore next in-band bytestream request with
071         * given session ID
072         */
073        this.manager.ignoreBytestreamRequestOnce(initiation.getSessionID());
074
075        Stanza streamInitiation = initiateIncomingStream(this.connection, initiation);
076        return negotiateIncomingStream(streamInitiation);
077    }
078
079    @Override
080    public void newStreamInitiation(String from, String streamID) {
081        /*
082         * this method is always called prior to #negotiateIncomingStream() so
083         * the In-Band Bytestream initiation listener must ignore the next
084         * In-Band Bytestream request with the given session ID
085         */
086        this.manager.ignoreBytestreamRequestOnce(streamID);
087    }
088
089    public String[] getNamespaces() {
090        return new String[] { DataPacketExtension.NAMESPACE };
091    }
092
093    InputStream negotiateIncomingStream(Stanza streamInitiation) throws NotConnectedException {
094        // build In-Band Bytestream request
095        InBandBytestreamRequest request = new ByteStreamRequest(this.manager,
096                        (Open) streamInitiation);
097
098        // always accept the request
099        InBandBytestreamSession session = request.accept();
100        session.setCloseBothStreamsEnabled(true);
101        return session.getInputStream();
102    }
103
104    /**
105     * Derive from InBandBytestreamRequest to access protected constructor.
106     */
107    private static class ByteStreamRequest extends InBandBytestreamRequest {
108
109        private ByteStreamRequest(InBandBytestreamManager manager, Open byteStreamRequest) {
110            super(manager, byteStreamRequest);
111        }
112
113    }
114
115}