public void setAuthenticationOutcome(String realm, URL tracker, boolean success) {
    try {
      this_mon.enter();

      setAuthenticationOutcome(
          realm, tracker.getProtocol(), tracker.getHost(), tracker.getPort(), success);

    } finally {

      this_mon.exit();
    }
  }
  public PasswordAuthentication getAuthentication(String realm, URL tracker) {
    try {
      this_mon.enter();

      return (getAuthentication(
          realm, tracker.getProtocol(), tracker.getHost(), tracker.getPort()));

    } finally {

      this_mon.exit();
    }
  }
  public void clearPasswords() {
    try {
      this_mon.enter();

      auth_cache = new HashMap();

      saveAuthCache();

    } finally {

      this_mon.exit();
    }
  }
  public static ConfigurationManager getInstance(Map data) {
    try {
      class_mon.enter();

      if (config == null) {

        config = new ConfigurationManager(data);
      }

      return config;
    } finally {

      class_mon.exit();
    }
  }
  public void setAuthenticationOutcome(
      String realm, String protocol, String host, int port, boolean success) {
    try {
      this_mon.enter();

      String tracker = protocol + "://" + host + ":" + port + "/";

      String auth_key = realm + ":" + tracker;

      authCache cache = (authCache) auth_cache.get(auth_key);

      if (cache != null) {

        cache.setOutcome(success);
      }
    } finally {

      this_mon.exit();
    }
  }
  public static ConfigurationManager getInstance() {
    try {
      class_mon.enter();

      if (config == null) {

        // this is nasty but I can't see an easy way around it. Unfortunately while reading the
        // config
        // we hit other code (logging for example) that needs access to the config data. Things are
        // cunningly (?) arranged so that a recursive call here *won't* result in a further
        // (looping)
        // recursive call if we attempt to load the config again. Hence this disgusting code that
        // goes for a second load attempt

        if (config_temp == null) {

          config_temp = new ConfigurationManager();

          config_temp.load();

          config_temp.initialise();

          config = config_temp;

        } else {

          if (config_temp.propertiesMap == null) {

            config_temp.load();
          }

          return (config_temp);
        }
      }

      return config;

    } finally {
      class_mon.exit();
    }
  }
  protected void saveAuthCache() {
    try {
      this_mon.enter();

      HashMap map = new HashMap();

      Iterator it = auth_cache.values().iterator();

      while (it.hasNext()) {

        authCache value = (authCache) it.next();

        if (value.isPersistent()) {

          try {
            HashMap entry_map = new HashMap();

            entry_map.put("user", value.getAuth().getUserName().getBytes("UTF-8"));
            entry_map.put("pw", new String(value.getAuth().getPassword()).getBytes("UTF-8"));

            map.put(value.getKey(), entry_map);

          } catch (Throwable e) {

            Debug.printStackTrace(e);
          }
        }
      }

      COConfigurationManager.setParameter(CONFIG_PARAM, map);

    } finally {

      this_mon.exit();
    }
  }
  public PasswordAuthentication getAuthentication(
      String realm, String protocol, String host, int port) {
    try {
      this_mon.enter();

      String tracker = protocol + "://" + host + ":" + port + "/";

      InetAddress bind_ip = NetworkAdmin.getSingleton().getSingleHomedServiceBindAddress();

      String self_addr;

      // System.out.println( "auth req for " + realm + " - " + tracker );

      if (bind_ip == null || bind_ip.isAnyLocalAddress()) {

        self_addr = "127.0.0.1";

      } else {

        self_addr = bind_ip.getHostAddress();
      }

      // when the tracker is connected to internally we don't want to prompt
      // for the password. Here we return a special user and the password hash
      // which is picked up in the tracker auth code - search for "<internal>"!

      // also include the tracker IP as well as for scrapes these can occur on
      // a raw torrent which hasn't been modified to point to localhost

      if (host.equals(self_addr)
          || host.equals(COConfigurationManager.getStringParameter("Tracker IP", ""))) {

        try {
          byte[] pw = COConfigurationManager.getByteParameter("Tracker Password", new byte[0]);

          String str_pw = new String(Base64.encode(pw));

          return (new PasswordAuthentication("<internal>", str_pw.toCharArray()));

        } catch (Throwable e) {

          Debug.printStackTrace(e);
        }
      }

      String auth_key = realm + ":" + tracker;

      authCache cache = (authCache) auth_cache.get(auth_key);

      if (cache != null) {

        PasswordAuthentication auth = cache.getAuth();

        if (auth != null) {

          return (auth);
        }
      }

      String[] res = getAuthenticationDialog(realm, tracker);

      if (res == null) {

        return (null);

      } else {

        PasswordAuthentication auth = new PasswordAuthentication(res[0], res[1].toCharArray());

        boolean save_pw = res[2].equals("true");

        boolean old_entry_existed =
            auth_cache.put(auth_key, new authCache(auth_key, auth, save_pw)) != null;

        if (save_pw || old_entry_existed) {

          saveAuthCache();
        }

        return (auth);
      }
    } finally {

      this_mon.exit();
    }
  }