/** @param requiresAuthorization */
  protected static void processQueue(String queueID, Map mapQueue) {
    if (!initialized) {
      init();
    }

    final Map mapProcessing = new HashMap();

    boolean sendAZID = true;
    long contentNetworkID = ContentNetwork.CONTENT_NETWORK_VUZE;

    // Create urlStem (or post data)
    boolean isMulti = false;
    StringBuffer urlStem = new StringBuffer();
    long sequenceNo = 0;

    Map<String, Object> mapPayload = new HashMap<String, Object>();
    mapPayload.put("azid", ConstantsVuze.AZID);
    mapPayload.put("azv", Constants.AZUREUS_VERSION);
    // mapPayload.put("mode", FeatureManagerUI.getMode());
    mapPayload.putAll(mapExtra);
    List<Map> listCommands = new ArrayList<Map>();
    mapPayload.put("commands", listCommands);

    queue_mon.enter();
    try {
      String lastServer = null;
      // add one at a time, ensure relay server messages are seperate
      boolean first = true;
      for (Iterator iter = mapQueue.keySet().iterator(); iter.hasNext(); ) {
        PlatformMessage message = (PlatformMessage) iter.next();
        Object value = mapQueue.get(message);

        Map<String, Object> mapCmd = new HashMap<String, Object>();

        if (first) {
          sendAZID = message.sendAZID();
          contentNetworkID = message.getContentNetworkID();
          first = false;
        }

        // build urlStem
        message.setSequenceNo(sequenceNo);

        if (urlStem.length() > 0) {
          urlStem.append('&');
        }

        String listenerID = message.getListenerID();
        String messageID = message.getMessageID();
        Map params = message.getParameters();
        try {
          urlStem.append("msg=");
          urlStem.append(URLEncoder.encode(listenerID, "UTF-8"));
          urlStem.append(":");
          urlStem.append(URLEncoder.encode(message.getOperationID(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
        }

        mapCmd.put("seq-id", sequenceNo);
        mapCmd.put("listener-id", listenerID);
        mapCmd.put("op-id", message.getOperationID());
        if (params != null) {
          mapCmd.put("values", params);
        }
        listCommands.add(mapCmd);

        // We used to check on MAX_POST_LENGTH, but with the changes that
        // would require converting the map to JSON on every iteration to get
        // the length.  For now, just limit to 10
        if (sequenceNo > 10) {
          debug("breaking up batch at " + sequenceNo + " because max limit would be exceeded");
          break;
        }

        String curServer = messageID + "-" + listenerID;
        if (lastServer != null && !lastServer.equals(curServer)) {
          isMulti = true;
        }
        lastServer = curServer;

        PlatformMessengerListener listener = (PlatformMessengerListener) mapProcessing.get(message);
        if (listener != null) {
          listener.messageSent(message);
        }
        sequenceNo++;

        // Adjust lists
        mapProcessing.put(message, value);

        iter.remove();

        if (!getAllowMulti()) {
          break;
        }
      }
    } finally {
      queue_mon.exit();
    }
    // debug("about to process " + mapProcessing.size());

    if (mapProcessing.size() == 0) {
      return;
    }

    // Build base RPC url based on listener and server

    // one day all this URL hacking should be moved into the ContentNetwork...

    ContentNetwork cn =
        ContentNetworkManagerFactory.getSingleton().getContentNetwork(contentNetworkID);
    if (cn == null) {
      cn = ConstantsVuze.getDefaultContentNetwork();
    }

    String sURL_RPC =
        ContentNetworkUtils.getUrl(cn, ContentNetwork.SERVICE_RPC) + "&" + urlStem.toString();

    // Build full url and data to send
    String sURL;
    String sPostData = null;
    String sJSONPayload = UrlUtils.encode(JSONUtils.encodeToJSON(mapPayload));
    if (USE_HTTP_POST) {
      sURL = sURL_RPC;

      sPostData = URL_POST_PLATFORM_DATA + "&payload=" + sJSONPayload;
      sPostData = cn.appendURLSuffix(sPostData, true, sendAZID);

      if (DEBUG_URL) {
        debug("POST for " + mapProcessing.size() + ": " + sURL + "\n   DATA: " + sPostData);
      } else {
        debug("POST for " + mapProcessing.size() + ": " + sURL);
      }
    } else {
      sURL = sURL_RPC + URL_PLATFORM_MESSAGE + "&payload=" + sJSONPayload;

      sURL = cn.appendURLSuffix(sURL, false, sendAZID);

      if (DEBUG_URL) {
        debug("GET: " + sURL);
      } else {
        debug("GET: " + sURL_RPC + URL_PLATFORM_MESSAGE);
      }
    }

    final String fURL = sURL;
    final String fPostData = sPostData;

    // one at a time to take advantage of keep-alive connections

    dispatcher.dispatch(
        new AERunnable() {
          public void runSupport() {
            try {
              processQueueAsync(fURL, fPostData, mapProcessing);
            } catch (Throwable e) {
              if (e instanceof ResourceDownloaderException) {
                debug("Error while sending message(s) to Platform: " + e.toString());
              } else {
                debug("Error while sending message(s) to Platform", e);
              }
              for (Iterator iter = mapProcessing.keySet().iterator(); iter.hasNext(); ) {
                PlatformMessage message = (PlatformMessage) iter.next();
                PlatformMessengerListener l =
                    (PlatformMessengerListener) mapProcessing.get(message);
                if (l != null) {
                  try {
                    HashMap map = new HashMap();
                    map.put("text", e.toString());
                    map.put("Throwable", e);
                    l.replyReceived(message, REPLY_EXCEPTION, map);
                  } catch (Throwable e2) {
                    debug("Error while sending replyReceived", e2);
                  }
                }
              }
            }
          }
        });
  }
  /**
   * @param mapProcessing
   * @param surl
   * @throws Exception
   */
  protected static void processQueueAsync(String sURL, String sData, Map mapProcessing)
      throws Exception {
    URL url;
    url = new URL(sURL);

    String s;
    byte[] bytes = downloadURL(url, sData);
    s = new String(bytes, "UTF8");

    Map mapAllReplies = JSONUtils.decodeJSON(s);
    List listReplies = MapUtils.getMapList(mapAllReplies, "replies", null);

    if (mapAllReplies == null || listReplies == null || listReplies.isEmpty()) {
      debug(
          "Error while sending message(s) to Platform: reply: "
              + s
              + "\nurl: "
              + sURL
              + "\nPostData: "
              + sData);
      for (Iterator iter = mapProcessing.keySet().iterator(); iter.hasNext(); ) {
        PlatformMessage message = (PlatformMessage) iter.next();
        PlatformMessengerListener l = (PlatformMessengerListener) mapProcessing.get(message);
        if (l != null) {
          try {
            HashMap map = new HashMap();
            map.put("text", "result was " + s);
            l.replyReceived(message, REPLY_EXCEPTION, map);
          } catch (Throwable e2) {
            debug(
                "Error while sending replyReceived" + "\nurl: " + sURL + "\nPostData: " + sData,
                e2);
          }
        }
      }
      return;
    }

    Map<Long, Map> mapOrder = new HashMap<Long, Map>();
    for (Object reply : listReplies) {
      if (reply instanceof Map) {
        mapOrder.put(MapUtils.getMapLong((Map) reply, "seq-id", -1), (Map) reply);
      }
    }
    for (Iterator iter = mapProcessing.keySet().iterator(); iter.hasNext(); ) {
      PlatformMessage message = (PlatformMessage) iter.next();
      PlatformMessengerListener l = (PlatformMessengerListener) mapProcessing.get(message);
      if (l == null) {
        continue;
      }
      Map mapReply = mapOrder.get(new Long(message.getSequenceNo()));
      if (mapReply == null) {
        debug("No reply for " + message.toShortString());
      }
      String replyType = MapUtils.getMapString(mapReply, "type", "payload");
      Map payload;
      if (replyType.equalsIgnoreCase("payload")) {
        payload = MapUtils.getMapMap(mapReply, "payload", Collections.EMPTY_MAP);
      } else {
        payload = new HashMap();
        payload.put("message", MapUtils.getMapString(mapReply, "message", "?"));
      }

      if (mapReply != null) {
        String reply = JSONUtils.encodeToJSON(payload);
        debug(
            "Got a reply for "
                + message.toShortString()
                + "\n\t"
                + reply.substring(0, Math.min(8192, reply.length())));
      }

      try {
        l.replyReceived(message, replyType, payload);
      } catch (Exception e2) {
        debug("Error while sending replyReceived", e2);
      }
    }
  }
  protected boolean handleLocalTunnel(
      TrackerWebPageRequest request, TrackerWebPageResponse response) throws IOException {
    start();

    if (SRP_VERIFIER == null || !active) {

      throw (new IOException("Secure pairing is not enabled"));
    }

    boolean good_request = false;

    try {
      // remove /pairing/tunnel/

      String url = request.getURL().substring(16);

      int q_pos = url.indexOf('?');

      Map<String, String> args = new HashMap<String, String>();

      if (q_pos != -1) {

        String args_str = url.substring(q_pos + 1);

        String[] bits = args_str.split("&");

        for (String arg : bits) {

          String[] x = arg.split("=");

          if (x.length == 2) {

            args.put(x[0].toLowerCase(), x[1]);
          }
        }

        url = url.substring(0, q_pos);
      }

      if (url.startsWith("create")) {

        String ac = args.get("ac");
        String sid = args.get("sid");

        if (ac == null || sid == null) {

          throw (new IOException("Access code or service id missing"));
        }

        if (!ac.equals(manager.peekAccessCode())) {

          throw (new IOException("Invalid access code"));
        }

        PairedServiceImpl ps = manager.getService(sid);

        if (ps == null) {

          good_request = true;

          throw (new IOException("Service '" + sid + "' not registered"));
        }

        PairedServiceRequestHandler handler = ps.getHandler();

        if (handler == null) {

          good_request = true;

          throw (new IOException("Service '" + sid + "' has no handler registered"));
        }

        JSONObject json = new JSONObject();

        JSONObject result = new JSONObject();

        json.put("result", result);

        byte[] ss = new byte[] {SRP_SALT[0], SRP_SALT[1], SRP_SALT[2], SRP_SALT[3]};

        long tunnel_id = RandomUtils.nextSecureAbsoluteLong();

        String tunnel_name = Base32.encode(ss) + "_" + tunnel_id;

        synchronized (local_server_map) {
          long diff = SystemTime.getMonotonousTime() - last_local_server_create_time;

          if (diff < 5000) {

            try {
              long sleep = 5000 - diff;

              System.out.println("Sleeping for " + sleep + " before starting srp");

              Thread.sleep(sleep);

            } catch (Throwable e) {
            }
          }

          SRP6Server server = new SRP6Server();

          server.init(N_3072, G_3072, SRP_VERIFIER, new SHA256Digest(), RandomUtils.SECURE_RANDOM);

          BigInteger B = server.generateServerCredentials();

          local_server_map.put(tunnel_name, new Object[] {server, handler, null, null});

          last_local_server_create_time = SystemTime.getMonotonousTime();

          total_local_servers++;

          result.put("srp_salt", Base32.encode(SRP_SALT));

          result.put("srp_b", Base32.encode(B.toByteArray()));

          Map<String, String> headers = request.getHeaders();

          String host = headers.get("host");

          // remove port number

          int pos = host.lastIndexOf("]");

          if (pos != -1) {

            // ipv6 literal

            host = host.substring(0, pos + 1);

          } else {

            pos = host.indexOf(':');

            if (pos != -1) {

              host = host.substring(0, pos);
            }
          }

          String abs_url = request.getAbsoluteURL().toString();

          // unfortunately there is some nasty code that uses a configured tracker
          // address as the default host

          abs_url = UrlUtils.setHost(new URL(abs_url), host).toExternalForm();

          pos = abs_url.indexOf("/create");

          String tunnel_url = abs_url.substring(0, pos) + "/id/" + tunnel_name;

          result.put("url", tunnel_url);
        }

        response.getOutputStream().write(JSONUtils.encodeToJSON(json).getBytes("UTF-8"));

        response.setContentType("application/json; charset=UTF-8");

        response.setGZIP(true);

        good_request = true;

        return (true);

      } else if (url.startsWith("id/")) {

        String tunnel_name = url.substring(3);

        Object[] entry;

        synchronized (local_server_map) {
          entry = local_server_map.get(tunnel_name);

          if (entry == null) {

            good_request = true;

            throw (new IOException("Unknown tunnel id"));
          }
        }

        String srp_a = args.get("srp_a");
        String enc_data = args.get("enc_data");
        String enc_iv = args.get("enc_iv");

        if (srp_a != null && enc_data != null && enc_iv != null) {

          try {
            synchronized (local_server_map) {
              long diff = SystemTime.getMonotonousTime() - last_local_server_agree_time;

              if (diff < 5000) {

                try {
                  long sleep = 5000 - diff;

                  System.out.println("Sleeping for " + sleep + " before completing srp");

                  Thread.sleep(sleep);

                } catch (Throwable e) {
                }
              }
            }

            JSONObject json = new JSONObject();

            JSONObject result = new JSONObject();

            json.put("result", result);

            SRP6Server server = (SRP6Server) entry[0];

            BigInteger A = new BigInteger(Base32.decode(srp_a));

            BigInteger serverS = server.calculateSecret(A);

            byte[] shared_secret = serverS.toByteArray();

            Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            byte[] key = new byte[16];

            System.arraycopy(shared_secret, 0, key, 0, 16);

            SecretKeySpec secret = new SecretKeySpec(key, "AES");

            decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(Base32.decode(enc_iv)));

            byte[] dec = decipher.doFinal(Base32.decode(enc_data));

            JSONObject dec_json = (JSONObject) JSONUtils.decodeJSON(new String(dec, "UTF-8"));

            String tunnel_url = (String) dec_json.get("url");

            if (!tunnel_url.contains(tunnel_name)) {

              throw (new IOException("Invalid tunnel url"));
            }

            String endpoint_url = (String) dec_json.get("endpoint");

            entry[2] = secret;
            entry[3] = endpoint_url;

            result.put("state", "activated");

            response.getOutputStream().write(JSONUtils.encodeToJSON(json).getBytes("UTF-8"));

            response.setContentType("application/json; charset=UTF-8");

            response.setGZIP(true);

            good_request = true;

            return (true);

          } catch (Throwable e) {

            throw (new IOException(Debug.getNestedExceptionMessage(e)));

          } finally {

            last_local_server_agree_time = SystemTime.getMonotonousTime();
          }
        } else if (args.containsKey("close")) {

          synchronized (local_server_map) {
            local_server_map.remove(tunnel_name);
          }

          good_request = true;

          return (true);

        } else {

          PairedServiceRequestHandler request_handler = (PairedServiceRequestHandler) entry[1];

          SecretKeySpec secret = (SecretKeySpec) entry[2];

          String endpoint_url = (String) entry[3];

          if (secret == null) {

            throw (new IOException("auth not completed"));
          }

          byte[] request_data = FileUtil.readInputStreamAsByteArray(request.getInputStream());

          try {
            byte[] decrypted;

            {
              byte[] IV = new byte[16];

              System.arraycopy(request_data, 0, IV, 0, IV.length);

              Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

              decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV));

              decrypted = decipher.doFinal(request_data, 16, request_data.length - 16);
            }

            byte[] reply_bytes =
                request_handler.handleRequest(
                    request.getClientAddress2().getAddress(), endpoint_url, decrypted);

            {
              Cipher encipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

              encipher.init(Cipher.ENCRYPT_MODE, secret);

              AlgorithmParameters params = encipher.getParameters();

              byte[] IV = params.getParameterSpec(IvParameterSpec.class).getIV();

              byte[] enc = encipher.doFinal(reply_bytes);

              byte[] rep_bytes = new byte[IV.length + enc.length];

              System.arraycopy(IV, 0, rep_bytes, 0, IV.length);
              System.arraycopy(enc, 0, rep_bytes, IV.length, enc.length);

              response.getOutputStream().write(rep_bytes);

              response.setContentType("application/octet-stream");

              good_request = true;

              return (true);
            }
          } catch (Throwable e) {

            throw (new IOException(Debug.getNestedExceptionMessage(e)));
          }
        }
      }

      throw (new IOException("Unknown tunnel operation"));

    } finally {

      if (!good_request) {

        manager.recordRequest(
            "SRP", request.getClientAddress2().getAddress().getHostAddress(), false);
      }
    }
  }