/**
  * Build the URL of the Vuze XML over HTTP plugin listener from the user settings.
  *
  * @return The URL of the RPC API
  */
 private String buildWebUIUrl() {
   return (settings.getSsl() ? "https://" : "http://")
       + settings.getAddress()
       + ":"
       + settings.getPort()
       + RPC_URL;
 }
 /**
  * Build the URL of the Transmission web UI from the user settings.
  *
  * @return The URL of the RPC API
  */
 private String buildWebUIUrl() {
   String webuiroot = "";
   if (settings.getFolder() != null) {
     webuiroot = settings.getFolder();
   }
   return (settings.getSsl() ? "https://" : "http://")
       + settings.getAddress()
       + ":"
       + settings.getPort()
       + webuiroot
       + "/";
 }
  private ArrayList<Torrent> parseJsonRetrieveTorrents(JSONArray results) throws JSONException {
    ArrayList<Torrent> torrents = new ArrayList<Torrent>();

    if (results != null) {
      for (int i = 0; i < results.length(); i++) {

        JSONObject tor = results.getJSONObject(i);
        long done_bytes = tor.getLong("done_bytes");
        long total_bytes = tor.getLong("total_bytes");
        float percent = ((float) done_bytes / ((float) total_bytes + 1));

        // @formatter:off
        torrents.add(
            new Torrent(
                i,
                tor.getString("key"),
                tor.getString("name"),
                convertBitfluStatus(tor),
                "/" + settings.getOS().getPathSeperator(),
                tor.getInt("speed_download"),
                tor.getInt("speed_upload"),
                tor.getInt("active_clients"),
                tor.getInt(
                    "active_clients"), // Bitflu doesn't distinguish between seeders and leechers
                tor.getInt("clients"),
                tor.getInt("clients"), // Bitflu doesn't distinguish between seeders and leechers
                tor.getInt("eta"),
                done_bytes,
                tor.getLong("uploaded_bytes"),
                total_bytes,
                percent, // Percentage to [0..1]
                0f, // Not available
                null, // label
                null, // Not available
                null, // Not available
                null, // Not available
                settings.getType()));
        // @formatter:on
      }
    }
    // Return the list
    return torrents;
  }
 /**
  * Creates a standard Apache HttpClient that is thread safe, supports different SSL auth methods
  * and basic authentication
  *
  * @param settings The server settings to adhere
  * @return An HttpClient that should be stored locally and reused for every new request
  * @throws DaemonException Thrown when information (such as username/password) is missing
  */
 public static DefaultHttpClient createStandardHttpClient(
     DaemonSettings settings, boolean userBasicAuth) throws DaemonException {
   return createStandardHttpClient(
       userBasicAuth && settings.shouldUseAuthentication(),
       settings.getUsername(),
       settings.getPassword(),
       settings.getSslTrustAll(),
       settings.getSslTrustKey(),
       settings.getTimeoutInMilliseconds(),
       settings.getAddress(),
       settings.getPort());
 }
 @Override
 public Daemon getType() {
   return settings.getType();
 }
  @SuppressWarnings("unchecked")
  private List<Torrent> onTorrentsRetrieved(Object result) throws DaemonException {

    Map<String, Object> response = (Map<String, Object>) result;

    // We might have an empty list if no torrents are on the server
    if (response == null) {
      return new ArrayList<Torrent>();
    }

    DLog.d(
        LOG_NAME,
        response.toString().length() > 300
            ? response.toString().substring(0, 300)
                + "... ("
                + response.toString().length()
                + " chars)"
            : response.toString());

    List<Torrent> torrents = new ArrayList<Torrent>();

    // Parse torrent list from Vuze response, which is a map list of ENTRYs
    for (String key : response.keySet()) {

      /**
       * Every Vuze ENTRY is a map of key-value pairs with information, or a key-map pair with that
       * map being a mapping of key-value pairs with information VuzeXmlTorrentListResponse.txt in
       * the Transdroid wiki shows a full example response, but it looks something like: ENTRY0={
       * position=1, torrent_file=/home/erickok/.azureus/torrents/ubuntu.torrent,
       * name=ubuntu-9.04-desktop-i386.iso, torrent={ size=732909568, creation_date=1240473087 } }
       */
      Map<String, Object> info = (Map<String, Object>) response.get(key);
      if (info == null
          || !info.containsKey("_object_id")
          || ((Long) info.get("_object_id")) == null) {
        // No valid XML data object returned
        throw new DaemonException(
            DaemonException.ExceptionType.UnexpectedResponse,
            "Map of objects returned by Vuze, but these object do not have some <info> attached or no <_object_id> is available");
      }
      Map<String, Object> torrentinfo = (Map<String, Object>) info.get("torrent");
      Map<String, Object> statsinfo = (Map<String, Object>) info.get("stats");
      Map<String, Object> scrapeinfo = (Map<String, Object>) info.get("scrape_result");
      Map<String, Object> announceinfo = (Map<String, Object>) info.get("announce_result");
      int scrapeSeedCount = ((Long) scrapeinfo.get("seed_count")).intValue();
      int scrapeNonSeedCount = ((Long) scrapeinfo.get("non_seed_count")).intValue();
      String error = (String) info.get("error_state_details");
      error = error != null && error.equals("") ? null : error;
      int announceSeedCount = ((Long) announceinfo.get("seed_count")).intValue();
      int announceNonSeedCount = ((Long) announceinfo.get("non_seed_count")).intValue();
      int rateDownload = ((Long) statsinfo.get("download_average")).intValue();
      Double availability = (Double) statsinfo.get("availability");
      Long size = torrentinfo != null ? (Long) torrentinfo.get("size") : 0;

      torrents.add(
          new Torrent(
              (Long) info.get("_object_id"), // id
              ((Long) info.get("_object_id"))
                  .toString(), // hash	//(String) torrentinfo.get("hash"), // hash
              (String) info.get("name").toString().trim(), // name
              convertTorrentStatus((Long) info.get("state")), // status
              (String) statsinfo.get("target_file_or_dir") + "/", // locationDir
              rateDownload, // rateDownload
              ((Long) statsinfo.get("upload_average")).intValue(), // rateUpload
              announceSeedCount, // seedersConnected
              scrapeSeedCount, // seedersKnown
              announceNonSeedCount, // leechersConnected
              scrapeNonSeedCount, // leechersKnown
              (rateDownload > 0
                  ? (int) ((Long) statsinfo.get("remaining") / rateDownload)
                  : -1), // eta (bytes left / rate download, if rate > 0)
              (Long) statsinfo.get("downloaded"), // downloadedEver
              (Long) statsinfo.get("uploaded"), // uploadedEver
              size, // totalSize
              (float) ((Long) statsinfo.get("downloaded"))
                  / (float) (size), // partDone (downloadedEver / totalSize)
              Math.min(availability.floatValue(), 1f),
              null, // TODO: Implement Vuze label support
              new Date((Long) statsinfo.get("time_started")), // dateAdded
              null, // Unsupported?
              error,
              settings.getType()));
    }

    return torrents;
  }
  private synchronized Map<String, Object> makeVuzeCall(
      DaemonMethod method,
      String serverMethod,
      Long actOnObject,
      Object[] params,
      TorrentStatus torrentStatus)
      throws DaemonException {

    // TODO: It would be nicer to now split each of these steps into separate makeVuzeCalls when
    // there are multiple logical steps such as stopping a torrent before removing it

    // Initialise the HTTP client
    if (rpcclient == null) {
      initialise();
    }
    if (settings.getAddress() == null || settings.getAddress().equals("")) {
      throw new DaemonException(
          DaemonException.ExceptionType.AuthenticationFailure, "No host name specified.");
    }

    if (savedConnectionID == null || savedPluginID == null) {
      // Get plug-in interface (for connection and plug-in object IDs)
      Map<String, Object> plugin = rpcclient.callXMLRPC(null, "getSingleton", null, null, false);
      if (!plugin.containsKey("_connection_id")) {
        throw new DaemonException(
            ExceptionType.UnexpectedResponse, "No connection ID returned on getSingleton request.");
      }
      savedConnectionID = (Long) plugin.get("_connection_id");
      savedPluginID = (Long) plugin.get("_object_id");
    }

    // If no specific torrent was provided, get the download manager or plugin config to execute the
    // method against
    long vuzeObjectID;
    if (actOnObject == null) {
      if (method == DaemonMethod.SetTransferRates) {

        // Execute this method against the plugin config (setParameter)
        if (savedPluginConfigID == null) {
          // Plugin config needed, but we don't know it's ID yet
          Map<String, Object> config =
              rpcclient.callXMLRPC(
                  savedPluginID, "getPluginconfig", null, savedConnectionID, false);
          if (!config.containsKey("_object_id")) {
            throw new DaemonException(
                ExceptionType.UnexpectedResponse,
                "No plugin config ID returned on getPluginconfig");
          }
          savedPluginConfigID = (Long) config.get("_object_id");
          vuzeObjectID = savedPluginConfigID;
        } else {
          // We stored the plugin config ID, so no need to ask for it again
          vuzeObjectID = savedPluginConfigID;
        }

      } else if (serverMethod.equals("createFromBEncodedData[byte[]]")) {

        // Execute this method against the torrent manager (createFromBEncodedData)
        if (savedTorrentManagerID == null) {
          // Download manager needed, but we don't know it's ID yet
          Map<String, Object> manager =
              rpcclient.callXMLRPC(
                  savedPluginID, "getTorrentManager", null, savedConnectionID, false);
          if (!manager.containsKey("_object_id")) {
            throw new DaemonException(
                ExceptionType.UnexpectedResponse,
                "No torrent manager ID returned on getTorrentManager");
          }
          savedTorrentManagerID = (Long) manager.get("_object_id");
          vuzeObjectID = savedTorrentManagerID;
        } else {
          // We stored the torrent manager ID, so no need to ask for it again
          vuzeObjectID = savedTorrentManagerID;
        }
        // And we will need the download manager as well later on (for addDownload after
        // createFromBEncodedData)
        if (savedDownloadManagerID == null) {
          // Download manager needed, but we don't know it's ID yet
          Map<String, Object> manager =
              rpcclient.callXMLRPC(
                  savedPluginID, "getDownloadManager", null, savedConnectionID, false);
          if (!manager.containsKey("_object_id")) {
            throw new DaemonException(
                ExceptionType.UnexpectedResponse,
                "No download manager ID returned on getDownloadManager");
          }
          savedDownloadManagerID = (Long) manager.get("_object_id");
        }

      } else {

        // Execute this method against download manager (addDownload, startAllDownloads, etc.)
        if (savedDownloadManagerID == null) {
          // Download manager needed, but we don't know it's ID yet
          Map<String, Object> manager =
              rpcclient.callXMLRPC(
                  savedPluginID, "getDownloadManager", null, savedConnectionID, false);
          if (!manager.containsKey("_object_id")) {
            throw new DaemonException(
                ExceptionType.UnexpectedResponse,
                "No download manager ID returned on getDownloadManager");
          }
          savedDownloadManagerID = (Long) manager.get("_object_id");
          vuzeObjectID = savedDownloadManagerID;
        } else {
          // We stored the download manager ID, so no need to ask for it again
          vuzeObjectID = savedDownloadManagerID;
        }
      }
    } else {
      vuzeObjectID = actOnObject;
    }

    if (method == DaemonMethod.Remove
        && torrentStatus != null
        && torrentStatus != TorrentStatus.Paused) {
      // Vuze, for some strange reason, wants us to stop the torrent first before removing it
      rpcclient.callXMLRPC(vuzeObjectID, "stop", new Object[] {}, savedConnectionID, false);
    }

    boolean paramsAreVuzeObjects = false;
    if (serverMethod.equals("createFromBEncodedData[byte[]]")) {
      // Vuze does not directly add the torrent that we are uploading the file contents of
      // We first do the createFromBEncodedData call and next actually add it
      Map<String, Object> torrentData =
          rpcclient.callXMLRPC(vuzeObjectID, serverMethod, params, savedConnectionID, false);
      serverMethod = "addDownload[Torrent]";
      vuzeObjectID = savedDownloadManagerID;
      params = new String[] {((Long) torrentData.get("_object_id")).toString()};
      paramsAreVuzeObjects = true;
    }

    // Call the actual method we wanted
    return rpcclient.callXMLRPC(
        vuzeObjectID, serverMethod, params, savedConnectionID, paramsAreVuzeObjects);
  }