/**
   * Perform the actual version check by connecting to the version server.
   *
   * @param data_to_send version message
   * @return version reply
   * @throws Exception if the server check connection fails
   */
  private Map performVersionCheck(
      Map data_to_send, boolean use_az_message, boolean use_http, boolean v6) throws Exception {
    Exception error = null;
    Map reply = null;

    if (use_http) {

      try {
        reply = executeHTTP(data_to_send, v6);

        reply.put("protocol_used", "HTTP");

        error = null;
      } catch (IOException e) {
        error = e;
      } catch (Exception e) {
        Debug.printStackTrace(e);
        error = e;
      }
    }

    if (reply == null && use_az_message) {

      try {
        reply = executeAZMessage(data_to_send, v6);

        reply.put("protocol_used", "AZMSG");
      } catch (IOException e) {
        error = e;
      } catch (Exception e) {
        Debug.printStackTrace(e);
        error = e;
      }
    }
    if (error != null) {

      throw (error);
    }

    if (Logger.isEnabled())
      Logger.log(
          new LogEvent(
              LOGID,
              "VersionCheckClient server "
                  + "version check successful. Received "
                  + reply.size()
                  + " reply keys."));

    if (v6) {

      last_check_time_v6 = SystemTime.getCurrentTime();

    } else {

      last_check_time_v4 = SystemTime.getCurrentTime();
    }

    return reply;
  }
  private boolean isVersionCheckDataValid(int address_type) {
    boolean v6_ok = last_check_data_v6 != null && last_check_data_v6.size() > 0;
    boolean v4_ok = last_check_data_v4 != null && last_check_data_v4.size() > 0;

    if (address_type == AT_V4) {

      return (v4_ok);

    } else if (address_type == AT_V6) {

      return (v6_ok);

    } else {

      return (v4_ok | v6_ok);
    }
  }
  public Map getVersionCheckInfo(String reason, int address_type) {
    if (address_type == AT_V4) {

      return (getVersionCheckInfoSupport(reason, false, false, false));

    } else if (address_type == AT_V6) {

      return (getVersionCheckInfoSupport(reason, false, false, true));

    } else {

      Map reply = getVersionCheckInfoSupport(reason, false, false, prefer_v6);

      if (reply == null || reply.size() == 0) {

        reply = getVersionCheckInfoSupport(reason, false, false, !prefer_v6);
      }

      return (reply);
    }
  }
  protected Object[] handleAnnounceAndScrape(
      String client_ip_address, PRUDPPacketRequest request, int request_type) throws Exception {
    if (!checkConnectionId(client_ip_address, request.getConnectionId())) {

      return (null);
    }

    List hashbytes = new ArrayList();
    HashWrapper peer_id = null;
    int port = 0;
    String event = null;

    long uploaded = 0;
    long downloaded = 0;
    long left = 0;
    int num_want = -1;

    String key = null;

    if (request_type == TRTrackerServerRequest.RT_ANNOUNCE) {

      if (PRUDPPacketTracker.VERSION == 1) {
        PRUDPPacketRequestAnnounce announce = (PRUDPPacketRequestAnnounce) request;

        hashbytes.add(announce.getHash());

        peer_id = new HashWrapper(announce.getPeerId());

        port = announce.getPort();

        int i_event = announce.getEvent();

        switch (i_event) {
          case PRUDPPacketRequestAnnounce.EV_STARTED:
            {
              event = "started";
              break;
            }
          case PRUDPPacketRequestAnnounce.EV_STOPPED:
            {
              event = "stopped";
              break;
            }
          case PRUDPPacketRequestAnnounce.EV_COMPLETED:
            {
              event = "completed";
              break;
            }
        }

        uploaded = announce.getUploaded();

        downloaded = announce.getDownloaded();

        left = announce.getLeft();

        num_want = announce.getNumWant();

        int i_ip = announce.getIPAddress();

        if (i_ip != 0) {

          client_ip_address = PRHelpers.intToAddress(i_ip);
        }
      } else {

        PRUDPPacketRequestAnnounce2 announce = (PRUDPPacketRequestAnnounce2) request;

        hashbytes.add(announce.getHash());

        peer_id = new HashWrapper(announce.getPeerId());

        port = announce.getPort();

        int i_event = announce.getEvent();

        switch (i_event) {
          case PRUDPPacketRequestAnnounce.EV_STARTED:
            {
              event = "started";
              break;
            }
          case PRUDPPacketRequestAnnounce.EV_STOPPED:
            {
              event = "stopped";
              break;
            }
          case PRUDPPacketRequestAnnounce.EV_COMPLETED:
            {
              event = "completed";
              break;
            }
        }

        uploaded = announce.getUploaded();

        downloaded = announce.getDownloaded();

        left = announce.getLeft();

        num_want = announce.getNumWant();

        int i_ip = announce.getIPAddress();

        if (i_ip != 0) {

          client_ip_address = PRHelpers.intToAddress(i_ip);
        }

        key = "" + announce.getKey();
      }
    } else {

      PRUDPPacketRequestScrape scrape = (PRUDPPacketRequestScrape) request;

      hashbytes.addAll(scrape.getHashes());
    }

    Map[] root_out = new Map[1];
    TRTrackerServerPeerImpl[] peer_out = new TRTrackerServerPeerImpl[1];

    TRTrackerServerTorrentImpl torrent =
        processTrackerRequest(
            server,
            "",
            root_out,
            peer_out,
            request_type,
            (byte[][]) hashbytes.toArray(new byte[0][0]),
            null,
            null,
            peer_id,
            false,
            TRTrackerServerTorrentImpl.COMPACT_MODE_NONE,
            key, // currently no "no_peer_id" / "compact" in the packet and anyway they aren't
                 // returned / key
            event,
            false,
            port,
            0,
            0,
            client_ip_address,
            client_ip_address,
            downloaded,
            uploaded,
            left,
            num_want,
            TRTrackerServerPeer.CRYPTO_NONE,
            (byte) 1,
            0,
            null);

    Map root = root_out[0];

    if (request_type == TRTrackerServerRequest.RT_ANNOUNCE) {

      if (PRUDPPacketTracker.VERSION == 1) {
        PRUDPPacketReplyAnnounce reply = new PRUDPPacketReplyAnnounce(request.getTransactionId());

        reply.setInterval(((Long) root.get("interval")).intValue());

        List peers = (List) root.get("peers");

        int[] addresses = new int[peers.size()];
        short[] ports = new short[addresses.length];

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

          Map peer = (Map) peers.get(i);

          addresses[i] = PRHelpers.addressToInt(new String((byte[]) peer.get("ip")));

          ports[i] = (short) ((Long) peer.get("port")).shortValue();
        }

        reply.setPeers(addresses, ports);

        return (new Object[] {reply, torrent});

      } else {

        PRUDPPacketReplyAnnounce2 reply = new PRUDPPacketReplyAnnounce2(request.getTransactionId());

        reply.setInterval(((Long) root.get("interval")).intValue());

        boolean local_scrape = client_ip_address.equals("127.0.0.1");

        Map scrape_details = torrent.exportScrapeToMap("", client_ip_address, !local_scrape);

        int seeders = ((Long) scrape_details.get("complete")).intValue();
        int leechers = ((Long) scrape_details.get("incomplete")).intValue();

        reply.setLeechersSeeders(leechers, seeders);

        List peers = (List) root.get("peers");

        int[] addresses = new int[peers.size()];
        short[] ports = new short[addresses.length];

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

          Map peer = (Map) peers.get(i);

          addresses[i] = PRHelpers.addressToInt(new String((byte[]) peer.get("ip")));

          ports[i] = (short) ((Long) peer.get("port")).shortValue();
        }

        reply.setPeers(addresses, ports);

        return (new Object[] {reply, torrent});
      }

    } else {

      if (PRUDPPacketTracker.VERSION == 1) {

        PRUDPPacketReplyScrape reply = new PRUDPPacketReplyScrape(request.getTransactionId());

        /*
        Long	interval = (Long)root.get("interval");

        if ( interval != null ){

        	reply.setInterval(interval.intValue());
        }
        */

        Map files = (Map) root.get("files");

        byte[][] hashes = new byte[files.size()][];
        int[] s_complete = new int[hashes.length];
        int[] s_downloaded = new int[hashes.length];
        int[] s_incomplete = new int[hashes.length];

        Iterator it = files.keySet().iterator();

        int pos = 0;

        while (it.hasNext()) {

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

          hashes[pos] = hash_str.getBytes(Constants.BYTE_ENCODING);

          Map details = (Map) files.get(hash_str);

          s_complete[pos] = ((Long) details.get("complete")).intValue();
          s_incomplete[pos] = ((Long) details.get("incomplete")).intValue();
          s_downloaded[pos] = ((Long) details.get("downloaded")).intValue();

          pos++;
        }

        reply.setDetails(hashes, s_complete, s_downloaded, s_incomplete);

        return (new Object[] {reply, torrent});

      } else {

        PRUDPPacketReplyScrape2 reply = new PRUDPPacketReplyScrape2(request.getTransactionId());

        /*
        Long	interval = (Long)root.get("interval");

        if ( interval != null ){

        	reply.setInterval(interval.intValue());
        }
        */

        Map files = (Map) root.get("files");

        int[] s_complete = new int[files.size()];
        int[] s_downloaded = new int[s_complete.length];
        int[] s_incomplete = new int[s_complete.length];

        Iterator it = files.keySet().iterator();

        int pos = 0;

        while (it.hasNext()) {

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

          Map details = (Map) files.get(hash_str);

          s_complete[pos] = ((Long) details.get("complete")).intValue();
          s_incomplete[pos] = ((Long) details.get("incomplete")).intValue();
          s_downloaded[pos] = ((Long) details.get("downloaded")).intValue();

          pos++;
        }

        reply.setDetails(s_complete, s_downloaded, s_incomplete);

        return (new Object[] {reply, torrent});
      }
    }
  }
  protected Map getVersionCheckInfoSupport(
      String reason, boolean only_if_cached, boolean force, boolean v6) {
    try {
      synchronized (listeners) {
        if (REASON_UPDATE_CHECK_START.equals(reason)) {
          startCheckRan = true;
        }
        for (VersionCheckClientListener l : listeners) {
          l.versionCheckStarted(reason);
        }
      }
    } catch (Throwable t) {
      Debug.out(t);
    }
    if (v6) {

      if (enable_v6) {

        try {
          check_mon.enter();

          long time_diff = SystemTime.getCurrentTime() - last_check_time_v6;

          force = force || time_diff > CACHE_PERIOD || time_diff < 0;

          if (last_check_data_v6 == null || last_check_data_v6.size() == 0 || force) {
            // if we've never checked before then we go ahead even if the "only_if_cached"
            // flag is set as its had not chance of being cached yet!
            if (only_if_cached && last_check_data_v6 != null) {
              return (new HashMap());
            }
            try {
              last_check_data_v6 =
                  performVersionCheck(constructVersionCheckMessage(reason), true, true, true);

              if (last_check_data_v6 != null && last_check_data_v6.size() > 0) {

                COConfigurationManager.setParameter("versioncheck.cache.v6", last_check_data_v6);
              }
            } catch (SocketException t) {
              // internet is broken
              // Debug.out(t.getClass().getName() + ": " + t.getMessage());
            } catch (UnknownHostException t) {
              // dns is broken
              // Debug.out(t.getClass().getName() + ": " + t.getMessage());
            } catch (Throwable t) {
              Debug.out(t);
              last_check_data_v6 = new HashMap();
            }
          } else {
            Logger.log(
                new LogEvent(
                    LOGID,
                    "VersionCheckClient is using "
                        + "cached version check info. Using "
                        + last_check_data_v6.size()
                        + " reply keys."));
          }
        } finally {
          check_mon.exit();
        }
      }

      if (last_check_data_v6 == null) last_check_data_v6 = new HashMap();

      return last_check_data_v6;

    } else {

      try {
        check_mon.enter();

        long time_diff = SystemTime.getCurrentTime() - last_check_time_v4;

        force = force || time_diff > CACHE_PERIOD || time_diff < 0;

        if (last_check_data_v4 == null || last_check_data_v4.size() == 0 || force) {
          // if we've never checked before then we go ahead even if the "only_if_cached"
          // flag is set as its had not chance of being cached yet!
          if (only_if_cached && last_check_data_v4 != null) {
            return (new HashMap());
          }
          try {
            last_check_data_v4 =
                performVersionCheck(constructVersionCheckMessage(reason), true, true, false);

            if (last_check_data_v4 != null && last_check_data_v4.size() > 0) {

              COConfigurationManager.setParameter("versioncheck.cache.v4", last_check_data_v4);
            }

            // clear down any plugin-specific data that has successfully been sent to the version
            // server

            try {
              if (AzureusCoreFactory.isCoreAvailable()) {

                // installed plugin IDs
                PluginInterface[] plugins =
                    AzureusCoreFactory.getSingleton().getPluginManager().getPluginInterfaces();

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

                  PluginInterface plugin = plugins[i];

                  Map data =
                      plugin
                          .getPluginconfig()
                          .getPluginMapParameter("plugin.versionserver.data", null);

                  if (data != null) {

                    plugin
                        .getPluginconfig()
                        .setPluginMapParameter("plugin.versionserver.data", new HashMap());
                  }
                }
              }
            } catch (Throwable e) {
            }
          } catch (UnknownHostException t) {
            // no internet
            Debug.outNoStack(
                "VersionCheckClient - " + t.getClass().getName() + ": " + t.getMessage());
          } catch (IOException t) {
            // General connection problem.
            Debug.outNoStack(
                "VersionCheckClient - " + t.getClass().getName() + ": " + t.getMessage());
          } catch (Throwable t) {
            Debug.out(t);
            last_check_data_v4 = new HashMap();
          }
        } else {
          if (Logger.isEnabled())
            Logger.log(
                new LogEvent(
                    LOGID,
                    "VersionCheckClient is using "
                        + "cached version check info. Using "
                        + last_check_data_v4.size()
                        + " reply keys."));
        }
      } finally {
        check_mon.exit();
      }

      if (last_check_data_v4 == null) last_check_data_v4 = new HashMap();

      last_feature_flag_cache_time = 0;

      return last_check_data_v4;
    }
  }