/* You may not modify, use, reproduce, or distribute this software except in compliance with the terms of the License at:
 http://developer.sun.com/berkeley_license.html
 $Id: GenericService.java,v 1.6 2007/08/08 15:51:17 gmurray71 Exp $ */

package jmaki.service;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// for MD5
import java.security.MessageDigest;

/**
 * @author Greg Murray
 */
public class GenericService extends HttpServlet {

    private MessageDigest md;
    // Server Side Token to Append to client provide tokens and host name
    private static String SERVER_TOKEN = "JMAKI_SERIVCE";

    public GenericService() {
    }

    private String callback = "callback";

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (java.security.NoSuchAlgorithmException nsa) {
            throw new ServletException(nsa);
        }
    }

    public void doGet(HttpServletRequest req, HttpServletResponse response)
        throws ServletException, IOException {

        PrintWriter writer = response.getWriter();
        String action = req.getParameter("action");

        if (action != null) {
            // generate an api key
            if ("genkey".equals(action)) {
            	// build up the host name
                String host = req.getScheme() + "://" +  req.getServerName();
                int port = req.getServerPort();
                if (port != 80) host += ":" + port;
                host += req.getContextPath();
                host += req.getServletPath();
                     	
                String url = req.getParameter("url");
                if (!url.endsWith("/")) url += "/";
                if (url != null) {
                	writer.print("{ 'service' : '" + host + "'," +
                                  " 'key' : '" + generateHash(url + SERVER_TOKEN) + "'}");
                }
                else writer.println("url required to generate an api key");
                return;
            }
        } else {
            if (req.getParameter("apikey") != null) {
                boolean valid = testAPIKey(req, req.getParameter("apikey"));
                String lc = req.getParameter("callback");
                String format = req.getParameter("format");
                if (valid) {
                    if ("jsonp".equals(format)) {
                      response.setContentType("text/javascript");
                      if (lc == null) lc = callback;
                      writer.write(lc +
                                    "(" + jsonContent + ");");
                       return;
                    } else if ("json".equals(format)) {
                       response.setContentType("text/json");
                       writer.write(jsonContent);
                       return;
                    } else {
                        response.setContentType("text/xml");
                        writer.write(xmlContent);
                    }
                } else {
                    String referer = req.getHeader("Referer");
                    // check if it's a relative URL and make sure we end with a slash
                    if (!referer.startsWith("http") &&
                       !referer.endsWith("/") ) referer = referer + "/";                	
                    writer.println("alert('Invalid apikey for " + referer + "');");
                    return;
                }
            } else {
                writer.println("Please provide an apikey.");
                return;
            }
        }
    }
    /**
     * Check the Referer against the API key provided
     * 
     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
     */
    private boolean testAPIKey(HttpServletRequest req, String apiKey) {
        String referer = req.getHeader("Referer");
        // check if it's a relative URL and make sure we end with a slash
        if (!referer.startsWith("http") &&
            !referer.endsWith("/") ) referer = referer + "/";
        // get the key for the host used by the request
        String refererKey = generateHash(referer + SERVER_TOKEN);
        if (apiKey != null) return refererKey.equals(apiKey);
        else return false;
    }

    private String generateHash(String key) {
    	key += SERVER_TOKEN;
        // start fresh
        md.reset();
        md.update(key.getBytes());
        byte[] bytes = md.digest();
        // buffer to write the md5 hash to
        StringBuffer buff = new StringBuffer();
        for (int l=0;l< bytes.length;l++) {
            String hx = Integer.toHexString(0xFF & bytes[l]);
            // make sure the hex string is correct if 1 character
            if (hx.length() == 1) buff.append("0");
            buff.append(hx);
        }
        return buff.toString().trim();
    }

    private String jsonContent =
        "{\n" +
        " 'columns' :[\n" +
        "  {'label' : 'Company', 'id' : 'company'},\n" +
        "  {'label' : 'Price', 'id' : 'price' },\n" +
        "  {'label' : 'Change', 'id' : 'change'},\n" +
        "  {'label' : '% Change', 'id' : 'pchange'},\n" +
        "  {'label' : 'Last Updated', 'id' : 'lupdate'}\n" +
        " ],\n" +
        " 'rows' :\n" +
        "  [\n" +
        "    { 'company' : 'A Co', 'price' : 61.72, 'change' : 1.85, 'pchange' : 0.03, 'lupdate' : '9/1 8:00am'},\n" +
        "    { 'company' : 'B Inc', 'price' : 51.30, 'change' : 3.59, 'pchange' : 0.07, 'lupdate' : '9/1 12:00am'},\n" +
        "    { 'company' : 'M Co', 'price' : 2.75, 'change' : 1.47, 'pchange' : 0.08, 'lupdate' : '9/1 2:00am'},\n" +
        "    { 'company' : 'Z Corp', 'price' : 5.32, 'change' : 1.81, 'pchange' : 0.34, 'lupdate' : '9/1 1:00am'}\n" +        
        "  ]\n" +
        "}\n";

    private String xmlContent =
        "<table>\n" +
        " <columns>\n" +
        "  <column><id>company</id><label>Company</label></column>\n" +
        "  <column><id>price</id><label>Price</label></column>\n" +
        "  <column><id>change</id><label>Change</label></column>\n" +
        "  <column><id>pchange</id><label>% Change</label></column>\n" +
        "  <column><id>lupdate</id><label>Last Updated</label></column>\n" +
        " </columns>\n" +
        " <rows>\n" +
        "  <row>\n" +
        "   <company>A Co</company>\n" +
        "   <price>71.72</price>\n" +
        "   <change>1.85</change>\n" +
        "   <pchange>0.03</pchange>\n" +
        "   <lupdate>9/1 8:00am</lupdate>\n" +
        "  </row>\n" +
        "  <row>\n" +
        "   <company>B Inc</company>\n" +
        "   <price>51.30</price>\n" +
        "   <change>3.59</change>\n" +
        "   <pchange>0.07</pchange>\n" +
        "   <lupdate>9/1 12:00am</lupdate>\n" +
        "  </row>\n" +
        "  <row>\n" +
        "   <company>M Co</company>\n" +
        "   <price>2.75</price>\n" +
        "   <change>0.08</change>\n" +
        "   <pchange>1.47</pchange>\n" +
        "   <lupdate>9/1 2:00am</lupdate>\n" +
        "  </row>\n" +
        "  <row>\n" +
        "   <company>Z Corp</company>\n" +
        "   <price>5.32</price>\n" +
        "   <change>1.81</change>\n" +
        "   <pchange>0.34</pchange>\n" +
        "   <lupdate>9/1 1:00am</lupdate>\n" +
        "  </row>\n" +
        " </rows>\n" +
        "</table>";
}