/**
  * {@inheritDoc}
  *
  * @throws IOException
  * @see
  *     org.sakaiproject.nakamura.api.proxy.ProxyPostProcessor#process(org.apache.sling.api.SlingHttpServletResponse,
  *     org.sakaiproject.nakamura.api.proxy.ProxyResponse)
  */
 public void process(
     Map<String, Object> templateParams,
     SlingHttpServletResponse response,
     ProxyResponse proxyResponse)
     throws IOException {
   for (Entry<String, String[]> h : proxyResponse.getResponseHeaders().entrySet()) {
     for (String v : h.getValue()) {
       response.setHeader(h.getKey(), v);
     }
   }
   int code = proxyResponse.getResultCode();
   response.setStatus(code);
   IOUtils.stream(proxyResponse.getResponseBodyAsInputStream(), response.getOutputStream());
 }
  protected void dispatch(
      SlingHttpServletRequest request, SlingHttpServletResponse response, boolean userInputStream)
      throws ServletException, IOException {
    try {

      Resource resource = request.getResource();
      if (!resource.getPath().startsWith(PROXY_PATH_PREFIX)) {
        response.sendError(
            HttpServletResponse.SC_FORBIDDEN,
            "Proxying templates may only be stored in " + PROXY_PATH_PREFIX);
        return;
      }
      Node node = resource.adaptTo(Node.class);
      if (!userInputStream) {
        Value[] v = JcrUtils.getValues(node, SAKAI_REQUEST_STREAM_BODY);
        if (v != null && v.length > 0) {
          userInputStream = Boolean.parseBoolean(v[0].getString());
        }
      }
      Map<String, String> headers = new ConcurrentHashMap<String, String>();
      for (Enumeration<?> enames = request.getHeaderNames(); enames.hasMoreElements(); ) {

        String name = (String) enames.nextElement();
        if (!headerBacklist.contains(name)) {
          headers.put(name, request.getHeader(name));
        }
      }
      // search for special headers.
      if (headers.containsKey(BASIC_USER)) {
        String user = headers.get(BASIC_USER);
        String password = headers.get(BASIC_PASSWORD);
        Base64 base64 = new Base64();
        String passwordDigest =
            new String(base64.encode((user + ":" + password).getBytes("UTF-8")));
        String digest = BASIC + passwordDigest.trim();
        headers.put(AUTHORIZATION, digest);
      }

      for (Entry<String, String> e : headers.entrySet()) {
        if (e.getKey().startsWith(":")) {
          headers.remove(e.getKey());
        }
      }

      // collect the parameters and store into a mutable map.
      RequestParameterMap parameterMap = request.getRequestParameterMap();
      Map<String, Object> templateParams = new ConcurrentHashMap<String, Object>(parameterMap);

      // search for special parameters.
      if (parameterMap.containsKey(BASIC_USER)) {
        String user = parameterMap.getValue(BASIC_USER).getString();
        String password = parameterMap.getValue(BASIC_PASSWORD).getString();
        Base64 base64 = new Base64();
        String passwordDigest =
            new String(base64.encode((user + ":" + password).getBytes("UTF-8")));
        String digest = BASIC + passwordDigest.trim();
        headers.put(AUTHORIZATION, digest);
      }

      // we might want to pre-process the headers
      if (node.hasProperty(ProxyPreProcessor.SAKAI_PREPROCESSOR)) {
        String preprocessorName =
            node.getProperty(ProxyPreProcessor.SAKAI_PREPROCESSOR).getString();
        ProxyPreProcessor preprocessor = preProcessors.get(preprocessorName);
        if (preprocessor != null) {
          preprocessor.preProcessRequest(request, headers, templateParams);
        } else {
          LOGGER.warn(
              "Unable to find pre processor of name {} for node {} ",
              preprocessorName,
              node.getPath());
        }
      }
      ProxyPostProcessor postProcessor = defaultPostProcessor;
      // we might want to post-process the headers
      if (node.hasProperty(ProxyPostProcessor.SAKAI_POSTPROCESSOR)) {
        String postProcessorName =
            node.getProperty(ProxyPostProcessor.SAKAI_POSTPROCESSOR).getString();
        if (postProcessors.containsKey(postProcessorName)) {
          postProcessor = postProcessors.get(postProcessorName);
        }
        if (postProcessor == null) {
          LOGGER.warn(
              "Unable to find post processor of name {} for node {} ",
              postProcessorName,
              node.getPath());
          postProcessor = defaultPostProcessor;
        }
      }

      ProxyResponse proxyResponse =
          proxyClientService.executeCall(node, headers, templateParams, null, -1, null);
      try {
        postProcessor.process(templateParams, response, proxyResponse);
      } finally {
        proxyResponse.close();
      }
    } catch (IOException e) {
      throw e;
    } catch (ProxyClientException e) {
      response.sendError(500, e.getMessage());
    } catch (RepositoryException e) {
      response.sendError(500, e.getMessage());
    }
  }
  public void process(SlingHttpServletResponse response, ProxyResponse proxyResponse)
      throws IOException {
    Map<String, String[]> headers = proxyResponse.getResponseHeaders();

    // Check if the content-length is smaller than the maximum (if any).
    String[] contentLengthHeader = headers.get("Content-Length");
    if (contentLengthHeader != null) {
      int length = Integer.parseInt(contentLengthHeader[0]);
      if (length > MAX_RSS_LENGTH) {
        response.sendError(
            HttpServletResponse.SC_FORBIDDEN,
            "This RSS feed is too big. The maximum for a feed is: " + MAX_RSS_LENGTH);
        return;
      }
    }

    // Check if the Content-Type we get is valid (if any).
    String[] contentTypeHeader = headers.get("Content-Type");
    if (contentTypeHeader != null) {
      String contentType = contentTypeHeader[0];
      if (contentType.contains(";")) {
        contentType = contentType.substring(0, contentType.indexOf(';'));
      }
      if (!contentTypes.contains(contentType)) {
        response.sendError(
            HttpServletResponse.SC_FORBIDDEN, "This URL doesn't send a proper Content-Type back");
        return;
      }
    }

    boolean isValid = false;
    InputStream in = proxyResponse.getResponseBodyAsInputStream();
    InputStreamReader reader = new InputStreamReader(in);

    // XMLStreamWriter writer = null;
    XMLEventWriter writer = null;
    ByteArrayOutputStream out = null;

    int i = 0;
    try {
      XMLEventReader eventReader = xmlInputFactory.createXMLEventReader(reader);
      // Create a temporary outputstream where we can write to.
      out = new ByteArrayOutputStream();

      Map<String, Boolean> checkedElements = new HashMap<String, Boolean>();
      checkedElements.put("rss", false);
      checkedElements.put("channel", false);
      checkedElements.put("title", false);
      checkedElements.put("link", false);
      checkedElements.put("item", false);
      checkedElements.put("title", false);
      checkedElements.put("link", false);

      XMLOutputFactory outputFactory = new WstxOutputFactory();
      writer = outputFactory.createXMLEventWriter(out);

      while (eventReader.hasNext()) {
        XMLEvent e = eventReader.nextEvent();
        // Stream it to an output stream.
        writer.add(e);

        if (!isValid) {
          if (e.getEventType() == XMLEvent.START_ELEMENT) {
            StartElement el = e.asStartElement();
            String name = el.getName().toString().toLowerCase();
            if (checkedElements.containsKey(name)) {
              checkedElements.put(name, true);
            }

            boolean all = true;
            for (Entry<String, Boolean> es : checkedElements.entrySet()) {
              if (!checkedElements.get(es.getKey())) {
                all = false;
                break;
              }
            }
            if (all) isValid = true;
          }

          if (i > 100) {
            response.sendError(
                HttpServletResponse.SC_FORBIDDEN,
                "This file does not match an RSS formatted XML file..");
            break;
          }
          i++;
        }
      }

      if (!isValid) {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid RSS file.");
        return;
      }

      // Check if we are not streaming a gigantic file..
      if (out.size() > MAX_RSS_LENGTH) {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "This file is to big.");
        return;
      }

      for (Entry<String, String[]> h : proxyResponse.getResponseHeaders().entrySet()) {
        for (String v : h.getValue()) {
          response.setHeader(h.getKey(), v);
        }
      }
      // We always return 200 when we get to this point.
      response.setStatus(200);
      response.setHeader("Content-Length", "" + out.size());
      // Write the cached stream to the output.
      out.writeTo(response.getOutputStream());

    } catch (XMLStreamException e) {
      response.sendError(HttpServletResponse.SC_FORBIDDEN, "This is not a valid XML file.");
    } catch (Exception e) {
      logger.warn("Exception reading RSS feed.");
      response.sendError(HttpServletResponse.SC_FORBIDDEN, "General exception caught.");
    } finally {
      out.close();
      reader.close();
      try {
        writer.close();
      } catch (XMLStreamException e) {
        // Not much we can do?
        e.printStackTrace();
      }
    }
  }