//Title:       AdvantaServer
//Version:	   1.7
//Author:      Stuart Lowry & Scott Palmateer
//Company:     Science Applications International Corporation
//Description: Miniature web server for serving up data files.

//package saic.oaa2;

import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

/**
* This program acts as a miniature web server. It supports posting of data, and subsequent retrieval via 
* unique reference numbers. It runs in its own thread for easy inclusion in applications
* @author Scott Palmateer & Stuart Lowry
* @version 1.7
*
**/

public class AdvantaServer extends Thread
{
	// The port on which this server listens.
	int port;
	// The datastore to store and retrieve data
	DataStore store;
	// The name of this server.
	String servername = "Advantaserver";

	String serverversion = "1.7";

	public AdvantaServer (int portp, DataStore storep)
	{
		port = portp;
		store = storep;
	}

	/**
	* Send output to socket.
	*
	* @param theConnection a socket connection with a client
	* @param resultheader result header, e.g. "HTTP/1.0 200 OK"
	* @param contenttype content type e.g. "text/html"
	* @param result the actual content
	**/
	void outputResult(Socket theConnection, String resultheader, String contenttype, String result)
	{
		try {
			int resultlen = result.length();
			String resultlenstring = Integer.toString(resultlen);
			PrintWriter pw = new PrintWriter(new BufferedOutputStream(theConnection.getOutputStream(),1024),false);
			pw.print(resultheader+"\r\n");
			pw.print("Content-type: "+contenttype+"\r\n");
			pw.print("Content-length: "+resultlenstring+"\r\n\r\n");
			pw.print(result);
			pw.flush();
		} catch (Exception ex) {
			System.err.println("advantaserver: caught exception (outputResult()): " + ex.getClass().getName());
		}
	}
	/**
	* Handle an unknown POST request
	*
	* @param theConnection a socket connection with a client
	* @param command the offending request, neither "put" nor "get"
	**/
	void handleUnknownCommand(Socket theConnection, String command)
	{
		StringBuffer sb = new StringBuffer();
		sb.append("<HTML>\n");
		sb.append("<HEAD>\n");
		sb.append("<TITLE>"+servername+": Bad Request</TITLE>\n");
		sb.append("</HEAD>\n");
		sb.append("<BODY BGCOLOR=\"white\">\n");
		sb.append("<H1>400 Bad Request</H1>\n");
		sb.append("<HR>\n");
		sb.append("Unknown command:"+command+"<BR>\n");
		sb.append("The following commands are supported:<BR>\n");
		sb.append("<UL>\n");
		sb.append("<LI>put?data=...\n");
		sb.append("<LI>get?ref=...\n");
		sb.append("</UL\n");
		sb.append("</BODY>\n");
		sb.append("</HTML>\n");
		outputResult(theConnection, "HTTP/1.0 400 Bad Request","text/html",sb.toString());
	}
	/**
	* Handle an unknown request method. This server only handles POST and GET.
	*
	* @param theConnection a socket connection with a client
	* @param command the offending method, neither "POST" nor "GET"
	**/
	void handleUnknownMethod(Socket theConnection, String method)
	{
		StringBuffer sb = new StringBuffer();
		sb.append("<HTML>\n");
		sb.append("<HEAD>\n");
		sb.append("<TITLE>"+servername+": Unimplemented Method</TITLE>\n");
		sb.append("</HEAD>\n");
		sb.append("<BODY BGCOLOR=\"white\">\n");
		sb.append("<H1>501 Not Implemented</H1>\n");
		sb.append("<HR>\n");
		sb.append("Unknown method:"+method+"<BR>\n");
		sb.append("The following methods are supported:<BR>\n");
		sb.append("<UL>\n");
		sb.append("<LI>POST\n");
		sb.append("<LI>GET\n");
		sb.append("</UL\n");
		sb.append("</BODY>\n");
		sb.append("</HTML>\n");
		outputResult(theConnection, "HTTP/1.0 501 Not Implemented","text/html",sb.toString());
	}
	/**
	* Handle Reference not found.
	*
	* @param theConnection a socket connection with a client
	* @param ref the unknown reference
	**/
	void handleReferenceNotFound(Socket theConnection, String ref)
	{
		StringBuffer sb = new StringBuffer();
		sb.append("<HTML>\n");
		sb.append("<HEAD>\n");
		sb.append("<TITLE>"+servername+": Not Found</TITLE>\n");
		sb.append("</HEAD>\n");
		sb.append("<BODY BGCOLOR=\"white\">\n");
		sb.append("<H1>404 Not Found</H1>\n");
		sb.append("<HR>\n");
		sb.append("Cannot find reference:"+ref+"<BR>\n");
		sb.append("</BODY>\n");
		sb.append("</HTML>\n");
		outputResult(theConnection, "HTTP/1.0 404 Not Found","text/html",sb.toString());
	}
	/**
	* Handle POST-put request
	*
	* @param theConnection a socket connection with a client
	* @param br the reader assosciated with this connection
	**/
	void handlePostPut(Socket theConnection, BufferedReader br)
	{
		try 
		{
			StringBuffer accum = new StringBuffer();
			int contentlength = 0;
			String inputline;
			int c;

			// Read the header information and extract the Content-length value.
			while((inputline = br.readLine()) != null && inputline.length() != 0)
			{
				StringTokenizer st2 = new StringTokenizer(inputline,":");
				String param = st2.nextToken().trim();
				String value = st2.nextToken().trim();
				if (param.compareTo("Content-length") == 0) { contentlength = Integer.parseInt(value);}
			}

			// Read in Content-length bytes. 
			// First, skip the "data=" part, then accumulate the actual data
			for (int i=0; i<5; i++) {
				c = br.read();
				contentlength--;
			}
			while(contentlength != 0  && (c=br.read()) != 1)
			{
				accum.append((char)c);
				contentlength--;
			}

			String ref = store.put(URLDecoder.decode(accum.toString()));
			outputResult(theConnection, "HTTP/1.0 200 OK","text/plain", ref);
		} catch (Exception ex) {
				System.err.println("advantaserver: caught exception (handlePostPut()): " +
				ex.getClass().getName());
		}
	}

