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 org.jivesoftware.smack.PacketCollector;
020import org.jivesoftware.smack.SmackException;
021import org.jivesoftware.smack.SmackException.NoResponseException;
022import org.jivesoftware.smack.SmackException.NotConnectedException;
023import org.jivesoftware.smack.XMPPConnection;
024import org.jivesoftware.smack.XMPPException;
025import org.jivesoftware.smack.XMPPException.XMPPErrorException;
026import org.jivesoftware.smack.filter.PacketFilter;
027import org.jivesoftware.smack.packet.IQ;
028import org.jivesoftware.smack.packet.Packet;
029import org.jivesoftware.smack.packet.XMPPError;
030import org.jivesoftware.smackx.si.packet.StreamInitiation;
031import org.jivesoftware.smackx.xdata.Form;
032import org.jivesoftware.smackx.xdata.FormField;
033import org.jivesoftware.smackx.xdata.packet.DataForm;
034
035import java.io.InputStream;
036import java.io.OutputStream;
037
038/**
039 * After the file transfer negotiation process is completed according to
040 * XEP-0096, the negotiation process is passed off to a particular stream
041 * negotiator. The stream negotiator will then negotiate the chosen stream and
042 * return the stream to transfer the file.
043 *
044 * @author Alexander Wenckus
045 */
046public abstract class StreamNegotiator {
047
048    /**
049     * Creates the initiation acceptance packet to forward to the stream
050     * initiator.
051     *
052     * @param streamInitiationOffer The offer from the stream initiator to connect for a stream.
053     * @param namespaces            The namespace that relates to the accepted means of transfer.
054     * @return The response to be forwarded to the initiator.
055     */
056    public StreamInitiation createInitiationAccept(
057            StreamInitiation streamInitiationOffer, String[] namespaces)
058    {
059        StreamInitiation response = new StreamInitiation();
060        response.setTo(streamInitiationOffer.getFrom());
061        response.setFrom(streamInitiationOffer.getTo());
062        response.setType(IQ.Type.RESULT);
063        response.setPacketID(streamInitiationOffer.getPacketID());
064
065        DataForm form = new DataForm(Form.TYPE_SUBMIT);
066        FormField field = new FormField(
067                FileTransferNegotiator.STREAM_DATA_FIELD_NAME);
068        for (String namespace : namespaces) {
069            field.addValue(namespace);
070        }
071        form.addField(field);
072
073        response.setFeatureNegotiationForm(form);
074        return response;
075    }
076
077
078    public IQ createError(String from, String to, String packetID, XMPPError xmppError) {
079        IQ iq = FileTransferNegotiator.createIQ(packetID, to, from, IQ.Type.ERROR);
080        iq.setError(xmppError);
081        return iq;
082    }
083
084    Packet initiateIncomingStream(XMPPConnection connection, StreamInitiation initiation) throws NoResponseException, XMPPErrorException, NotConnectedException  {
085        StreamInitiation response = createInitiationAccept(initiation,
086                getNamespaces());
087
088        // establish collector to await response
089        PacketCollector collector = connection
090                .createPacketCollector(getInitiationPacketFilter(initiation.getFrom(), initiation.getSessionID()));
091        connection.sendPacket(response);
092
093        Packet streamMethodInitiation = collector.nextResultOrThrow();
094
095        return streamMethodInitiation;
096    }
097
098    /**
099     * Returns the packet filter that will return the initiation packet for the appropriate stream
100     * initiation.
101     *
102     * @param from     The initiator of the file transfer.
103     * @param streamID The stream ID related to the transfer.
104     * @return The <b><i>PacketFilter</b></i> that will return the packet relatable to the stream
105     *         initiation.
106     */
107    public abstract PacketFilter getInitiationPacketFilter(String from, String streamID);
108
109
110    abstract InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPErrorException,
111            InterruptedException, NoResponseException, SmackException;
112
113    /**
114     * This method handles the file stream download negotiation process. The
115     * appropriate stream negotiator's initiate incoming stream is called after
116     * an appropriate file transfer method is selected. The manager will respond
117     * to the initiator with the selected means of transfer, then it will handle
118     * any negotiation specific to the particular transfer method. This method
119     * returns the InputStream, ready to transfer the file.
120     *
121     * @param initiation The initiation that triggered this download.
122     * @return After the negotiation process is complete, the InputStream to
123     *         write a file to is returned.
124     * @throws XMPPErrorException If an error occurs during this process an XMPPException is
125     *                       thrown.
126     * @throws InterruptedException If thread is interrupted.
127     * @throws SmackException 
128     */
129    public abstract InputStream createIncomingStream(StreamInitiation initiation)
130            throws XMPPErrorException, InterruptedException, NoResponseException, SmackException;
131
132    /**
133     * This method handles the file upload stream negotiation process. The
134     * particular stream negotiator is determined during the file transfer
135     * negotiation process. This method returns the OutputStream to transmit the
136     * file to the remote user.
137     *
138     * @param streamID  The streamID that uniquely identifies the file transfer.
139     * @param initiator The fully-qualified JID of the initiator of the file transfer.
140     * @param target    The fully-qualified JID of the target or receiver of the file
141     *                  transfer.
142     * @return The negotiated stream ready for data.
143     * @throws XMPPErrorException If an error occurs during the negotiation process an
144     *                       exception will be thrown.
145     * @throws SmackException 
146     * @throws XMPPException 
147     */
148    public abstract OutputStream createOutgoingStream(String streamID,
149            String initiator, String target) throws XMPPErrorException, NoResponseException, SmackException, XMPPException;
150
151    /**
152     * Returns the XMPP namespace reserved for this particular type of file
153     * transfer.
154     *
155     * @return Returns the XMPP namespace reserved for this particular type of
156     *         file transfer.
157     */
158    public abstract String[] getNamespaces();
159
160    /**
161     * Cleanup any and all resources associated with this negotiator.
162     */
163    public abstract void cleanup();
164
165}