package com.mwc.httpd;

import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
import java.security.Security;
import java.security.KeyStore;
import javax.net.*;
import javax.net.ssl.*;
import javax.security.cert.X509Certificate;
import javax.servlet.*;
import javax.servlet.http.*;
import com.sun.net.ssl.*;
import gnu.regexp.*;
import sun.misc.BASE64Decoder;
import com.mwc.util.*;

class SessionData implements Serializable {
   boolean isNew;
   Hashtable attrs;
   Date creationDate;
   String id;
   Date lastAccessedDate;
   int maxInactiveInterval;
   boolean valid;
   
   SessionData() {
      super();
   }
}

/** 
 * Factory V0.0.1
 * 
 * A multi-threaded HTTP/HTTPS server.
 * 
 * @author Matthew W. Coan 05/04/2002
 */
public class httpd implements Runnable {
   public static final String VERSION = "Servlet Factory V0.0.1b";
   private boolean _debugMode;
   private void _debug(String msg) {
      if(_debugMode)
         System.out.println("["+Thread.currentThread().getName()+"]"+msg);
   }
   private boolean _debugMode2;
   private void _debug2(String msg) {
      if(_debugMode2)
         System.out.println("["+Thread.currentThread().getName()+"]"+msg);
   }
   private TemplateWebPage _errorPage;
   private String _propFileName;
   private Properties _props;
   private ServerSocket _svr;
   private String _docRoot;
   private int _timeout;
   private int _maxHeaders;
   private Hashtable _servletTable;
   private Hashtable _servletLastMod;
   private FileServlet _fileServlet;
   private SimpleDateFormat _dateFormat = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
   private SimpleDateFormat _cookieDateFormat = new SimpleDateFormat("E, dd-MMM-yyyy HH:mm:ss z");
   private LinkedList _filterList = new LinkedList();
   private LinkedList _patternList = new LinkedList();
   private LogFile _servletLogFile;
   private AccepterThread _accepterThreadArray[];
   private ThreadPool _threadPool;
   private Hashtable _sessionTbl = new Hashtable();
   private Hashtable _sessionReadWriteLockTbl = new Hashtable();
   private boolean _sessionManagement;
   private String _sessionSaveDir;
   private ServletBinding _servletBindingArray[];
      
   private static httpd _server = null;
   private static boolean _stopFlag = false;
   private static Object _stopFlagMutex = new Object(); 

   private static boolean _isRunning() {
      synchronized(_stopFlagMutex) {
         return _stopFlag == false;
      }
   }
   private static void _stop() {
      synchronized(_stopFlagMutex) {
         _stopFlag = true;
         try {
            _server._svr.close();
         }
         catch(Throwable t) {
            ;
         }
      }
      _server._threadPool.stop();
   }
   
   private class ServletBinding {
      private RE _re;
      private String _className;
      
      private ServletBinding(String expr, String className)
      throws Exception {
         _re = new RE(expr);
         _className = className;
      }
      
      private boolean _isMatch(String uri) {
         REMatch m = _re.getMatch(uri);
         boolean ret = false;
         if(m != null) {
            if(m.getStartIndex() == 0 && m.getEndIndex() == uri.length())
               ret = true;
         }
         return ret;
      }
   }
   
   private class ServletClassLoader extends ClassLoader {
      
      public Class findClass(String name)
      throws ClassNotFoundException {
         HttpServlet hs = (HttpServlet)_servletTable.get(name);
         if(hs == null) {
            byte[] b;
            try {
               b = loadClassData(name);
            }
            catch(IOException ioe) {
               throw new ClassNotFoundException(ioe.getMessage());
            }
            return defineClass(name, b, 0, b.length);
         }
         else {
            Long lm = (Long)_servletLastMod.get(name);
            URL url = this.getResource(name.replace('.', '/')+".class");
            File f = new File(url.getFile());
            if(f.lastModified() > lm.longValue()) {
               byte[] b;
               try {
                  b = loadClassData(name);
               }
               catch(IOException ioe) {
                  throw new ClassNotFoundException(ioe.getMessage());
               }
               return defineClass(name, b, 0, b.length);
            }
            else
               return hs.getClass();
         }
      }

      private byte[] loadClassData(String name)
      throws IOException {
         LinkedList list = new LinkedList();
         int ch;
         InputStream in = this.getResourceAsStream(name.replace('.', '/')+".class");
         if(in != null) {
            while((ch = in.read()) != -1)
               list.add(new Byte((byte)ch));
         }
         byte ret[] = new byte[list.size()];
         for(int i = 0; i < ret.length; i++)
            ret[i] = ((Byte)list.get(i)).byteValue();
         return ret;
      }
   }
   
   private class Handler implements Runnable {
      Socket _client;
      
      public Handler(Socket client) {
         _client = client;
      }
      
      public void run() {
         try {
            try {
               _handleClient(_client);
            }
            finally {
               _client.close();
            }
         }
         catch(SocketException se) {
            se.printStackTrace();
         }
         catch(Throwable t) {
            t.printStackTrace();
            _servletLogFile.log("", "Handle client error", t);
         }
      }
   }
   
   private class AccepterThread extends Thread {
      public void run() {
         Socket client;
         try {
            while(_isRunning()) {
               synchronized(_svr) {
                  client = _svr.accept();
               }
               _debug("ACCEPT: " + client.getInetAddress().getHostName());
               try {
                  _threadPool.postTaskForExecution(new Handler(client));
               }
               catch(Throwable throwable) {
                  throwable.printStackTrace();
               }
            }
         }
         catch(Throwable t) {
            t.printStackTrace();
         }
      }
   }
   
   private class LogFile {
      private PrintWriter _out;
      private String _fileName;
      private SimpleDateFormat _dateFormat = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
      
