Home | History | Annotate | Download | only in filetransfer
      1 /**
      2  * $RCSfile$
      3  * $Revision$
      4  * $Date$
      5  *
      6  * Copyright 2003-2006 Jive Software.
      7  *
      8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *     http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  */
     20 package org.jivesoftware.smackx.filetransfer;
     21 
     22 import org.jivesoftware.smack.PacketListener;
     23 import org.jivesoftware.smack.Connection;
     24 import org.jivesoftware.smack.filter.AndFilter;
     25 import org.jivesoftware.smack.filter.IQTypeFilter;
     26 import org.jivesoftware.smack.filter.PacketTypeFilter;
     27 import org.jivesoftware.smack.packet.IQ;
     28 import org.jivesoftware.smack.packet.Packet;
     29 import org.jivesoftware.smack.packet.XMPPError;
     30 import org.jivesoftware.smack.util.StringUtils;
     31 import org.jivesoftware.smackx.packet.StreamInitiation;
     32 
     33 import java.util.ArrayList;
     34 import java.util.List;
     35 
     36 /**
     37  * The file transfer manager class handles the sending and recieving of files.
     38  * To send a file invoke the {@link #createOutgoingFileTransfer(String)} method.
     39  * <p>
     40  * And to recieve a file add a file transfer listener to the manager. The
     41  * listener will notify you when there is a new file transfer request. To create
     42  * the {@link IncomingFileTransfer} object accept the transfer, or, if the
     43  * transfer is not desirable reject it.
     44  *
     45  * @author Alexander Wenckus
     46  *
     47  */
     48 public class FileTransferManager {
     49 
     50 	private final FileTransferNegotiator fileTransferNegotiator;
     51 
     52 	private List<FileTransferListener> listeners;
     53 
     54 	private Connection connection;
     55 
     56 	/**
     57 	 * Creates a file transfer manager to initiate and receive file transfers.
     58 	 *
     59 	 * @param connection
     60 	 *            The Connection that the file transfers will use.
     61 	 */
     62 	public FileTransferManager(Connection connection) {
     63 		this.connection = connection;
     64 		this.fileTransferNegotiator = FileTransferNegotiator
     65 				.getInstanceFor(connection);
     66 	}
     67 
     68 	/**
     69 	 * Add a file transfer listener to listen to incoming file transfer
     70 	 * requests.
     71 	 *
     72 	 * @param li
     73 	 *            The listener
     74 	 * @see #removeFileTransferListener(FileTransferListener)
     75 	 * @see FileTransferListener
     76 	 */
     77 	public void addFileTransferListener(final FileTransferListener li) {
     78 		if (listeners == null) {
     79 			initListeners();
     80 		}
     81 		synchronized (this.listeners) {
     82 			listeners.add(li);
     83 		}
     84 	}
     85 
     86 	private void initListeners() {
     87 		listeners = new ArrayList<FileTransferListener>();
     88 
     89 		connection.addPacketListener(new PacketListener() {
     90 			public void processPacket(Packet packet) {
     91 				fireNewRequest((StreamInitiation) packet);
     92 			}
     93 		}, new AndFilter(new PacketTypeFilter(StreamInitiation.class),
     94 				new IQTypeFilter(IQ.Type.SET)));
     95 	}
     96 
     97 	protected void fireNewRequest(StreamInitiation initiation) {
     98 		FileTransferListener[] listeners = null;
     99 		synchronized (this.listeners) {
    100 			listeners = new FileTransferListener[this.listeners.size()];
    101 			this.listeners.toArray(listeners);
    102 		}
    103 		FileTransferRequest request = new FileTransferRequest(this, initiation);
    104 		for (int i = 0; i < listeners.length; i++) {
    105 			listeners[i].fileTransferRequest(request);
    106 		}
    107 	}
    108 
    109 	/**
    110 	 * Removes a file transfer listener.
    111 	 *
    112 	 * @param li
    113 	 *            The file transfer listener to be removed
    114 	 * @see FileTransferListener
    115 	 */
    116 	public void removeFileTransferListener(final FileTransferListener li) {
    117 		if (listeners == null) {
    118 			return;
    119 		}
    120 		synchronized (this.listeners) {
    121 			listeners.remove(li);
    122 		}
    123 	}
    124 
    125 	/**
    126 	 * Creates an OutgoingFileTransfer to send a file to another user.
    127 	 *
    128 	 * @param userID
    129 	 *            The fully qualified jabber ID (i.e. full JID) with resource of the user to
    130 	 *            send the file to.
    131 	 * @return The send file object on which the negotiated transfer can be run.
    132 	 * @exception IllegalArgumentException if userID is null or not a full JID
    133 	 */
    134 	public OutgoingFileTransfer createOutgoingFileTransfer(String userID) {
    135         if (userID == null) {
    136             throw new IllegalArgumentException("userID was null");
    137         }
    138         // We need to create outgoing file transfers with a full JID since this method will later
    139         // use XEP-0095 to negotiate the stream. This is done with IQ stanzas that need to be addressed to a full JID
    140         // in order to reach an client entity.
    141         else if (!StringUtils.isFullJID(userID)) {
    142             throw new IllegalArgumentException("The provided user id was not a full JID (i.e. with resource part)");
    143         }
    144 
    145 		return new OutgoingFileTransfer(connection.getUser(), userID,
    146 				fileTransferNegotiator.getNextStreamID(),
    147 				fileTransferNegotiator);
    148 	}
    149 
    150 	/**
    151 	 * When the file transfer request is acceptable, this method should be
    152 	 * invoked. It will create an IncomingFileTransfer which allows the
    153 	 * transmission of the file to procede.
    154 	 *
    155 	 * @param request
    156 	 *            The remote request that is being accepted.
    157 	 * @return The IncomingFileTransfer which manages the download of the file
    158 	 *         from the transfer initiator.
    159 	 */
    160 	protected IncomingFileTransfer createIncomingFileTransfer(
    161 			FileTransferRequest request) {
    162 		if (request == null) {
    163 			throw new NullPointerException("RecieveRequest cannot be null");
    164 		}
    165 
    166 		IncomingFileTransfer transfer = new IncomingFileTransfer(request,
    167                 fileTransferNegotiator);
    168 		transfer.setFileInfo(request.getFileName(), request.getFileSize());
    169 
    170 		return transfer;
    171 	}
    172 
    173 	protected void rejectIncomingFileTransfer(FileTransferRequest request) {
    174 		StreamInitiation initiation = request.getStreamInitiation();
    175 
    176 		IQ rejection = FileTransferNegotiator.createIQ(
    177 				initiation.getPacketID(), initiation.getFrom(), initiation
    178 						.getTo(), IQ.Type.ERROR);
    179 		rejection.setError(new XMPPError(XMPPError.Condition.no_acceptable));
    180 		connection.sendPacket(rejection);
    181 	}
    182 }
    183