/**
   * Sends a file transfer request to the given <tt>toContact</tt>.
   *
   * @return the transfer object
   * @param toContact the contact that should receive the file
   * @param file file to send
   * @param gw special gateway to be used for receiver if its jid misses the domain part
   */
  FileTransfer sendFile(Contact toContact, File file, String gw)
      throws IllegalStateException, IllegalArgumentException, OperationNotSupportedException {
    OutgoingFileTransferJabberImpl outgoingTransfer = null;

    try {
      assertConnected();

      if (file.length() > getMaximumFileLength())
        throw new IllegalArgumentException("File length exceeds the allowed one for this protocol");

      String fullJid = null;
      // Find the jid of the contact which support file transfer
      // and is with highest priority if more than one found
      // if we have equals priorities
      // choose the one that is more available
      OperationSetMultiUserChat mucOpSet =
          jabberProvider.getOperationSet(OperationSetMultiUserChat.class);
      if (mucOpSet != null && mucOpSet.isPrivateMessagingContact(toContact.getAddress())) {
        fullJid = toContact.getAddress();
      } else {
        Iterator<Presence> iter =
            jabberProvider.getConnection().getRoster().getPresences(toContact.getAddress());
        int bestPriority = -1;

        PresenceStatus jabberStatus = null;

        while (iter.hasNext()) {
          Presence presence = iter.next();

          if (jabberProvider.isFeatureListSupported(
              presence.getFrom(),
              new String[] {
                "http://jabber.org/protocol/si",
                "http://jabber.org/protocol/si/profile/file-transfer"
              })) {

            int priority =
                (presence.getPriority() == Integer.MIN_VALUE) ? 0 : presence.getPriority();

            if (priority > bestPriority) {
              bestPriority = priority;
              fullJid = presence.getFrom();
              jabberStatus =
                  OperationSetPersistentPresenceJabberImpl.jabberStatusToPresenceStatus(
                      presence, jabberProvider);
            } else if (priority == bestPriority && jabberStatus != null) {
              PresenceStatus tempStatus =
                  OperationSetPersistentPresenceJabberImpl.jabberStatusToPresenceStatus(
                      presence, jabberProvider);
              if (tempStatus.compareTo(jabberStatus) > 0) {
                fullJid = presence.getFrom();
                jabberStatus = tempStatus;
              }
            }
          }
        }
      }

      // First we check if file transfer is at all supported for this
      // contact.
      if (fullJid == null) {
        throw new OperationNotSupportedException(
            "Contact client or server does not support file transfers.");
      }

      if (gw != null && !fullJid.contains("@") && !fullJid.endsWith(gw)) {
        fullJid = fullJid + "@" + gw;
      }

      OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(fullJid);

      outgoingTransfer =
          new OutgoingFileTransferJabberImpl(toContact, file, transfer, jabberProvider);

      // Notify all interested listeners that a file transfer has been
      // created.
      FileTransferCreatedEvent event = new FileTransferCreatedEvent(outgoingTransfer, new Date());

      fireFileTransferCreated(event);

      // Send the file through the Jabber file transfer.
      transfer.sendFile(file, "Sending file");

      // Start the status and progress thread.
      new FileTransferProgressThread(transfer, outgoingTransfer).start();
    } catch (XMPPException e) {
      logger.error("Failed to send file.", e);
    }

    return outgoingTransfer;
  }
  /**
   * Handles presence stanzas
   *
   * @param presence
   */
  private void handlePresence(Presence presence) {
    // unavailable is sent when user leaves the room
    if (!presence.isAvailable()) {
      return;
    }

    String from = presence.getFrom();
    JitsiMeetConference conference = getConferenceForMucJid(from);

    if (conference == null) {
      if (logger.isDebugEnabled()) {
        logger.debug("Room not found for JID: " + from);
      }
      return;
    }

    ChatRoomMemberRole role = conference.getRoleForMucJid(from);

    if (role != null && role.compareTo(ChatRoomMemberRole.MODERATOR) < 0) {
      StartMutedPacketExtension ext =
          (StartMutedPacketExtension)
              presence.getExtension(
                  StartMutedPacketExtension.ELEMENT_NAME, StartMutedPacketExtension.NAMESPACE);

      if (ext != null) {
        boolean[] startMuted = {ext.getAudioMuted(), ext.getVideoMuted()};

        conference.setStartMuted(startMuted);
      }
    }

    Participant participant = conference.findParticipantForRoomJid(from);
    ColibriConference colibriConference = conference.getColibriConference();

    if (participant != null && colibriConference != null) {
      // Check if this conference is valid
      String conferenceId = colibriConference.getConferenceId();
      if (StringUtils.isNullOrEmpty(conferenceId)) {
        logger.error("Unable to send DisplayNameChanged event" + " - no conference id");
        return;
      }

      // Check for changes to the display name
      String oldDisplayName = participant.getDisplayName();
      String newDisplayName = null;
      for (PacketExtension pe : presence.getExtensions()) {
        if (pe instanceof Nick) {
          newDisplayName = ((Nick) pe).getName();
          break;
        }
      }

      if (!Objects.equals(oldDisplayName, newDisplayName)) {
        participant.setDisplayName(newDisplayName);

        EventAdmin eventAdmin = FocusBundleActivator.getEventAdmin();
        if (eventAdmin != null) {
          // Prevent NPE when adding to event hashtable
          if (newDisplayName == null) {
            newDisplayName = "";
          }
          eventAdmin.sendEvent(
              EventFactory.endpointDisplayNameChanged(
                  conferenceId, participant.getEndpointId(), newDisplayName));
        }
      }
    }
  }