      LogFile(String fileName)
      throws IOException {
         _fileName = fileName;
         _out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_fileName, true))));
      }
      
      void log(String servlet, 
               java.lang.Exception exception, 
               java.lang.String msg) {
         synchronized(this) {
            _out.println(servlet + ":" + exception.getMessage());
            _out.println("MESSAGE: " + msg);
            _out.println("DATE: " + _dateFormat.format(new Date()));
            exception.printStackTrace(_out);
            _out.flush();
         }
      }
      
      void log(String servlet, 
               java.lang.String msg) {
         synchronized(this) {
            _out.println(servlet + ":" + msg);
            _out.println("DATE: " + _dateFormat.format(new Date()));
            _out.flush();
         }
      }
      
      void log(String servlet, 
               java.lang.String message, 
               java.lang.Throwable throwable) {
         synchronized(this) {
            _out.println(servlet + ":" + throwable.getMessage());
            _out.println("MESSAGE: " + message);
            _out.println("DATE: " + _dateFormat.format(new Date()));
            throwable.printStackTrace(_out);
            _out.flush();
         }
      }
      
      void close() 
      throws IOException {
         synchronized(this) {
            _out.flush();
            _out.close();
         }
      }
   }
   
   private class FileServlet extends HttpServlet {
      private String _def = _props.getProperty("server.default.content.type");
      private int _maxBuffer = Integer.parseInt(_props.getProperty("server.max.buffer"));
      private String _root = _props.getProperty("server.document.root");
      
      private String _htaccess(String path) {
         String temp = path;
         File f;
         int index;
         while(temp.compareTo(_root) != 0) {
            index = temp.lastIndexOf('/');
            if(index < 0)
               return null;
            temp = temp.substring(0, index) + "/.htaccess";
            f = new File(temp);
            if(f.exists())
               return temp;
            temp = temp.substring(0, index);
         }
         return null;
      }
      
      public void doGet(HttpServletRequest req, 
                        HttpServletResponse res)
      throws IOException, ServletException {
         String path = req.getRequestURI();
         path = _translatePath(path);
         if(path == null || path.toLowerCase().endsWith("/.htaccess")) {
            res.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
         }
         int index = path.lastIndexOf('.');
         String contentType;
         if(index >= 0) {
            String ext = path.substring(index).toLowerCase();
            contentType = _props.getProperty("content.type"+ext);
            if(contentType == null)
               contentType = _def;
         }
         else
            contentType = _def;
         String htaccess;
         File f;
         htaccess = _htaccess(path);
         if(htaccess != null) {
            f = new File(htaccess);
            Properties users = new Properties();
            FileInputStream fin = new FileInputStream(f);
            BufferedInputStream bin = new BufferedInputStream(fin);
            users.load(bin);
            fin.close();
               
            String realm = users.getProperty("realm");
               
            String auth = req.getHeader("Authorization");
            if(auth == null) {
               res.setHeader("WWW-Authenticate", "Basic realm=\""+realm+"\"");
               res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
               return;
            }
            else {
               boolean ok = false;
               auth = auth.substring(6);
               BASE64Decoder b64d = new BASE64Decoder();
               String up = new String(b64d.decodeBuffer(auth));
               int i = up.indexOf(':');
               if(i >= 0 && (i+1) < up.length()) {
                  String u = up.substring(0, i);
                  String p = up.substring(i+1);
                  String rp = users.getProperty(u);
                  if(rp != null && rp.compareTo(p) == 0 && rp.compareTo("realm") != 0)
                     ok = true;
               }
               if(!ok) {
                  res.setHeader("WWW-Authenticate", "Basic realm=\""+realm+"\"");
                  res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                  return;
               }
            }
         }
         f = new File(path);
         res.addDateHeader("Last-Modified", f.lastModified());
         res.setContentLength((int)(f.length()));
         res.setContentType(contentType);
         OutputStream out = res.getOutputStream();
         _debug("SENDING FILE: " + path);
         FileInputStream fin = new FileInputStream(path);
         DataInputStream din = new DataInputStream(fin);
         byte buffer[] = new byte[_maxBuffer];
         int n;
         while((n = din.read(buffer)) > 0) {
            out.write(buffer, 0, n);
            out.flush();
         }
         fin.close();
      }
   }
   
   private class ContextImpl implements ServletContext {
      private Hashtable _attrs = new Hashtable();
      private Hashtable _initParams = new Hashtable();
      private String _servlet;
      
      ContextImpl(String servlet, String initParameters) {
         if(initParameters == null)
            initParameters = "";
         StringTokenizer sTok = new StringTokenizer(initParameters, "&");
         String temp;
         String name;
         String value;
         int index;
         while(sTok.hasMoreTokens()) {
            temp = sTok.nextToken();
            index = temp.indexOf("=");
            if(index < 0) {
               name = temp;
               value = "";
            }
            else {
               name = temp.substring(0, index);
               if((index+1) < temp.length())
                  value = temp.substring(index+1);
               else
                  value = "";
            }
            _initParams.put(name, value);
         }
         //_attrs.put("javax.servlet.context.tempdir", _props.getProperty("server.temp.dir"));
         _servlet = servlet;
      }
                    
      String name() { return _servlet; }
      
      public java.lang.Object getAttribute(java.lang.String name) {
         _debug2("getAttribute(\""+name+"\")");
         return _attrs.get(name);
      }
      
      public java.util.Enumeration getAttributeNames() {
         return _attrs.keys();
      }
      
      public ServletContext getContext(java.lang.String uripath) {
         Servlet servlet = null;
         try {
            servlet = _findServlet(uripath);
         }
         catch(Exception ex) {
            return null;
         }
         if(servlet == null)
            return null;
         return servlet.getServletConfig().getServletContext();
      }
      
      public java.lang.String getInitParameter(java.lang.String name) {
         _debug2("getInitParameter(\""+name+"\")");
         Object ip = _initParams.get(name);
         if(ip == null)
            return null;
         _debug2("returning=\""+ip.toString()+"\"");
         return ip.toString();
      }
      
      public java.util.Enumeration getInitParameterNames() {
         return _initParams.keys();
      }
      
      public int getMajorVersion() {
         return 0;
      }
      
      public java.lang.String getMimeType(java.lang.String file) {
         String s = file.toLowerCase();
         int i = s.lastIndexOf('.');
         if(i >= 0) {
            s = s.substring(i);
            return _props.getProperty("content.type"+s);
         }
         return null;
      }
      
      public int getMinorVersion() {
         return 0;
      }
      
      public RequestDispatcher getNamedDispatcher(java.lang.String name) {
         _debug("getNameDispatcher");
         return null;
      }
      
      public java.lang.String getRealPath(java.lang.String path) {
         _debug2("getRealPath(\""+path+"\")");
         if(path.indexOf("..") >= 0)
            return null;
         String f = _docRoot + path;
         File ff = new File(f);
         _debug2("ff="+ff.getAbsolutePath());
         if(ff.exists())
            return f;
         else
            return null;
      }
      
      public RequestDispatcher getRequestDispatcher(java.lang.String path) {
         _debug("getRequestDispatcher");
         return null;
      }
      
      public java.net.URL getResource(java.lang.String path) {
         _debug2("getResource");
         String file = _translatePath(path);
         _debug2("translated="+file);
         if(file == null)
            return null;
         try {
            return new URL("file://"+file);
         }
         catch(java.net.MalformedURLException mue) {
            mue.printStackTrace();
            return null;
         }
      }
      
      public java.io.InputStream getResourceAsStream(java.lang.String path) {
         _debug2("getResourceAsStream(\""+path+"\")");
         String file = _translatePath(path);
         _debug2("translated="+file);
         if(file == null)
            return null;
         try {
            return new FileInputStream(file);
         }
         catch(IOException ioe) {
            ioe.printStackTrace();
            return null;
         }
      }
      
      public java.util.Set getResourcePaths(java.lang.String path) {
         _debug2("getResourcePaths");
         return new java.util.TreeSet();
      }
      
      public java.lang.String getServerInfo() {
         return VERSION;
      }
      
      public Servlet getServlet(java.lang.String name) {
         _debug2("getServlet");
         return null;
      }
      
      public java.lang.String getServletContextName() {
         _debug2("getServletContextName");
         return null;
      }
      
      public java.util.Enumeration getServletNames() {
         _debug2("getServletNames");
         return null;
      }
      
      public java.util.Enumeration getServlets() {
         _debug2("getServlets");
         return null;
      }
      
      public void log(java.lang.Exception exception, java.lang.String msg) {
         _servletLogFile.log(_servlet, exception, msg);
      }
      
      public void log(java.lang.String msg) {
         _servletLogFile.log(_servlet, msg);
      }
      
      public void log(java.lang.String message, java.lang.Throwable throwable) {
         _servletLogFile.log(_servlet, message, throwable);
      }
      
      public void removeAttribute(java.lang.String name) {
         _attrs.remove(name);
      }
      
      public void setAttribute(java.lang.String name, java.lang.Object object)  {
         _attrs.put(name, object);
      }
   }
   
   private class ConfigImpl implements ServletConfig {
      private ContextImpl _ctx;
      
      ConfigImpl(ContextImpl ctx) {
         _ctx = ctx;
      }
      
      public String getInitParameter(String name) {
         return _ctx.getInitParameter(name);
      }
      public Enumeration getInitParameterNames() {
         return _ctx.getInitParameterNames();
      }
      public ServletContext getServletContext() {
         return _ctx;
      }
      public String getServletName() {
         return _ctx.name();
      }
   }
   
   private class HttpSessionImpl implements HttpSession {
      private boolean _isNew;
      private Hashtable _attrs;
      private Date _creationDate;
      private String _id;
      private Date _lastAccessedDate;
      private int _maxInactiveInterval;
      private boolean _valid;
      private ServletContext _servletCtx;
      
      private SessionData _getSessionData() {
         SessionData sd = new SessionData();
         sd.isNew = _isNew;
         sd.attrs = _attrs;
         sd.creationDate = _creationDate;
         sd.id = _id;
         sd.lastAccessedDate = _lastAccessedDate;
         sd.maxInactiveInterval = _maxInactiveInterval;
         sd.valid = _valid;
         return sd;
      }
      
      private HttpSessionImpl(SessionData sd, ServletContext ctx) {
         _isNew = sd.isNew;
         _attrs = sd.attrs;
         _creationDate = sd.creationDate;
         _id = sd.id;
         _lastAccessedDate = sd.lastAccessedDate;
         _maxInactiveInterval = sd.maxInactiveInterval;
         _valid = sd.valid;
         _servletCtx = ctx;
      }
      
      private HttpSessionImpl(ServletContext ctx) {
         _servletCtx = ctx;
         _isNew = true;
         _attrs = new Hashtable();
         _creationDate = new Date();
         _lastAccessedDate = new Date();
         _maxInactiveInterval = -1;
         _valid = true;
         
         // Search for a unique ID
         Object ref;
         String from = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
         int index, randIndex, len = 20;
         StringBuffer key;
         do {
            key = new StringBuffer();
            for(index = 0; index < len; index++) {
               randIndex = ((int)(Math.random() * 1000.0)) % len;
               key.append(from.charAt(randIndex));
            }
            _id = key.toString();
            if(_sessionManagement) {
               if(new File(_sessionSaveDir + "/" + _id + ".dat").exists())
                  ref = null;
               else
                  break;
            }
            else {
               synchronized(_sessionTbl) {
                  ref = _sessionTbl.get(_id);
               }
            }
         }
         while(ref != null);
      }
      
      public java.lang.Object getAttribute(java.lang.String name)
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         return _attrs.get(name);
      }
      
      public java.util.Enumeration getAttributeNames()
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         return _attrs.keys();
      }
      
      public long getCreationTime()
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         return _creationDate.getTime();
      }
      
      public java.lang.String getId() {
         return _id;
      }
      
      public long getLastAccessedTime() {
         synchronized(this) {
            return _lastAccessedDate.getTime();
         }
      }
      
      public int getMaxInactiveInterval() {
         synchronized(this) {
            return _maxInactiveInterval;
         }
      }
      
      public ServletContext getServletContext() {
         return _servletCtx;
      }
      
      public HttpSessionContext getSessionContext() {
         return null;
      }
      
      public java.lang.Object getValue(java.lang.String name)
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         return _attrs.get(name);
      }
      
      public java.lang.String[] getValueNames()
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         String array[] = new String[_attrs.size()];
         Enumeration keys = _attrs.keys();
         int index = 0;
         while(keys.hasMoreElements()) {
            array[index] = keys.nextElement().toString();
            index++;
         }
         return array;
      }
      
      public void invalidate()
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
            _valid = false;
         }
      }
      
      public boolean isNew() {
         return _isNew;
      }
      
      public void putValue(java.lang.String name, java.lang.Object value)
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         _attrs.put(name, value);
      }
      
      public void removeAttribute(java.lang.String name)
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         _attrs.remove(name);
      }
      
      public void removeValue(java.lang.String name)
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         _attrs.remove(name);
      }
      
      public void setAttribute(java.lang.String name, java.lang.Object value)
      throws IllegalStateException {
         synchronized(this) {
            if(!_valid)
               throw new IllegalStateException("Session is invalid");
         }
         _attrs.put(name, value);
      }
      
      public void setMaxInactiveInterval(int interval) {
         synchronized(this) {
            _maxInactiveInterval = interval;
         }
      }
   }
      
   private class RequestImpl implements HttpServletRequest {
      private Socket _client;
      private LinkedList _headerList;
      private String _method;
      private String _mMethod;
      private String _path;
      private Cookie _cookieArray[] = null;
      private String _queryString;
      private Vector _paramVec = new Vector();
      private HashMap _paramMap = new HashMap();
      private String _contentType;
      private int _contentLength = -1;
      private BufferedReader _br;
      private java.io.StringBufferInputStream _sbi = null;
      private Hashtable _attrs = new Hashtable();
      private String _newSessionID = null;
      private HttpSessionImpl _newSession = null;
      
      public class InputStream extends ServletInputStream {
         private int _nleft = _contentLength;
         
         public int read() 
         throws IOException {
            if(_sbi == null) {
               if(_nleft <= 0)
                  return -1;
               _nleft--;
               int ch = _br.read();
               return ch;
               //return _br.read();
            }
            return _sbi.read();
         }
      }
      
      private class Parameter {
         String name;
         String value;
         
         Parameter(String name, String value) {
            this.name = name;
            this.value = value;
         }
      }
      
      RequestImpl(Socket client, LinkedList headerList, String method, 
              String path, String queryString, BufferedReader br)
      throws IOException {
         _br = br;
         _queryString = queryString;
         _client = client;
         _headerList = headerList;
         _method = method;
         _path = path;
         for(int i = 0; i < _method.length(); i++) {
            if(Character.isWhitespace(_method.charAt(i))) {
               _mMethod = _method.substring(0, i);
               break;
            }
         }
         String cookieString = _headerValue(_headerList, "Cookie");
         if(cookieString != null) {
            Cookie cookie;
            StringTokenizer sTok = new StringTokenizer(cookieString, ";");
            String c, name, value;
            int i;
            LinkedList cookieList = new LinkedList();
            while(sTok.hasMoreTokens()) {
               c = sTok.nextToken();
               i = c.indexOf('=');
               if(i >= 0) {
                  name = c.substring(0, i);
                  name = name.trim();
                  if((i+1) < c.length())
                     value = c.substring(i+1);
                  else
                     value = "";
                  _debug("COOKIE("+name+","+value+")");
                  cookie = new Cookie(URLDecoder.decode(name), URLDecoder.decode(value));
                  cookieList.add(cookie);
               }
            }
            if(cookieList.size() > 0) {
               _cookieArray = new Cookie[cookieList.size()];
               for(i = 0; i < cookieList.size(); i++)
                  _cookieArray[i] = (Cookie)cookieList.get(i);
            }
         }
                  
         if(_queryString != null)
            _parseFormData(_queryString);
         
         String temp = _headerValue(_headerList, "Content-Length");
         if(temp != null)
            _contentLength = Integer.parseInt(temp);
         else if(_queryString != null)
            _contentLength = _queryString.length();
         
         _contentType = _headerValue(_headerList, "Content-Type");
         if(_contentType != null) {
            if(_contentType.compareTo("application/x-www-form-urlencoded") == 0
               && _mMethod.equalsIgnoreCase("POST")
               && _contentLength != -1) {
               char buffer[] = new char[_contentLength];
               StringBuffer buf = new StringBuffer();
               int n = -1, nn = 0;
               while((nn < _contentLength) && ((n = _br.read(buffer)) > 0)) {
                  nn += n;
                  _debug("READ: " + new String(buffer, 0, n));
                  buf.append(new String(buffer, 0, n));
               }
               _sbi = new StringBufferInputStream(buf.toString());
               _parseFormData(buf.toString());
            }
         }
      }

      private void _parseFormData(String data) {
         StringTokenizer sTok = new StringTokenizer(data, ";&");
         String tok, name, value;
         Parameter param;
         LinkedList l;
         int i;
         while(sTok.hasMoreTokens()) {
            tok = sTok.nextToken();
            i = tok.indexOf('=');
            if(i >= 0) {
               name = tok.substring(0, i);
               i++;
               if(i < tok.length()) 
                  value = tok.substring(i);
               else
                  value = "";
               name = URLDecoder.decode(name);
               value = URLDecoder.decode(value);
               param = new Parameter(name, value);
               _paramVec.addElement(param);
               l = (LinkedList)_paramMap.get(name);
               if(l == null) {
                  l = new LinkedList();
                  l.add(param);
                  _paramMap.put(name, l);
               }
               else {
                  l.add(param);
                  _paramMap.put(name, l);
               }
            }
         }
      }
      
      // HttpServletRequest
      public java.lang.String getAuthType() {
         _debug2("getAuthType()");
         return null;
      }
      
      public java.lang.String getContextPath() {
         _debug2("getContextPath()");
         return "";
      }
      
      public Cookie[] getCookies() {
         _debug2("getCookies()");
         return _cookieArray;
      }
      
      public long getDateHeader(java.lang.String name) 
      throws IllegalArgumentException {
         _debug2("getDateHeader("+name+")");
         String value = _headerValue(_headerList, name);
         if(value == null)
            return -1;
         try {
            return _dateFormat.parse(value).getTime();
         }
         catch(Throwable t) {
            throw new IllegalArgumentException("Unable to parse date header");
         }
      }
      
      public java.lang.String getHeader(java.lang.String name) {
         _debug2("getHeader("+name+")");
         return _headerValue(_headerList, name);
      }
      
      public java.util.Enumeration getHeaderNames() {
         _debug2("getHeaderNames()");
         Vector v = new Vector();
         Iterator it = _headerList.iterator();
         int i;
         String h;
         while(it.hasNext()) {
            h = it.next().toString();
            i = h.indexOf(':');
            if(i >= 0) 
               v.addElement(h.substring(0,i));
         }
         return v.elements();
      }
      
      public java.util.Enumeration getHeaders(java.lang.String name) {
         _debug2("getHeaders("+name+")");
         Vector v = new Vector();
         Iterator it = _headerList.iterator();
         int i;
         String h;
         while(it.hasNext()) {
            h = it.next().toString();
            i = h.indexOf(':');
            if(i >= 0) {
               if(!h.substring(0,i).equalsIgnoreCase(name))
                  continue;
               i++;
               while(i < h.length() && Character.isWhitespace(h.charAt(i)))
                  i++;
               if(i < h.length()) {
                  h = h.substring(i);
                  v.addElement(h);
               }
            }
         }
         return v.elements();
      }
      
      public int getIntHeader(java.lang.String name) {
         _debug2("getHeader("+name+")");
         return Integer.parseInt(_headerValue(_headerList, name));
      }
      
      public java.lang.String getMethod() {
         _debug2("getMethod()");
         return _mMethod;
      }
      
      public java.lang.String getPathInfo() {
         _debug2("getPathInfo()");
         return "";
      }
      
      public java.lang.String getPathTranslated() {
         _debug2("getPathTranslated()");
         return _translatePath(_path);
      }
      
      public java.lang.String getQueryString() {
         _debug2("getQueryString()");
         return  _queryString;
      }
      
      public java.lang.String getRemoteUser() {
         String auth = getHeader("Authorization");
         if(auth == null || auth.length() < 6)
            return null;
         auth = auth.substring(6);
         BASE64Decoder b64d = new BASE64Decoder();
         String up;
         try {
            up = new String(b64d.decodeBuffer(auth));
         }
         catch(IOException ioe) {
            return null;
         }
         int i = up.indexOf(':');
         if(i >= 0)
            return up.substring(0, i);
         return null;
      }
      
      public java.lang.String getRequestedSessionId() {
         _debug2("getRequestedSessionId()");
         return _getSessionId();
      }
      
      public java.lang.String getRequestURI() {
         _debug2("getRequestURI("+_path+")");
         return _path;
      }
      
      public java.lang.StringBuffer getRequestURL() {
         _debug2("getRequestURL()");
         return null;
      }
      
      public java.lang.String getServletPath() {
         _debug2("getServletPath()");
         return _path;
      }
      
      private String _getSessionId() {
         String id = null;
         Cookie c[] = getCookies();
         String sid = _props.getProperty("session.cookie.id");
         if(c != null) {
            for(int i = 0; i < c.length; i++) {
               if(c[i].getName().compareTo(sid) == 0) {
                  id = c[i].getValue();
                  break;
               }
            }
         }
         return id;
      }
      
      public HttpSession getSession() {
         _debug2("getSession()");
         HttpSessionImpl ret;
         if(_newSession != null)
            return _newSession;
         String id = _getSessionId();
         if(id == null)
            return null;
         if(_sessionManagement) {
            File f = new File(_sessionSaveDir + "/" + id + ".dat");
            if(!f.exists())
               ret = null;
            else {
               try {
                  ReadWriteLock rwLock;
                  synchronized(_sessionReadWriteLockTbl) {
                     rwLock = (ReadWriteLock)_sessionReadWriteLockTbl.get(id);
                     if(rwLock == null) {
                        rwLock = new ReadWriteLock();
                        _sessionReadWriteLockTbl.put(id, rwLock);
                     }
                  }
                  rwLock.readLock();
                  try {
                     FileInputStream fin = new FileInputStream(f);
                     BufferedInputStream bin = new BufferedInputStream(fin);
                     ObjectInputStream oin = new ObjectInputStream(bin);
                     Object o = oin.readObject();
                     fin.close();
                     if(o == null)
                        ret = null;
                     else if(o instanceof SessionData) {
                        ret = new HttpSessionImpl((SessionData)o, _findServlet(_path).getServletConfig().getServletContext());
                        _newSession = ret;
                        _newSessionID = ret._id;
                     }
                     else
                        ret = null;
                  }
                  finally {
                     rwLock.readUnlock();
                  }
               }
               catch(Throwable th) {
                  _servletLogFile.log(null, "error reading session from file!", th);
                  ret = null;
               }
            }
         }
         else {
            synchronized(_sessionTbl) {
               ret = (HttpSessionImpl)_sessionTbl.get(id);
            }
         }
         if(ret != null) {
            synchronized(ret) {
               if(!ret._valid)
                  return null;
            }
         }
         return ret;
      }
      
      public HttpSession getSession(boolean create) {
         _debug2("getSession("+create+")");
         HttpSessionImpl ret = (HttpSessionImpl)getSession();
         if(ret == null) {
            Servlet servlet;
            try {
               servlet = _findServlet(_path);
            }
            catch(Throwable t) {
               servlet = _fileServlet;
            }
            ret = new HttpSessionImpl(servlet.getServletConfig().getServletContext());
            _newSessionID = ret.getId();
            _newSession = ret;
            ReadWriteLock rwLock = new ReadWriteLock();
            synchronized(_sessionReadWriteLockTbl) {
               _sessionReadWriteLockTbl.put(_newSessionID, rwLock);
            }
            if(!_sessionManagement) {
               synchronized(_sessionTbl) {
                  _sessionTbl.put(ret.getId(), ret);
               }
            }
         }
         return ret;
      }
            
      public java.security.Principal getUserPrincipal() {
         _debug2("getUserPrincipal()");
         return null;
      }
      
      public boolean isRequestedSessionIdFromCookie() {
         _debug2("isRequestedSessionIdFromCookie()");
         return true;
      }
      
      public boolean isRequestedSessionIdFromUrl() {
         _debug2("isRequestedSessionIdFromUrl()");
         return false;
      }
      
      public boolean isRequestedSessionIdFromURL() {
         _debug2("isRequestedSessionIdFromURL()");
         return false;
      }
      
      public boolean isRequestedSessionIdValid() {
         _debug2("isRequestedSessionIdValid()");
         String id = _getSessionId();
         if(id == null)
            return false;
         return true;
      }
      
      public boolean isUserInRole(java.lang.String role) {
         _debug2("isUserInRole("+role+")");
         return false;
      }
      
      // ServletRequest
      public java.lang.Object getAttribute(java.lang.String name) {
         _debug2("getAttribute("+name+")");
         return _attrs.get(name);
      }
      
      public java.util.Enumeration getAttributeNames() {
         _debug2("getAttributeNames()");
         return _attrs.keys();
      }
      
      public java.lang.String getCharacterEncoding() {
         _debug2("getCharacterEncoding()");
         return null;
      }
      
      public int getContentLength() {
         _debug2("getContentLength()");
         return _contentLength;
      }
      
      public java.lang.String getContentType() {
         _debug2("getContentType()");
         return _contentType;
      }
      
      public ServletInputStream getInputStream() {
         _debug2("getInputStream()");
         return new InputStream();
      }
      
      public java.util.Locale getLocale() {
         _debug2("getLocale()");
         return null;
      }
      
      public java.util.Enumeration getLocales() {
         _debug2("getLocales()");
         return null;
      }
      
      public java.lang.String getParameter(java.lang.String name) {
         _debug2("getParameter("+name+")");
         LinkedList l = (LinkedList)_paramMap.get(name);
         if(l == null)
            return null;
         if(l.size() == 0)
            return null;
         return ((Parameter)(l.get(0))).value;
      }
      
      public java.util.Map getParameterMap() {
         _debug2("getParameterMap()");
         HashMap map = new HashMap();
         Set s = _paramMap.keySet();
         Iterator it = s.iterator();
         LinkedList l;
         String key;
         Parameter p;
         while(it.hasNext()) {
            key = it.next().toString();
            l = (LinkedList)_paramMap.get(key);
            if(l != null) {
               if(l.size() > 0) {
                  p = (Parameter)l.get(0);
                  map.put(key, p.value);
               }
            }
         }
         return map;
      }
      
      public java.util.Enumeration getParameterNames() {
         _debug2("getParameterNames()");
         Vector v = new Vector();
         for(int i = 0; i < _paramVec.size(); i++)
            v.addElement(((Parameter)(_paramVec.elementAt(i))).name);
         return v.elements();
      }
      
      public java.lang.String[] getParameterValues(java.lang.String name) {
         _debug2("getParameterValues("+name+")");
         LinkedList l = (LinkedList)_paramMap.get(name);
         if(l == null)
            return null;
         if(l.size() == 0)
            return null;
         String array[] = new String[l.size()];
         for(int i = 0; i < l.size(); i++)
            array[i] = l.get(i).toString();
         return array;
      }
      
      public java.lang.String getProtocol() {
         _debug2("getProtocol()");
         if(isSecure())
            return "https";
         return "http";
      }
      
      public java.io.BufferedReader getReader() {
         _debug2("getReader()");
         return null;
      }
      
      public java.lang.String getRealPath(java.lang.String path) {
         _debug2("getRealPath("+path+")");
         return _translatePath(path);
      }
      
      public java.lang.String getRemoteAddr() {
         _debug2("getRemoteAddr()");
         return _client.getInetAddress().getHostAddress();
      }
      
      public java.lang.String getRemoteHost() {
         _debug2("getRemoteHost()");
         return _client.getInetAddress().getHostName();
      }
      
      public RequestDispatcher getRequestDispatcher(java.lang.String path) {
         _debug2("getRequestDispatcher("+path+")");
         return null;
      }
      
      public java.lang.String getScheme() {
         _debug2("getScheme()");
         if(isSecure())
            return "https";
         return "http";
      }
      
      public java.lang.String getServerName() {
         _debug2("getServerName()");
         return _svr.getInetAddress().getHostName();
      }
      
      public int getServerPort() {
         _debug2("getServerPort()");
         return _svr.getLocalPort();
      }
      
      public boolean isSecure() {
         _debug2("isSecure()");
         return (_props.getProperty("server.type").compareTo("TLS") == 0);
      }
      
      public void removeAttribute(java.lang.String name) {
         _debug2("removeAttribute("+name+")");
         _attrs.remove(name);
      }
      
      public void setAttribute(java.lang.String name, java.lang.Object o) {
         _debug2("setAttribute("+name+","+o.toString()+")");
         _attrs.put(name, o);
      }
      
      public void setCharacterEncoding(java.lang.String env)  {
         _debug2("setCharacterEncoding("+env+")");
      }
   }
   
   private class ResponseImpl implements HttpServletResponse {
      private Socket _client;
      private String _contentType = _props.getProperty("server.default.content.type");
      private boolean _isCommitted = false;
      private LinkedList _headerList = new LinkedList();
      private int _status = HttpServletResponse.SC_OK;
      private String _statusMessage = "OK";
      private LinkedList _cookieList = new LinkedList();
      private Locale _loc = Locale.getDefault();
      private java.io.OutputStream _os;
      private OutputStream _out;
      private String _location = null;
      private RequestImpl _req;
      
      private void _setCookies(PrintWriter out) {
         if(_cookieList.size() > 0) {
            Iterator it = _cookieList.iterator();
            Cookie c;
            StringBuffer buf;
            while(it.hasNext()) {
               c = (Cookie)it.next();
               buf = new StringBuffer();
               buf.append("Set-Cookie: ");
               buf.append(URLEncoder.encode(c.getName())+"="+URLEncoder.encode(c.getValue()));
               if(c.getDomain() != null)
                  buf.append(";domain="+c.getDomain());
               if(c.getMaxAge() != -1) {
                  GregorianCalendar gc = new GregorianCalendar();
                  gc.add(Calendar.SECOND, c.getMaxAge());
                  buf.append(";expires="+_cookieDateFormat.format(gc.getTime()));
               }
               if(c.getPath() != null)
                  buf.append(";path="+c.getPath());
               if(c.getSecure())
                  buf.append("secure");
               _debug("WRITE: "+buf.toString());
               buf.append("\r\n");
               out.print(buf.toString());
               out.flush();
            }
         }
      }
      
      protected void _writeHeaders() 
      throws IOException {
         HttpSessionImpl session = (HttpSessionImpl)_req.getSession();
         if(session != null) {
            session._isNew = false;
            session._lastAccessedDate = new Date();
            String s = _props.getProperty("session.cookie.id");
            Cookie c = new Cookie(s, session.getId());
            if(session.getMaxInactiveInterval() != -1)
               c.setMaxAge(session.getMaxInactiveInterval());
            addCookie(c);
            if(_sessionManagement) {
               
               ReadWriteLock rwLock;
               synchronized(_sessionReadWriteLockTbl) {
                  rwLock = (ReadWriteLock)_sessionReadWriteLockTbl.get(session._id);
                  if(rwLock == null) {
                     rwLock = new ReadWriteLock();
                     _sessionReadWriteLockTbl.put(session._id, rwLock);
                  }
               }
               rwLock.writeLock();
               try {
                  File f = new File(_sessionSaveDir + "/" + session._id + ".dat");
                  FileOutputStream fout = new FileOutputStream(f);
                  BufferedOutputStream bout = new BufferedOutputStream(fout);
                  ObjectOutputStream oout = new ObjectOutputStream(bout);
                  oout.writeObject(session._getSessionData());
                  oout.flush();
                  fout.close();
               }
               catch(Throwable th) {
                  _servletLogFile.log(null, "error writeing session save file!", th);
               }
               finally {
                  rwLock.writeUnlock();
               }
            }
         }

         PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(_os)));
         _debug("WRITE: " + "HTTP/1.0 "+_status+" "+_statusMessage);
         out.print("HTTP/1.0 "+_status+" "+_statusMessage+"\r\n");
         Iterator it = _headerList.iterator();
         String h;
         while(it.hasNext()) {
            h = it.next().toString();
            _debug("WRITE: " + h);
            out.print(h+"\r\n");
            out.flush();
         }
         _setCookies(out);
         if(_location == null && _req.getHeader("Content-Type") == null) {
            _debug("WRITE: "+ "Content-Type: " + _contentType + "\r\n");
            out.print("Content-Type: " + _contentType + "\r\n\r\n");
         }
         else {
            _debug("Location: "+ _location + "\r\n");
            out.print("Location: " + _location + "\r\n\r\n");
         }
         out.flush();
      }
      
      private class OutputStream extends ServletOutputStream {
         LinkedList _charList = new LinkedList();
                                                      
         public void write(int b)
         throws IOException {
            _charList.add(new Integer(b));
         }
         
         public void flush()
         throws IOException {
            if(!_isCommitted) {
               _isCommitted = true;
               _writeHeaders();
            }
            Iterator it = _charList.iterator();
            Integer ii;
            byte buf[] = new byte[_charList.size()];
            int i = 0;
            while(it.hasNext()) {
               ii = (Integer)it.next();
               buf[i] = (byte)ii.intValue();
               i++;
            }
            _os.write(buf);
            _os.flush();
            _charList = new LinkedList();
         }
      }
      
      ResponseImpl(Socket client, RequestImpl req)
      throws IOException {
         _req = req;
         _client = client;
         _os = _client.getOutputStream();
         _out = new OutputStream();
         addHeader("Server", VERSION);
         addDateHeader("Date", new java.util.Date().getTime());
      }
      
      // HttpServletResponse
      public void addCookie(Cookie cookie) {
         _cookieList.add(cookie);
      }
      
      public void addDateHeader(java.lang.String name, long date) {
         _headerList.add(name+": "+_dateFormat.format(new java.util.Date(date)));
      }
      
      public void addHeader(java.lang.String name, java.lang.String value) {
         _headerList.add(name+": "+value);
      }
      
      public void addIntHeader(java.lang.String name, int value) {
         _headerList.add(name+": "+value);
      }
      
      public boolean containsHeader(java.lang.String name) {
         return (_headerValue(_headerList, name) != null);
      }
      
      public java.lang.String encodeRedirectUrl(java.lang.String url) {
         return null;
      }
      
      public java.lang.String encodeRedirectURL(java.lang.String url) {
         return null;
      }
      
      public java.lang.String encodeUrl(java.lang.String url) {
         return null;
      }
      
      public java.lang.String encodeURL(java.lang.String url) {
         return null;
      }
      
      public void sendError(int sc) 
      throws IOException, IllegalStateException {
         if(this._isCommitted)
            throw new IllegalStateException("Response has been committed");
         setStatus(sc);
         setContentType("text/html");
         TemplateWebPage errorPage = new TemplateWebPage(_errorPage);
         errorPage.expandTemplate("#error.code#", sc+"");
         errorPage.expandTemplate("#error.message#", _statusMessage);
         errorPage.expandTemplate("#optional.message#", "");
         errorPage.sendTo(this.getWriter());
      }
      
      public void sendError(int sc, java.lang.String msg)
      throws IOException, IllegalStateException {
         if(this._isCommitted)
            throw new IllegalStateException("Response has been committed");
         setStatus(sc);
         setContentType("text/html");
         TemplateWebPage errorPage = new TemplateWebPage(_errorPage);
         errorPage.expandTemplate("#error.code#", sc+"");
         errorPage.expandTemplate("#error.message#", _statusMessage);
         errorPage.expandTemplate("#optional.message#", msg);
         errorPage.sendTo(this.getWriter());
      }
      
      public void sendRedirect(java.lang.String location)
      throws IOException {
         setStatus(HttpServletResponse.SC_MULTIPLE_CHOICES);
         _location = location;
         this.getWriter().flush();
      }
      
      public void setDateHeader(java.lang.String name, long date) {
         Iterator it = _headerList.iterator();
         String h;
         int i, index = 0;
         boolean found = false;
         while(it.hasNext()) {
            h = it.next().toString();
            i = h.indexOf(':');
            if(i >= 0) {
               h = h.substring(0, i);
               if(name.equalsIgnoreCase(h)) {
                  _headerList.set(index, name + ": " + _dateFormat.format(new java.util.Date(date)));
                  found = true;
                  break;
               }
            }
            index++;
         }
         if(!found)
            addDateHeader(name, date);
      }
      
      public void setHeader(java.lang.String name, java.lang.String value) {
         Iterator it = _headerList.iterator();
         String h;
         int i, index = 0;
         boolean found = false;
         while(it.hasNext()) {
            h = it.next().toString();
            i = h.indexOf(':');
            if(i >= 0) {
               h = h.substring(0, i);
               if(name.equalsIgnoreCase(h)) {
                  _headerList.set(index, name + ": " + value);
                  found = true;
                  break;
               }
            }
            index++;
         }
         if(!found)
            addHeader(name, value);
      }
      
      public void setIntHeader(java.lang.String name, int value) {
         Iterator it = _headerList.iterator();
         String h;
         int i, index = 0;
         boolean found = false;
         while(it.hasNext()) {
            h = it.next().toString();
            i = h.indexOf(':');
            if(i >= 0) {
               h = h.substring(0, i);
               if(name.equalsIgnoreCase(h)) {
                  _headerList.set(index, name + ": " + value);
                  found = true;
                  break;
               }
            }
            index++;
         }
         if(!found)
            addIntHeader(name, value);
      }
      
      public void setStatus(int sc) {
         _status = sc;
         switch(_status) {
         case HttpServletResponse.SC_OK:
            _statusMessage = "OK";
            break;

         case HttpServletResponse.SC_CREATED:
            _statusMessage = "Created";
            break;

         case HttpServletResponse.SC_ACCEPTED:
            _statusMessage = "Accepted";
            break;

         case HttpServletResponse.SC_NO_CONTENT:
            _statusMessage = "No Content";
            break;
            
         case HttpServletResponse.SC_MULTIPLE_CHOICES:
            _statusMessage = "Multiple Choices";
            break;
            
         case HttpServletResponse.SC_MOVED_PERMANENTLY:
            _statusMessage = "Moved Permanently";
            break;
            
         case HttpServletResponse.SC_MOVED_TEMPORARILY:
            _statusMessage = "Moved Temporarily";
            break;

         case HttpServletResponse.SC_NOT_MODIFIED:
            _statusMessage = "Not Modified";
            break;
            
         case HttpServletResponse.SC_BAD_REQUEST:
            _statusMessage = "Bad Request";
            break;
            
         case HttpServletResponse.SC_UNAUTHORIZED:
            _statusMessage = "Unautorized";
            break;

         case HttpServletResponse.SC_FORBIDDEN:
            _statusMessage = "Forbidden";
            break;
            
         case HttpServletResponse.SC_NOT_FOUND:
            _statusMessage = "Not Found";
            break;
            
         case HttpServletResponse.SC_INTERNAL_SERVER_ERROR:
            _statusMessage = "Internal Server Error";
            break;
            
         case HttpServletResponse.SC_NOT_IMPLEMENTED:
            _statusMessage = "Not Implemented";
            break;

         case HttpServletResponse.SC_BAD_GATEWAY:
            _statusMessage = "Bad Gateway";
            break;

         case HttpServletResponse.SC_SERVICE_UNAVAILABLE:
            _statusMessage = "Service Unavailable";
            break;
            
         default:
            _statusMessage = "OK";
         }
      }
      
      public void setStatus(int sc, java.lang.String sm) {
         setStatus(sc);
      }
      
      // ServletResponse
      public void flushBuffer()
      throws IOException {
         _out.flush();
      }
      
      public int getBufferSize() {
         return _out._charList.size();
      }
       
      public java.lang.String getCharacterEncoding() {
         return null;
      }
      
      public java.util.Locale getLocale() {
         return _loc;
      }
      
      public ServletOutputStream getOutputStream() 
      throws IOException {
         if(this._isCommitted)
            throw new IllegalStateException("Response has been committed");
         return _out;
      }
      
      public java.io.PrintWriter getWriter() 
      throws IOException {
         if(this._isCommitted)
            throw new IllegalStateException("Response has been committed");
         return new PrintWriter(new OutputStreamWriter(_out));
      }
      
      public boolean isCommitted() {
         return _isCommitted;
      }
      
      public void reset() {
         _status = HttpServletResponse.SC_OK;
         _statusMessage = "OK";
         _headerList = new LinkedList();
         _contentType = _props.getProperty("server.default.content.type");
         _cookieList = new LinkedList();
      }
      
      public void resetBuffer() {
      }
      
      public void setBufferSize(int size) {
      }
      
      public void setContentLength(int len) {
         setIntHeader("Content-Length", len);
      }
      
      public void setContentType(java.lang.String type) {
         _contentType = type;
      }
      
      public void setLocale(java.util.Locale loc) {
         _loc = loc;
      }
   }
   
   public httpd(String propFileName) 
   throws Exception {
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
      _dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
      _cookieDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
      _servletLastMod = new Hashtable();
      _propFileName = propFileName;
      FileInputStream fin = new FileInputStream(propFileName);
      _props = new Properties();
      _props.load(fin);
      fin.close();
      _maxHeaders = Integer.parseInt(_props.getProperty("server.max.headers"));
      _timeout = Integer.parseInt(_props.getProperty("server.timeout"));
      _docRoot = _props.getProperty("server.document.root");
      _debugMode = Boolean.valueOf(_props.getProperty("server.debug.mode")).booleanValue();
      _debugMode2 = Boolean.valueOf(_props.getProperty("server.debug2.mode")).booleanValue();
      if(_props.getProperty("server.session.management").compareTo("file") == 0)
         _sessionManagement = true;
      else
         _sessionManagement = false;
      _sessionSaveDir = _props.getProperty("server.session.save.dir");
      _servletBindingArray = new ServletBinding[Integer.parseInt(_props.getProperty("servlet.n"))];
      for(int i = 1; i <= _servletBindingArray.length; i++) {
         _servletBindingArray[i-1] = new ServletBinding(_props.getProperty("servlet."+i+".pattern"),
                                                        _props.getProperty("servlet."+i+".class"));
      }
      ServerSocketFactory ssf = _getServerSocketFactory(_props.getProperty("server.type"));
      String addr = _props.getProperty("server.ifAddress");
      if(addr.compareTo("ANY") == 0)
         _svr = ssf.createServerSocket(Integer.parseInt(_props.getProperty("server.port")),
                                       Integer.parseInt(_props.getProperty("server.backlog")));
      else
         _svr = ssf.createServerSocket(Integer.parseInt(_props.getProperty("server.port")),
                                       Integer.parseInt(_props.getProperty("server.backlog")),
                                       InetAddress.getByName(addr));
      _errorPage = new TemplateWebPage();
      _errorPage.loadTemplate(_props.getProperty("server.error.page"));
      _servletTable = new Hashtable();
      _servletLogFile = new LogFile(_props.getProperty("server.servlet.log.file"));
      _fileServlet = new FileServlet();
      _fileServlet.init(new ConfigImpl(new ContextImpl(_fileServlet.getClass().getName(), null)));
      _loadFilters();
   }
   
   public class FilterConfigImpl implements FilterConfig {
      private String _filterName;
      private String _pattern;
      private String _className;
      
      FilterConfigImpl(String filterName, String pattern, String className) {
         _filterName = filterName;
         _pattern = pattern;
         _className = className;
      }
      
      public java.lang.String getFilterName() {
         return _filterName;
      }
      
      public java.lang.String getInitParameter(java.lang.String name) {
         return null;
      }
      
      public java.util.Enumeration getInitParameterNames() {
         return null;
      }
      
      public ServletContext getServletContext() {
         return new ContextImpl(_className, _props.getProperty(_className+".initParameters"));
      }
   }
   
   private class FilterChainImpl implements FilterChain {
      private LinkedList _filterList;
      private Iterator _it;
      private String _path;
      
      FilterChainImpl(LinkedList filterList, String path) {
         _filterList = filterList;
         _path = path;
         _it = _filterList.iterator();
      }
      
      public void doFilter(ServletRequest request, ServletResponse response)
      throws ServletException, IOException {
         if(_it.hasNext()) {
            _debug("FILTER NEXT");
            Filter f = (Filter)_it.next();
            f.doFilter(request, response, this);
            response.flushBuffer();
         }
         else {
            try {
               _debug("BOTTOM OF THE FILTER CHAIN CALLING A SERVLET!");
               _debug2("Find servlet next...");
               Servlet s = _findServlet(_path);
               _debug2("Back from call to findServlet...");
               if(s != null) {
                  _debug2("Calling service...");
                  s.service(request, response);
                  _debug2("Back from call to service...");
                  response.flushBuffer();
               }
            }
            catch(Exception ex) {
               ServletException se;
               Throwable t;
               if(ex instanceof ServletException) {
                  se = (ServletException)ex;
                  se.printStackTrace();
                  boolean stop = false;
                  while((t = se.getRootCause()) != null && !stop) {
                     if(t instanceof ServletException)
                        se = (ServletException)t;
                     else 
                        stop = true;
                     t.printStackTrace();
                  }
               }
               else
                  ex.printStackTrace();
               throw new ServletException(ex.getMessage(), ex);
            }
         }
      }
   }
   
   private void _loadFilters() 
   throws Exception {
      int n = Integer.parseInt(_props.getProperty("filter.n"));
      String name, pattern, className;
      Class c;
      Filter f;
      for(int i = 1; i <= n; i++) {
         name = _props.getProperty("filter."+i+".name");
         pattern = _props.getProperty("filter."+i+".pattern");
         className = _props.getProperty("filter."+i+".className");
         c = Class.forName(className);
         f = (Filter)c.newInstance();
         f.init(new FilterConfigImpl(name, pattern, className));
         _patternList.add(new RE(pattern));
         _filterList.add(f);
      }
   }
   
   private ServerSocketFactory _getServerSocketFactory(String type) 
   throws Exception {
      if(type.compareTo("TLS") == 0) {
         SSLServerSocketFactory ssf = null;
         SSLContext ctx;
         KeyManagerFactory kmf;
         KeyStore ks;
         char[] passphrase = _props.getProperty("server.keyfile.passphrase").toCharArray();

         ctx = SSLContext.getInstance("TLS");
         kmf = KeyManagerFactory.getInstance("SunX509");
         ks = KeyStore.getInstance("JKS");

         ks.load(new FileInputStream(_props.getProperty("server.keyfile")), passphrase);
         kmf.init(ks, _props.getProperty("server.key.passphrase").toCharArray());
         ctx.init(kmf.getKeyManagers(), null, null);

         ssf = ctx.getServerSocketFactory();
         return ssf;
      }
      else 
         return ServerSocketFactory.getDefault();
   }   
   
   private LinkedList _readHeaderList(Socket client, BufferedReader br)
   throws Exception {
      String line;
      LinkedList l = new LinkedList();
      while((line = br.readLine()) != null && l.size() < _maxHeaders) {
         _debug("READ: " + line);
         if(line.compareTo("") == 0)
            break;
         l.add(line);
      }
      return l;
   }
   
   private String _headerValue(LinkedList headerList, String headerName) {
      Iterator it = headerList.iterator();
      String header;
      int i;
      String name;
      String ret = null;
      while(it.hasNext()) {
         header = it.next().toString();
         i = header.indexOf(':');
         if(i >= 0) {
            name = header.substring(0, i);
            if(name.equalsIgnoreCase(headerName)) {
               i++;
               while(i < header.length() && Character.isWhitespace(header.charAt(i)))
                  i++;
               if(i < header.length())
                  ret = header.substring(i);
               else
                  ret = "";
               break;
            }
         }
      }
      
      return ret;
   }

   private String _getPath(String method) {
      int index = 0;
      while(index < method.length() && !Character.isWhitespace(method.charAt(index)))
         index++;
      while(index < method.length() && Character.isWhitespace(method.charAt(index)))
         index++;
      int index2 = index;
      while(index2 < method.length() && !Character.isWhitespace(method.charAt(index2)))
         index2++;
      return method.substring(index, index2);
   }
   
   private String _translatePath(String path) {
      if(path.indexOf("..") >= 0)
         return null;
      String f = _docRoot + path;
      File ff = new File(f);
      if(ff.isDirectory()) {
         if(f.charAt(f.length()-1) != '/')
            f += "/";
         f += _props.getProperty("server.default.document");
         ff = new File(f);
      }
      if(ff.exists())
         return f;
      else
         return null;
   }
      
   private Servlet _findServlet(String path)
   throws Exception {
      if(path.indexOf("/servlet/") == 0) {
         String servletName = path.substring("/servlet/".length());
         synchronized(_servletTable) {
            Servlet servlet = (Servlet)_servletTable.get(servletName);
            if(servlet == null) { 
               servlet = (Servlet)(Class.forName(servletName, false, new ServletClassLoader()).newInstance());
               servlet.init(new ConfigImpl(new ContextImpl(servletName, 
                                                   _props.getProperty(servletName+".initParameters"))));
               _servletTable.put(servletName, servlet);
               HttpServlet hs = (HttpServlet)servlet;
               ClassLoader cl = hs.getClass().getClassLoader();
               URL classURL = cl.getResource(servletName.replace('.', '/')+".class");
               long lm = new File(classURL.getFile()).lastModified();
               _servletLastMod.put(servletName, new Long(lm));
               return servlet;
            }
            else {
               HttpServlet hs = (HttpServlet)servlet;
               ServletClassLoader cl = new ServletClassLoader();
               URL classURL = cl.getResource(servletName.replace('.', '/')+".class");
               long lm = new File(classURL.getFile()).lastModified();
               Long llm = (Long)_servletLastMod.get(servletName);
               if(lm > llm.longValue()) {
                  _debug2("RELOAD "+servletName+" SERVLET CLASS FILE");
                  hs.destroy();
                  servlet = (Servlet)(cl.findClass(servletName).newInstance());
                  servlet.init(new ConfigImpl(new ContextImpl(servletName, 
                                                      _props.getProperty(servletName+".initParameters"))));
                  _servletTable.put(servletName, servlet);
                  _servletLastMod.put(servletName, new Long(lm));
               }
               return servlet;
            }
         }
      }
      else {
         for(int i = 0; i < _servletBindingArray.length; i++) {
            if(_servletBindingArray[i]._isMatch(path))
               return _findServlet("/servlet/" + _servletBindingArray[i]._className);
         }
         return _fileServlet;
      }
   }
      
   private LinkedList _getFilterList(String path) {
      LinkedList filterList = new LinkedList();
      Iterator it = _filterList.iterator();
      Iterator it2 = _patternList.iterator();
      Filter f;
      RE p;
      REMatch m;
      while(it.hasNext()) {
         f = (Filter)it.next();
         p = (RE)it2.next();
         if((m = p.getMatch(path)) != null) {
            if(m.getStartIndex() == 0
               && m.getEndIndex() == path.length())
               filterList.add(f);
         }
      }
      return filterList;
   }
   
   private void _handleClient(Socket client)
   throws Exception {
      String method, path, queryString;
      RequestImpl request;
      ResponseImpl response;
      Servlet servlet;
      int i;
      FilterChain chain;
      LinkedList filterList;
      
      // Set socket options
      client.setSoTimeout(_timeout);
      client.setSoLinger(false, 0);
      
      // Read header list
      InputStream in = client.getInputStream();
      BufferedReader br = new BufferedReader(new InputStreamReader(in));
      LinkedList headerList = _readHeaderList(client, br);
      
      if(headerList.size() > 0) {
         method = headerList.get(0).toString();
         path = _getPath(method);
         i = path.indexOf('?');
         queryString = null;
         if(i >= 0) {
            if((i+1) < path.length())
               queryString = path.substring(i+1);
            path = path.substring(0, i);
         }
         
         filterList = _getFilterList(path);
         request = new RequestImpl(client, headerList, 
                               method, path, queryString,
                               br);
         response = new ResponseImpl(client, request);
         if(filterList.size() != 0) {
            chain = new FilterChainImpl(filterList, path);            
            try {
               chain.doFilter(request, response);
               HttpSessionImpl session = (HttpSessionImpl)request.getSession();
               if(session != null) {
                  synchronized(session) {
                     session._lastAccessedDate = new Date();
                  }
               }
            }
            catch(Throwable filterError) {
               filterError.printStackTrace();
               _servletLogFile.log("httpd", "Filter exception", filterError);
               if(!response._isCommitted) {
                  response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                     path + ": Filter exception.  Check log file.");
               }
            }
         }
         else {
            servlet = _findServlet(path);
            if(servlet != null) {
               try {
                  servlet.service(request, response);
                  response.flushBuffer();
                  HttpSessionImpl session = (HttpSessionImpl)request.getSession();
                  if(session != null) {
                     synchronized(session) {
                        session._lastAccessedDate = new Date();
                     }
                  }
               }
               catch(Throwable servletError) {
                  servletError.printStackTrace();
                  _servletLogFile.log("httpd", "Servlet exception", servletError);
                  if(!response._isCommitted) {
                     response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                        path + ": Servlet exception.  Check log file.");
                  }
               }
            }
            else {
               response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                  path + ": No such servlet");
            }
         }
      }
   }
   
   public void run() {
      AccepterThread at;
      
      _debug2("httpd.run()");
      
      // Create the thread pools
      _threadPool = new ThreadPool(Integer.parseInt(_props.getProperty("server.min.handler.thread.pool.size")),
                                   Integer.parseInt(_props.getProperty("server.max.handler.thread.pool.size")),
                                   new Long(_props.getProperty("server.handler.thread.timeout")).longValue(),
                                   Integer.parseInt(_props.getProperty("server.connection.queue.max.size")));
      _accepterThreadArray = new AccepterThread[Integer.parseInt(_props.getProperty("server.accept.thread.pool.size"))];
      for(int i = 0; i < _accepterThreadArray.length; i++) {
         at = new AccepterThread();
         _accepterThreadArray[i] = at;
         at.start();
      }
      
      int sleepN = Integer.parseInt(_props.getProperty("server.run.clean.thread.every"));
      boolean printStats = Boolean.valueOf(_props.getProperty("server.print.thread.pool.stats")).booleanValue();
      long dif;
      
      while(_isRunning()) {
         try {
            Thread.sleep(sleepN * 1000);
         }
         catch(InterruptedException ie) {
            ;
         }
         
         // Clean up any invalid sessions
         if(_sessionManagement) {
            File dir = new File(_sessionSaveDir);
            String id;
            String sessionFileArray[] = dir.list();
            ReadWriteLock rwLock;
            int index;
            for(int i = 0; i < sessionFileArray.length; i++) {
               File f = new File(_sessionSaveDir + "/" + sessionFileArray[i]);
               if(f.isDirectory())
                  continue;
               id = sessionFileArray[i];
               index = id.indexOf('.');
               if(index < 0)
                  continue;
               id = id.substring(0, index);
               _debug2("CHECKING VALIDITY OF SESSION ID="+id);
               synchronized(_sessionReadWriteLockTbl) {
                  rwLock = (ReadWriteLock)_sessionReadWriteLockTbl.get(id);
                  if(rwLock == null) {
                     rwLock = new ReadWriteLock();
                     _sessionReadWriteLockTbl.put(id, rwLock);
                  }
               }
               try {
                  rwLock.readLock();
                  SessionData sd;
                  try {
                     FileInputStream fin = new FileInputStream(f);
                     BufferedInputStream bin = new BufferedInputStream(fin);
                     ObjectInputStream oin = new ObjectInputStream(bin);
                     sd = (SessionData)oin.readObject();
                     fin.close();
                  }
                  finally {
                     rwLock.readUnlock();
                  }
                  dif = (new Date()).getTime() - sd.lastAccessedDate.getTime();
                  dif /= 1000;
                  if(dif >= sd.maxInactiveInterval) {
                     _debug2("Invalidation of session id="+sd.id);
                     rwLock.writeLock();
                     try {
                        f.delete();
                        synchronized(_sessionReadWriteLockTbl) {
                           _sessionReadWriteLockTbl.remove(id);
                        }
                     }
                     finally {
                        rwLock.writeUnlock();
                     }
                  }
               }
               catch(Throwable th) {
                  _servletLogFile.log(null, "Unable to read session file (" + _sessionSaveDir + "/" + sessionFileArray[i]+")!", th);
               }
            }
         }
         else {
            synchronized(_sessionTbl) {
               L1: while(true) {
                  Enumeration vals = _sessionTbl.elements();
                  HttpSessionImpl session;
                  while(vals.hasMoreElements()) {
                     session = (HttpSessionImpl)vals.nextElement();
                     synchronized(session) {
                        dif = (new Date()).getTime() - session._lastAccessedDate.getTime();
                        dif /= 1000;
                        if(dif >= session._maxInactiveInterval) {
                           _debug2("Invalidation of session id="+session._id);
                           session._valid = false;
                           _sessionTbl.remove(session._id);
                           continue L1;
                        }
                     }
                  }
                  break;
               }
            }
         }
         
         // Print thread pool stats
         if(printStats)
            System.out.print(_threadPool.getStats().toString() + "\n");
      }
   }
   
   public static void stopApplication() {
      _stop();
   }
      
   public static void main(String args[]) {
      System.getProperties().put("shutdown.method", "stopApplication");
      if(args.length != 1) {
         System.out.println("Usage: httpd <properties file>");
         System.exit(1);
      }
      try {
         _server = new httpd(args[0]);
      }
      catch(Throwable th) {
         th.printStackTrace();
         System.exit(1);
      }
      _server.run();
   }
}