/**
   * 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;
    }
  }
Example #2
0
  /**
   * Parses the given <tt>parser</tt> in order to create a <tt>FileElement</tt> from it.
   *
   * @param parser the parser to parse
   * @see IQProvider#parseIQ(XmlPullParser)
   */
  public IQ parseIQ(final XmlPullParser parser) throws Exception {
    boolean done = false;

    // si
    String id = parser.getAttributeValue("", "id");
    String mimeType = parser.getAttributeValue("", "mime-type");
    StreamInitiation initiation = new StreamInitiation();

    // file
    String name = null;
    String size = null;
    String hash = null;
    String date = null;
    String desc = null;
    ThumbnailElement thumbnail = null;
    boolean isRanged = false;

    // feature
    DataForm form = null;
    DataFormProvider dataFormProvider = new DataFormProvider();

    int eventType;
    String elementName;
    String namespace;

    while (!done) {
      eventType = parser.next();
      elementName = parser.getName();
      namespace = parser.getNamespace();

      if (eventType == XmlPullParser.START_TAG) {
        if (elementName.equals("file")) {
          name = parser.getAttributeValue("", "name");
          size = parser.getAttributeValue("", "size");
          hash = parser.getAttributeValue("", "hash");
          date = parser.getAttributeValue("", "date");
        } else if (elementName.equals("desc")) {
          desc = parser.nextText();
        } else if (elementName.equals("range")) {
          isRanged = true;
        } else if (elementName.equals("x") && namespace.equals("jabber:x:data")) {
          form = (DataForm) dataFormProvider.parseExtension(parser);
        } else if (elementName.equals("thumbnail")) {
          thumbnail = new ThumbnailElement(parser.getText());
        }
      } else if (eventType == XmlPullParser.END_TAG) {
        if (elementName.equals("si")) {
          done = true;
        }
        // The name-attribute is required per XEP-0096, so ignore the
        // IQ if the name is not set to avoid exceptions. Particularly,
        // the SI response of Empathy contains an invalid, empty
        // file-tag.
        else if (elementName.equals("file") && name != null) {
          long fileSize = 0;

          if (size != null && size.trim().length() != 0) {
            try {
              fileSize = Long.parseLong(size);
            } catch (NumberFormatException e) {
              logger.warn(
                  "Received an invalid file size," + " continuing with fileSize set to 0", e);
            }
          }

          FileElement file = new FileElement(name, fileSize);
          file.setHash(hash);

          if (date != null) {
            // try all known date formats
            boolean found = false;
            if (date.matches(".*?T\\d+:\\d+:\\d+(\\.\\d+)?(\\+|-)\\d+:\\d+")) {
              int timeZoneColon = date.lastIndexOf(":");
              date =
                  date.substring(0, timeZoneColon)
                      + date.substring(timeZoneColon + 1, date.length());
            }
            for (DateFormat fmt : DATE_FORMATS) {
              try {
                file.setDate(fmt.parse(date));
                found = true;
                break;
              } catch (ParseException ex) {
              }
            }

            if (!found) {
              logger.warn("Unknown dateformat on incoming file transfer: " + date);
            }
          }

          if (thumbnail != null) file.setThumbnailElement(thumbnail);

          file.setDesc(desc);
          file.setRanged(isRanged);
          initiation.setFile(file);
        }
      }
    }

    initiation.setSesssionID(id);
    initiation.setMimeType(mimeType);
    initiation.setFeatureNegotiationForm(form);

    return initiation;
  }