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}