FileTransferManager.java

  1. /**
  2.  *
  3.  * Copyright 2003-2006 Jive Software.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.filetransfer;

  18. import org.jivesoftware.smack.Manager;
  19. import org.jivesoftware.smack.SmackException.NotConnectedException;
  20. import org.jivesoftware.smack.XMPPConnection;
  21. import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
  22. import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
  23. import org.jivesoftware.smack.packet.IQ;
  24. import org.jivesoftware.smack.packet.XMPPError;
  25. import org.jivesoftware.smackx.si.packet.StreamInitiation;
  26. import org.jxmpp.jid.FullJid;

  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.WeakHashMap;
  30. import java.util.concurrent.CopyOnWriteArrayList;

  31. /**
  32.  * The file transfer manager class handles the sending and recieving of files.
  33.  * To send a file invoke the {@link #createOutgoingFileTransfer(FullJid)} method.
  34.  * <p>
  35.  * And to recieve a file add a file transfer listener to the manager. The
  36.  * listener will notify you when there is a new file transfer request. To create
  37.  * the {@link IncomingFileTransfer} object accept the transfer, or, if the
  38.  * transfer is not desirable reject it.
  39.  *
  40.  * @author Alexander Wenckus
  41.  *
  42.  */
  43. public class FileTransferManager extends Manager {

  44.     private static final Map<XMPPConnection, FileTransferManager> INSTANCES = new WeakHashMap<XMPPConnection, FileTransferManager>();

  45.     public static synchronized FileTransferManager getInstanceFor(XMPPConnection connection) {
  46.         FileTransferManager fileTransferManager = INSTANCES.get(connection);
  47.         if (fileTransferManager == null) {
  48.             fileTransferManager = new FileTransferManager(connection);
  49.             INSTANCES.put(connection, fileTransferManager);
  50.         }
  51.         return fileTransferManager;
  52.     }

  53.     private final FileTransferNegotiator fileTransferNegotiator;

  54.     private final List<FileTransferListener> listeners = new CopyOnWriteArrayList<FileTransferListener>();

  55.     /**
  56.      * Creates a file transfer manager to initiate and receive file transfers.
  57.      *
  58.      * @param connection
  59.      *            The XMPPConnection that the file transfers will use.
  60.      */
  61.     private FileTransferManager(XMPPConnection connection) {
  62.         super(connection);
  63.         this.fileTransferNegotiator = FileTransferNegotiator
  64.                 .getInstanceFor(connection);
  65.         connection.registerIQRequestHandler(new AbstractIqRequestHandler(StreamInitiation.ELEMENT,
  66.                         StreamInitiation.NAMESPACE, IQ.Type.set, Mode.async) {
  67.             @Override
  68.             public IQ handleIQRequest(IQ packet) {
  69.                 StreamInitiation si = (StreamInitiation) packet;
  70.                 final FileTransferRequest request = new FileTransferRequest(FileTransferManager.this, si);
  71.                 for (final FileTransferListener listener : listeners) {
  72.                             listener.fileTransferRequest(request);
  73.                 }
  74.                 return null;
  75.             }
  76.         });
  77.     }

  78.     /**
  79.      * Add a file transfer listener to listen to incoming file transfer
  80.      * requests.
  81.      *
  82.      * @param li
  83.      *            The listener
  84.      * @see #removeFileTransferListener(FileTransferListener)
  85.      * @see FileTransferListener
  86.      */
  87.     public void addFileTransferListener(final FileTransferListener li) {
  88.         listeners.add(li);
  89.     }

  90.     /**
  91.      * Removes a file transfer listener.
  92.      *
  93.      * @param li
  94.      *            The file transfer listener to be removed
  95.      * @see FileTransferListener
  96.      */
  97.     public void removeFileTransferListener(final FileTransferListener li) {
  98.         listeners.remove(li);
  99.     }

  100.     /**
  101.      * Creates an OutgoingFileTransfer to send a file to another user.
  102.      *
  103.      * @param userID
  104.      *            The fully qualified jabber ID (i.e. full JID) with resource of the user to
  105.      *            send the file to.
  106.      * @return The send file object on which the negotiated transfer can be run.
  107.      * @exception IllegalArgumentException if userID is null or not a full JID
  108.      */
  109.     public OutgoingFileTransfer createOutgoingFileTransfer(FullJid userID) {
  110.         // We need to create outgoing file transfers with a full JID since this method will later
  111.         // use XEP-0095 to negotiate the stream. This is done with IQ stanzas that need to be addressed to a full JID
  112.         // in order to reach an client entity.
  113.         if (userID == null) {
  114.             throw new IllegalArgumentException("userID was null");
  115.         }

  116.         return new OutgoingFileTransfer(connection().getUser(), userID,
  117.                 fileTransferNegotiator.getNextStreamID(),
  118.                 fileTransferNegotiator);
  119.     }

  120.     /**
  121.      * When the file transfer request is acceptable, this method should be
  122.      * invoked. It will create an IncomingFileTransfer which allows the
  123.      * transmission of the file to procede.
  124.      *
  125.      * @param request
  126.      *            The remote request that is being accepted.
  127.      * @return The IncomingFileTransfer which manages the download of the file
  128.      *         from the transfer initiator.
  129.      */
  130.     protected IncomingFileTransfer createIncomingFileTransfer(
  131.             FileTransferRequest request) {
  132.         if (request == null) {
  133.             throw new NullPointerException("RecieveRequest cannot be null");
  134.         }

  135.         IncomingFileTransfer transfer = new IncomingFileTransfer(request,
  136.                 fileTransferNegotiator);
  137.         transfer.setFileInfo(request.getFileName(), request.getFileSize());

  138.         return transfer;
  139.     }

  140.     /**
  141.      * Reject an incoming file transfer.
  142.      * <p>
  143.      * Specified in XEP-95 4.2 and 3.2 Example 8
  144.      * </p>
  145.      * @param request
  146.      * @throws NotConnectedException
  147.      * @throws InterruptedException
  148.      */
  149.     protected void rejectIncomingFileTransfer(FileTransferRequest request) throws NotConnectedException, InterruptedException {
  150.         StreamInitiation initiation = request.getStreamInitiation();

  151.         // Reject as specified in XEP-95 4.2. Note that this is not to be confused with the Socks 5
  152.         // Bytestream rejection as specified in XEP-65 5.3.1 Example 13, which says that
  153.         // 'not-acceptable' should be returned. This is done by Smack in
  154.         // Socks5BytestreamManager.replyRejectPacket(IQ).
  155.         IQ rejection = IQ.createErrorResponse(initiation, new XMPPError(
  156.                         XMPPError.Condition.forbidden));
  157.         connection().sendStanza(rejection);
  158.     }
  159. }