public Set<String> getAutoInstallPluginIDs() {
    Set<String> result = new HashSet<String>();

    Map m = getMostRecentVersionCheckData();

    if (m != null) {

      byte[] x = (byte[]) m.get("autoinstall_pids");

      if (x != null) {

        String str = new String(x);

        String latest = COConfigurationManager.getStringParameter("vc.autoinstall_pids.latest", "");

        if (!str.equals(latest)) {

          byte[] sig = (byte[]) m.get("autoinstall_pids_sig");

          if (sig == null) {

            Debug.out("autoinstall plugins sig missing");

            return (result);
          }

          try {
            AEVerifier.verifyData(str, sig);

            COConfigurationManager.setParameter("vc.autoinstall_pids.latest", str);

          } catch (Throwable e) {

            return (result);
          }
        }

        String[] bits = str.split(",");

        for (String b : bits) {

          b = b.trim();

          if (b.length() > 0) {

            result.add(b);
          }
        }
      }
    }

    return (result);
  }
  public InetAddress getExternalIpAddressUDP(InetAddress bind_ip, int bind_port, boolean v6)
      throws Exception {
    Map reply = executeUDP(new HashMap(), bind_ip, bind_port, v6);

    byte[] address = (byte[]) reply.get("source_ip_address");

    return (InetAddress.getByName(new String(address)));
  }
  public InetAddress getExternalIpAddressHTTP(boolean v6) throws Exception {

    Map reply = executeHTTP(new HashMap(), v6);

    byte[] address = (byte[]) reply.get("source_ip_address");

    return (InetAddress.getByName(new String(address)));
  }
  public Map<String, Object> getCountryInfo() {
    Map reply = getVersionCheckInfo(REASON_EXTERNAL_IP, AT_EITHER);

    Map<String, Object> info = (Map<String, Object>) reply.get("source_info");

    if (info == null) {

      return (new HashMap<String, Object>());

    } else {

      return (BDecoder.decodeStrings(info));
    }
  }
  /**
   * Is the DHT allowed to be used by external plugins.
   *
   * @return true if extended DHT use is allowed, false if not allowed
   */
  public boolean DHTExtendedUseAllowed() {
    Map reply = getVersionCheckInfo(REASON_DHT_EXTENDED_ALLOWED, AT_EITHER);

    boolean res = false;

    byte[] value = (byte[]) reply.get("enable_dht_extended_use");
    if (value != null) {
      res = new String(value).equalsIgnoreCase("true");
    }

    // be generous and enable extended use if check failed

    if (!res) {
      res = !isVersionCheckDataValid(AT_EITHER);
    }

    return res;
  }
  public String[] getRecommendedPlugins() {
    Map reply = getVersionCheckInfo(REASON_RECOMMENDED_PLUGINS, AT_EITHER);

    List l = (List) reply.get("recommended_plugins");

    if (l == null) {

      return (new String[0]);
    }

    String[] res = new String[l.size()];

    for (int i = 0; i < l.size(); i++) {

      res[i] = new String((byte[]) l.get(i));
    }

    return (res);
  }
  public long getFeatureFlags() {
    long now = SystemTime.getCurrentTime();

    if (now > last_feature_flag_cache_time && now - last_feature_flag_cache_time < 60000) {

      return (last_feature_flag_cache);
    }

    Map m = getMostRecentVersionCheckData();

    long result;

    if (m == null) {

      result = 0;

    } else {

      byte[] b_feat_flags = (byte[]) m.get("feat_flags");

      if (b_feat_flags != null) {

        try {

          result = Long.parseLong(new String((byte[]) b_feat_flags));

        } catch (Throwable e) {

          result = 0;
        }
      } else {

        result = 0;
      }
    }

    last_feature_flag_cache = result;
    last_feature_flag_cache_time = now;

    return (result);
  }
  /**
   * Is the DHT plugin allowed to be enabled.
   *
   * @return true if DHT can be enabled, false if it should not be enabled
   */
  public boolean DHTEnableAllowed() {
    Map reply = getVersionCheckInfo(REASON_DHT_ENABLE_ALLOWED, AT_EITHER);

    boolean res = false;

    byte[] value = (byte[]) reply.get("enable_dht");

    if (value != null) {

      res = new String(value).equalsIgnoreCase("true");
    }

    // we take the view that if the version check failed then we go ahead
    // and enable the DHT (i.e. we're being optimistic)

    if (!res) {
      res = !isVersionCheckDataValid(AT_EITHER);
    }

    return res;
  }
  // Used for debugging in main.
  private static void printDataMap(Map map) throws Exception {
    TreeMap res = new TreeMap(map);
    Iterator key_itr = map.keySet().iterator();
    while (key_itr.hasNext()) {
      Object key = key_itr.next();
      Object val = map.get(key);
      if (val instanceof byte[]) {
        String as_bytes = ByteFormatter.nicePrint((byte[]) val);
        String as_text = new String((byte[]) val, Constants.BYTE_ENCODING);
        res.put(key, as_text + " [" + as_bytes + "]");
      }
    }

    Iterator entries = res.entrySet().iterator();
    Map.Entry entry;
    while (entries.hasNext()) {
      entry = (Map.Entry) entries.next();
      System.out.print("  ");
      System.out.print(entry.getKey());
      System.out.print(": ");
      System.out.print(entry.getValue());
      System.out.println();
    }
  }
  protected void preProcessReply(Map reply, final boolean v6) {
    NetworkAdmin admin = NetworkAdmin.getSingleton();

    try {
      byte[] address = (byte[]) reply.get("source_ip_address");

      InetAddress my_ip = InetAddress.getByName(new String(address));

      NetworkAdminASN old_asn = admin.getCurrentASN();

      NetworkAdminASN new_asn = admin.lookupCurrentASN(my_ip);

      if (!new_asn.sameAs(old_asn)) {

        // kick off a secondary version check to communicate the new information

        if (!secondary_check_done) {

          secondary_check_done = true;

          new AEThread("Secondary version check", true) {
            public void runSupport() {
              getVersionCheckInfoSupport(REASON_SECONDARY_CHECK, false, true, v6);
            }
          }.start();
        }
      }
    } catch (Throwable e) {

      Debug.printStackTrace(e);
    }

    Long as_advice = (Long) reply.get("as_advice");

    if (as_advice != null) {

      NetworkAdminASN current_asn = admin.getCurrentASN();

      String asn = current_asn.getASName();

      if (asn != null) {

        long advice = as_advice.longValue();

        if (advice != 0) {

          // require crypto

          String done_asn = COConfigurationManager.getStringParameter("ASN Advice Followed", "");

          if (!done_asn.equals(asn)) {

            COConfigurationManager.setParameter("ASN Advice Followed", asn);

            boolean change = advice == 1 || advice == 2;
            boolean alert = advice == 1 || advice == 3;

            if (!COConfigurationManager.getBooleanParameter(
                "network.transport.encrypted.require")) {

              if (change) {

                COConfigurationManager.setParameter("network.transport.encrypted.require", true);
              }

              if (alert) {

                String msg = MessageText.getString("crypto.alert.as.warning", new String[] {asn});

                Logger.log(new LogAlert(false, LogAlert.AT_WARNING, msg));
              }
            }
          }
        }
      }
    }

    // set ui.toolbar.uiswitcher based on instructions from tracker
    // Really shouldn't be in VersionCheck client, but instead have some
    // listener and have the code elsewhere.  Simply calling
    // getVersionCheckInfo from "code elsewhere" (to get the cached result)
    // caused a deadlock at startup.
    Long lEnabledUISwitcher = (Long) reply.get("ui.toolbar.uiswitcher");
    if (lEnabledUISwitcher != null) {
      COConfigurationManager.setBooleanDefault(
          "ui.toolbar.uiswitcher", lEnabledUISwitcher.longValue() == 1);
    }
  }