/**
   * Selects an appropriate stream negotiator after examining the incoming file transfer request.
   *
   * @param request The related file transfer request.
   * @return The file transfer object that handles the transfer
   * @throws XMPPException If there are either no stream methods contained in the packet, or there
   *     is not an appropriate stream method.
   */
  public StreamNegotiator selectStreamNegotiator(FileTransferRequest request) throws XMPPException {
    final StreamInitiation si = request.getStreamInitiation();
    final FormField streamMethodField = getStreamMethodField(si.getFeatureNegotiationForm());

    if (streamMethodField == null) {
      final String errorMessage = "No stream methods contained in packet.";
      final XMPPError error = new XMPPError(XMPPError.Condition.bad_request, errorMessage);
      final IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ.Type.ERROR);
      iqPacket.setError(error);
      connection.sendPacket(iqPacket);
      throw new XMPPException(errorMessage, error);
    }

    // select the appropriate protocol

    StreamNegotiator selectedStreamNegotiator;
    try {
      selectedStreamNegotiator = getNegotiator(streamMethodField);
    } catch (final XMPPException e) {
      final IQ iqPacket = createIQ(si.getPacketID(), si.getFrom(), si.getTo(), IQ.Type.ERROR);
      iqPacket.setError(e.getXMPPError());
      connection.sendPacket(iqPacket);
      throw e;
    }

    // return the appropriate negotiator

    return selectedStreamNegotiator;
  }
  /**
   * Send a request to another user to send them a file. The other user has the option of,
   * accepting, rejecting, or not responding to a received file transfer request.
   *
   * <p>If they accept, the packet will contain the other user's chosen stream type to send the file
   * across. The two choices this implementation provides to the other user for file transfer are <a
   * href="http://www.jabber.org/jeps/jep-0065.html">SOCKS5 Bytestreams</a>, which is the preferred
   * method of transfer, and <a href="http://www.jabber.org/jeps/jep-0047.html">In-Band
   * Bytestreams</a>, which is the fallback mechanism.
   *
   * <p>The other user may choose to decline the file request if they do not desire the file, their
   * client does not support JEP-0096, or if there are no acceptable means to transfer the file.
   *
   * <p>Finally, if the other user does not respond this method will return null after the specified
   * timeout.
   *
   * @param userID The userID of the user to whom the file will be sent.
   * @param streamID The unique identifier for this file transfer.
   * @param fileName The name of this file. Preferably it should include an extension as it is used
   *     to determine what type of file it is.
   * @param size The size, in bytes, of the file.
   * @param desc A description of the file.
   * @param responseTimeout The amount of time, in milliseconds, to wait for the remote user to
   *     respond. If they do not respond in time, this
   * @return Returns the stream negotiator selected by the peer.
   * @throws XMPPException Thrown if there is an error negotiating the file transfer.
   */
  public StreamNegotiator negotiateOutgoingTransfer(
      final String userID,
      final String streamID,
      final String fileName,
      final long size,
      final String desc,
      int responseTimeout)
      throws XMPPException {
    final StreamInitiation si = new StreamInitiation();
    si.setSesssionID(streamID);
    si.setMimeType(URLConnection.guessContentTypeFromName(fileName));

    final StreamInitiation.File siFile = new StreamInitiation.File(fileName, size);
    siFile.setDesc(desc);
    si.setFile(siFile);

    si.setFeatureNegotiationForm(createDefaultInitiationForm());

    si.setFrom(connection.getUser());
    si.setTo(userID);
    si.setType(IQ.Type.SET);

    final PacketCollector collector =
        connection.createPacketCollector(new PacketIDFilter(si.getPacketID()));
    connection.sendPacket(si);
    final Packet siResponse = collector.nextResult(responseTimeout);
    collector.cancel();

    if (siResponse instanceof IQ) {
      final IQ iqResponse = (IQ) siResponse;
      if (iqResponse.getType().equals(IQ.Type.RESULT)) {
        final StreamInitiation response = (StreamInitiation) siResponse;
        return getOutgoingNegotiator(getStreamMethodField(response.getFeatureNegotiationForm()));

      } else if (iqResponse.getType().equals(IQ.Type.ERROR)) {
        throw new XMPPException(iqResponse.getError());
      } else {
        throw new XMPPException("File transfer response unreadable");
      }
    } else {
      return null;
    }
  }