	/**
	* Handle POST-get request
	*
	* @param theConnection a socket connection with a client
	* @param br the reader assosciated with this connection
	**/
	void handlePostGet(Socket theConnection, BufferedReader br)
	{
		try 
		{
			int contentlength = 0;
			String inputline;

			while((inputline = br.readLine()) != null && inputline.length() != 0)
			{
				StringTokenizer st2 = new StringTokenizer(inputline,":");
				String param = st2.nextToken().trim();
				String value = st2.nextToken().trim();
				if (param.compareTo("Content-length") == 0) { contentlength = Integer.parseInt(value);}
			}

			// Collect the reference request 
			StringBuffer accum = new StringBuffer();
			int c;
			while(contentlength != 0  && (c=br.read()) != 1)
			{
				accum.append((char)c);
				contentlength--;
			}

			StringTokenizer st3 = new StringTokenizer(accum.toString(), "=");
			st3.nextToken();
			String ref = st3.nextToken();
			String val = store.get(ref);
			if (val != null) {
				val = URLEncoder.encode(val);
				outputResult(theConnection, "HTTP/1.0 200 OK","application/x-www-form-urlencoded", val);
			} else {
				handleReferenceNotFound(theConnection,ref);
			}
		} catch (Exception ex) {
				System.err.println("advantaserver: caught exception (handlePostGet()): " +
				ex.getClass().getName());
		}
	}

	/**
	* Handle GET-get request
	*
	* @param theConnection a socket connection with a client
	* @param command the offending method, neither "POST" nor "GET"
	* @param br the reader assosciated with this connection
	**/
	void handleGetGet(Socket theConnection, String command, BufferedReader br)
	{
		try 
		{
			int i = command.indexOf("=");
			String ref = command.substring(i+1);
			String val = store.get(ref);
			if (val != null)
			{
				val = URLEncoder.encode(val);
				outputResult(theConnection, "HTTP/1.0 200 OK","application/x-www-form-urlencoded", val);
			} else {
				handleReferenceNotFound(theConnection,ref);
			}
		} catch (Exception ex) {
				System.err.println("advantaserver: caught exception (handleGetGet()): " +
				ex.getClass().getName());
		}
	}
	/**
	* Main workhorse method. Run the server, handle requests.
	*/
	public void run()
	{
		try
		{
			ServerSocket theServer = new ServerSocket(port);
			while (true)
			{
				Socket theConnection = theServer.accept();
				BufferedReader br = new BufferedReader(new InputStreamReader(theConnection.getInputStream()));
				String inputline = br.readLine();
				StringTokenizer st = new StringTokenizer(inputline);
				String method = st.nextToken();
				String command = st.nextToken();
				//handle POST
				if (method.compareTo("POST") == 0)
				{
					if (command.compareTo("/put") == 0){
						handlePostPut(theConnection,br);
					} else if (command.compareTo("/get") == 0){
						handlePostGet(theConnection,br);
					} else {
						handleUnknownCommand(theConnection,command);
					}
				} else if (method.compareTo("GET") == 0){
					handleGetGet(theConnection,command,br);
				} else {
					handleUnknownMethod(theConnection,method);
				}
				br.close();
				theConnection.close();
			}
		} catch (Exception ex) {
			System.err.println("advantaserver: caught exception (run): "+ ex.getClass().getName());
		}
	}
	public static void main (String args[])
	{
		int port = 4444;
		if (args.length >1)
		{
			System.err.println("usage: java AdvantaServer [PORTNUM]");
			System.exit(-1);
		}
		if (args.length == 1)
		{
			try
			{
				port = Integer.parseInt(args[0]);
			} catch (Exception ex) {
				System.err.println("usage: java AdvantaServer [PORTNUM]");
				System.exit(-1);
			}
			Frame f = new Frame();
			f.setLayout(new BorderLayout());
			Button b = new Button("Exit AdvantaServer");
			b.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					System.exit(0);
				}
			});
			f.add(b, BorderLayout.NORTH);
			f.pack();
			f.show();
			System.out.println("advantaserver: Started up on port: "+port);
			String userName = System.getProperty("user.name");
			DataStore store = new DataStore(userName,"/tmp");
			AdvantaServer httpserver = new AdvantaServer(port,store);
			httpserver.start();
		}
	}
}
