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));
    }
  }
  private Map executeHTTP(Map data_to_send, boolean v6) throws Exception {

    if (v6 && !enable_v6) {

      throw (new Exception("IPv6 is disabled"));
    }

    String host = getHost(v6, HTTP_SERVER_ADDRESS_V6, HTTP_SERVER_ADDRESS_V4);

    if (Logger.isEnabled())
      Logger.log(
          new LogEvent(
              LOGID,
              "VersionCheckClient retrieving "
                  + "version information from "
                  + host
                  + ":"
                  + HTTP_SERVER_PORT
                  + " via HTTP"));

    String url_str =
        "http://"
            + (v6 ? UrlUtils.convertIPV6Host(host) : host)
            + (HTTP_SERVER_PORT == 80 ? "" : (":" + HTTP_SERVER_PORT))
            + "/version?";

    url_str +=
        URLEncoder.encode(new String(BEncoder.encode(data_to_send), "ISO-8859-1"), "ISO-8859-1");

    URL url = new URL(url_str);

    HttpURLConnection url_connection = (HttpURLConnection) url.openConnection();

    url_connection.connect();

    try {
      InputStream is = url_connection.getInputStream();

      Map reply = BDecoder.decode(new BufferedInputStream(is));

      preProcessReply(reply, v6);

      return (reply);

    } finally {

      url_connection.disconnect();
    }
  }
  public void dumpConfigChanges(IndentWriter writer) {
    ConfigurationDefaults defaults = ConfigurationDefaults.getInstance();

    Set<String> keys =
        new TreeSet<String>(
            new Comparator<String>() {
              public int compare(String o1, String o2) {
                return (o1.compareToIgnoreCase(o2));
              }
            });

    keys.addAll(propertiesMap.keySet());

    Iterator<String> it = keys.iterator();

    while (it.hasNext()) {

      String key = it.next();

      // don't dump crypto stuff

      if (ignoreKeyForDump(key)) {

        continue;
      }

      Object value = propertiesMap.get(key);

      boolean bParamExists = defaults.doesParameterDefaultExist(key.toString());

      if (bParamExists) {

        Object def = defaults.getParameter(key);

        if (def != null && value != null) {

          if (!BEncoder.objectsAreIdentical(def, value)) {

            if (value instanceof Long) {

              writer.println(key + "=" + value);

            } else if (value instanceof List) {

              writer.println(
                  key + "=" + BDecoder.decodeStrings((List) BEncoder.clone(value)) + "[list]");

            } else if (value instanceof Map) {

              writer.println(
                  key + "=" + BDecoder.decodeStrings((Map) BEncoder.clone(value)) + "[map]");

            } else if (value instanceof byte[]) {

              byte[] b = (byte[]) value;

              boolean hex = false;

              for (int i = 0; i < b.length; i++) {

                char c = (char) b[i];

                if (!(Character.isLetterOrDigit(c)
                    || "\\ `¬\"£$%^&*()-_=+[{]};:'@#~,<.>/?'".indexOf(c) != -1)) {

                  hex = true;

                  break;
                }
              }
              writer.println(
                  key + "=" + (hex ? ByteFormatter.nicePrint(b) : bytesToString((byte[]) value)));

            } else {

              writer.println(key + "=" + value + "[unknown]");
            }
          }
        }
      }
    }
  }
  public void generate(IndentWriter writer) {
    writer.println("Configuration Details");

    try {
      writer.indent();

      writer.println(
          "version=" + Constants.AZUREUS_VERSION + ", subver=" + Constants.AZUREUS_SUBVER);

      writer.println("System Properties");

      try {
        writer.indent();

        Properties props = System.getProperties();

        Iterator it = new TreeSet(props.keySet()).iterator();

        while (it.hasNext()) {

          String key = (String) it.next();

          writer.println(key + "=" + props.get(key));
        }
      } finally {

        writer.exdent();
      }

      writer.println("Environment");

      try {
        writer.indent();

        Map<String, String> env = System.getenv();

        if (env == null) {

          writer.println("Not supported");

        } else {

          Iterator it = new TreeSet(env.keySet()).iterator();

          while (it.hasNext()) {

            String key = (String) it.next();

            writer.println(key + "=" + env.get(key));
          }
        }
      } finally {

        writer.exdent();
      }

      writer.println("Azureus Config");

      ConfigurationDefaults defaults = ConfigurationDefaults.getInstance();

      try {
        writer.indent();

        Set<String> keys =
            new TreeSet<String>(
                new Comparator<String>() {
                  public int compare(String o1, String o2) {
                    return (o1.compareToIgnoreCase(o2));
                  }
                });

        keys.addAll(propertiesMap.keySet());

        Iterator<String> it = keys.iterator();

        while (it.hasNext()) {

          String key = it.next();

          // don't dump crypto stuff

          if (ignoreKeyForDump(key)) {

            continue;
          }

          Object value = propertiesMap.get(key);

          boolean bParamExists = defaults.doesParameterDefaultExist(key.toString());

          if (!bParamExists) {

            key = "[NoDef] " + key;
          } else {

            Object def = defaults.getParameter(key);

            if (def != null && value != null) {

              if (!BEncoder.objectsAreIdentical(def, value)) {

                key = "-> " + key;
              }
            }
          }

          if (value instanceof Long) {

            writer.println(key + "=" + value);

          } else if (value instanceof List) {

            writer.println(
                key + "=" + BDecoder.decodeStrings((List) BEncoder.clone(value)) + "[list]");

          } else if (value instanceof Map) {

            writer.println(
                key + "=" + BDecoder.decodeStrings((Map) BEncoder.clone(value)) + "[map]");

          } else if (value instanceof byte[]) {

            byte[] b = (byte[]) value;

            boolean hex = false;

            for (int i = 0; i < b.length; i++) {

              char c = (char) b[i];

              if (!(Character.isLetterOrDigit(c)
                  || "\\ `¬\"£$%^&*()-_=+[{]};:'@#~,<.>/?'".indexOf(c) != -1)) {

                hex = true;

                break;
              }
            }
            writer.println(
                key + "=" + (hex ? ByteFormatter.nicePrint(b) : bytesToString((byte[]) value)));

          } else {

            writer.println(key + "=" + value + "[unknown]");
          }
        }
      } finally {

        writer.exdent();
      }
    } finally {

      writer.exdent();
    }
  }
  private Map executeTCP(Map data_to_send, InetAddress bind_ip, int bind_port, boolean v6)
      throws Exception {
    if (v6 && !enable_v6) {

      throw (new Exception("IPv6 is disabled"));
    }

    String host = getHost(v6, TCP_SERVER_ADDRESS_V6, TCP_SERVER_ADDRESS_V4);

    if (Logger.isEnabled())
      Logger.log(
          new LogEvent(
              LOGID,
              "VersionCheckClient retrieving "
                  + "version information from "
                  + host
                  + ":"
                  + TCP_SERVER_PORT
                  + " via TCP"));

    String get_str = getHTTPGetString(data_to_send, false, v6);

    Socket socket = null;

    try {
      socket = new Socket();

      if (bind_ip != null) {

        socket.bind(new InetSocketAddress(bind_ip, bind_port));

      } else if (bind_port != 0) {

        socket.bind(new InetSocketAddress(bind_port));
      }

      socket.setSoTimeout(10000);

      socket.connect(new InetSocketAddress(host, TCP_SERVER_PORT), 10000);

      OutputStream os = socket.getOutputStream();

      os.write(get_str.getBytes("ISO-8859-1"));

      os.flush();

      InputStream is = socket.getInputStream();

      byte[] buffer = new byte[1];

      String header = "";

      int content_length = -1;

      while (true) {

        int len = is.read(buffer);

        if (len <= 0) {

          break;
        }

        header += (char) buffer[0];

        if (header.endsWith("\r\n\r\n")) {

          header = header.toLowerCase(MessageText.LOCALE_ENGLISH);

          int pos = header.indexOf("content-length:");

          if (pos == -1) {

            throw (new IOException("content length missing"));
          }

          header = header.substring(pos + 15);

          pos = header.indexOf('\r');

          header = header.substring(0, pos).trim();

          content_length = Integer.parseInt(header);

          if (content_length > 10000) {

            throw (new IOException("content length too large"));
          }

          break;
        }

        if (header.length() > 2048) {

          throw (new IOException("header too large"));
        }
      }

      ByteArrayOutputStream baos = new ByteArrayOutputStream(content_length);

      buffer = new byte[content_length];

      while (content_length > 0) {

        int len = is.read(buffer);

        if (len <= 0) {

          break;
        }

        baos.write(buffer, 0, len);

        content_length -= len;
      }

      if (content_length != 0) {

        throw (new IOException("error reading reply"));
      }

      byte[] reply_bytes = baos.toByteArray();

      Map reply = BDecoder.decode(new BufferedInputStream(new ByteArrayInputStream(reply_bytes)));

      preProcessReply(reply, v6);

      return (reply);

    } finally {

      if (socket != null) {

        try {
          socket.close();

        } catch (Throwable e) {

        }
      }
    }
  }