/** * $RCSfile$ * $Revision$ * $Date$ * * Copyright 2003-2006 Jive Software. * * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.smackx.filetransfer; import org.jivesoftware.smack.PacketCollector; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.Connection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smackx.Form; import org.jivesoftware.smackx.FormField; import org.jivesoftware.smackx.packet.DataForm; import org.jivesoftware.smackx.packet.StreamInitiation; import java.io.InputStream; import java.io.OutputStream; /** * After the file transfer negotiation process is completed according to * JEP-0096, the negotiation process is passed off to a particular stream * negotiator. The stream negotiator will then negotiate the chosen stream and * return the stream to transfer the file. * * @author Alexander Wenckus */ public abstract class StreamNegotiator { /** * Creates the initiation acceptance packet to forward to the stream * initiator. * * @param streamInitiationOffer The offer from the stream initiator to connect for a stream. * @param namespaces The namespace that relates to the accepted means of transfer. * @return The response to be forwarded to the initiator. */ public StreamInitiation createInitiationAccept( StreamInitiation streamInitiationOffer, String[] namespaces) { StreamInitiation response = new StreamInitiation(); response.setTo(streamInitiationOffer.getFrom()); response.setFrom(streamInitiationOffer.getTo()); response.setType(IQ.Type.RESULT); response.setPacketID(streamInitiationOffer.getPacketID()); DataForm form = new DataForm(Form.TYPE_SUBMIT); FormField field = new FormField( FileTransferNegotiator.STREAM_DATA_FIELD_NAME); for (String namespace : namespaces) { field.addValue(namespace); } form.addField(field); response.setFeatureNegotiationForm(form); return response; } public IQ createError(String from, String to, String packetID, XMPPError xmppError) { IQ iq = FileTransferNegotiator.createIQ(packetID, to, from, IQ.Type.ERROR); iq.setError(xmppError); return iq; } Packet initiateIncomingStream(Connection connection, StreamInitiation initiation) throws XMPPException { StreamInitiation response = createInitiationAccept(initiation, getNamespaces()); // establish collector to await response PacketCollector collector = connection .createPacketCollector(getInitiationPacketFilter(initiation.getFrom(), initiation.getSessionID())); connection.sendPacket(response); Packet streamMethodInitiation = collector .nextResult(SmackConfiguration.getPacketReplyTimeout()); collector.cancel(); if (streamMethodInitiation == null) { throw new XMPPException("No response from file transfer initiator"); } return streamMethodInitiation; } /** * Returns the packet filter that will return the initiation packet for the appropriate stream * initiation. * * @param from The initiator of the file transfer. * @param streamID The stream ID related to the transfer. * @return The <b><i>PacketFilter</b></i> that will return the packet relatable to the stream * initiation. */ public abstract PacketFilter getInitiationPacketFilter(String from, String streamID); abstract InputStream negotiateIncomingStream(Packet streamInitiation) throws XMPPException, InterruptedException; /** * This method handles the file stream download negotiation process. The * appropriate stream negotiator's initiate incoming stream is called after * an appropriate file transfer method is selected. The manager will respond * to the initiator with the selected means of transfer, then it will handle * any negotiation specific to the particular transfer method. This method * returns the InputStream, ready to transfer the file. * * @param initiation The initiation that triggered this download. * @return After the negotiation process is complete, the InputStream to * write a file to is returned. * @throws XMPPException If an error occurs during this process an XMPPException is * thrown. * @throws InterruptedException If thread is interrupted. */ public abstract InputStream createIncomingStream(StreamInitiation initiation) throws XMPPException, InterruptedException; /** * This method handles the file upload stream negotiation process. The * particular stream negotiator is determined during the file transfer * negotiation process. This method returns the OutputStream to transmit the * file to the remote user. * * @param streamID The streamID that uniquely identifies the file transfer. * @param initiator The fully-qualified JID of the initiator of the file transfer. * @param target The fully-qualified JID of the target or receiver of the file * transfer. * @return The negotiated stream ready for data. * @throws XMPPException If an error occurs during the negotiation process an * exception will be thrown. */ public abstract OutputStream createOutgoingStream(String streamID, String initiator, String target) throws XMPPException; /** * Returns the XMPP namespace reserved for this particular type of file * transfer. * * @return Returns the XMPP namespace reserved for this particular type of * file transfer. */ public abstract String[] getNamespaces(); /** * Cleanup any and all resources associated with this negotiator. */ public abstract void cleanup(); }