// DataBaseBureauResource.java
// $Id: DataBaseBureauResource.java,v 1.3 1997/07/28 12:49:17 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.pics ;

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

import w3c.tools.store.*;
import w3c.www.http.*;
import w3c.www.mime.*;
import w3c.jigsaw.http.* ;
import w3c.jigsaw.resources.* ;
import w3c.jigsaw.forms.* ;

class RequestParser {

  private static final int VARSIZE = 8 ;
  private static final int VALSIZE = 500 ; // it's enough I think!
    
  String vars[] = null ;
  String vals[] = null ;

  int    varptr = 0 ;

  LineNumberInputStream in = null ;
  int         ch = -1 ;
  byte buffer[] = null ;
  int  bufptr = 0 ;

  /**
   * Append the given char in the internal buffer
   */
  
  public void append (int ch) {
    if ( bufptr + 1 >= buffer.length) {
      // resize buffer 
      byte nbuf[] = new byte[buffer.length * 2] ;
      System.arraycopy (buffer, 0, nbuf, 0, bufptr) ;
      buffer = nbuf ;
    }
    buffer[bufptr++] = (byte) ch ;
  }
 
  /**
   * Get the token from our internal buffer.
   */
  
  public String getToken (boolean clear) {
    String tok = new String (buffer, 0, 0, bufptr) ;
    if ( clear )
      bufptr = 0 ;
    return tok ;
  }

  void expect (int c) 
    throws InvalidRequestException
  {
    if ( ch == c )
      return ;
    String msg = ("expected " 
                   + (new Character((char) c)).toString()
		   + "[" + c + "]"
		   + " got " 
                   + (new Character((char) ch)).toString() 
		   + "[" + ch + "]");
    throw new InvalidRequestException(msg);
  }

  String parseVariableName () 
    throws IOException
  {
    while ((ch != '=') && (ch != '\n') && (ch != -1)) {
      append (Character.toLowerCase((char)ch)) ;
      ch = in.read() ;
    }
    return getToken(true) ;
  }
  
  String parseVariableValue() 
    throws IOException
  {
    while ( (ch != -1) && (ch != '&') ) {
      append (ch) ;
      ch = in.read() ;
    }
    return getToken(true) ;
  }
  
  public boolean hasOption (String option) {
    for (int i = 0 ; i < varptr ; i++) {
      if ( vars[i].equals (option) )
	return true ;
    }
    return false ;
  }

  public String getOptionValue (String option) {
    for (int i = 0 ; i < varptr ; i++) {
      if ( vars[i].equals (option) ) 
	return vals[i] ;
    }
    return null ;
  }

  void addBinding (String var, String val) {
    if ( varptr + 1 >= vars.length ) {
      // resize arrays 
      String nvars[] = new String[vars.length*2] ;
      String nvals[] = new String[vals.length*2] ;
      System.arraycopy (vars, 0, nvars, 0, vars.length) ;
      System.arraycopy (vals, 0, nvals, 0, vals.length) ;
      vars = nvars ;
      vals = nvals ;
    }
    vars[varptr] = var ;
    vals[varptr] = val ;
    varptr++ ;
  }

  void parse () 
    throws InvalidRequestException
  {
    try {
      while ( true ) {
	switch (ch) {
	case -1:
	  // we are done.
	  return;
	case ' ': case '\t': case '\n':
	  ch = in.read() ;
	  continue ;
	default:
	  String name = parseVariableName() ;
	  expect ('=') ; ch = in.read() ;
	  String value = parseVariableValue() ;
	  if (ch != -1) {
	    expect ('&') ; 
	    ch = in.read() ;
	  }
	  addBinding (name, value) ;
	  continue ;
	}
      }
    } catch (IOException ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
    }
  }


  RequestParser (String string) {
    try{
      this.in = (new LineNumberInputStream 
		 (new BufferedInputStream
		  (new StringBufferInputStream (string)))) ;
      
      this.buffer = new byte[32] ;
      this.ch = in.read() ;
      this.vars   = new String[VARSIZE] ;
      this.vals   = new String[VARSIZE] ;
      this.varptr = 0 ;
    } catch (IOException ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
    }
  }
}

/**
 * An HTTP interface to query a DataBase Label bureau.
 * This conforms to the
 * <a href="http://www.w3.org/hypertext/WWW/PICS/labels.html">PICS 
 * protocol specification</a>.
 * This resource accept PUT request to register labels and GET request to
 * deliver PICS labels or information about the services.
 * In the future the label will be store in PICS format.
 * @author Benoit Mahe <bmahe@sophia.inria.fr>
 */


public class DataBaseBureauResource extends LabelBureauResource {

  RequestParser parser = null;

  /**
   * Used to register labels in the DataBaseBureau.
   * Here is the detailed PUT request specification :<BR>
   * PUT uri HTTP1.x <BR>
   * service=<I>service url</I>&<BR> 
   * url=<I>the url to label</I>&<BR>
   * label=<I>the label in the alternative format</I>&<BR>
   * generic=[true|false]&    (is the label generic?)<BR>
   * overwrite=[true|false]&  (overwrite existing label?)<BR>
   * createservice=[true|false]      (create service?)<BR>
   * <BR>
   * @param request The request to handle.
   * @return Reply witch can have the followings codes values : <ul>
   * <li> HTTP.CREATED  (label registered)
   * <li> HTTP.CONTINUE (url already labeled)
   * <li> HTTP.SERVICE_UNAVAILABLE (service doesn't exist in this bureau) 
   * <li> HTTP.INTERNAL_SERVER_ERROR
   * <li> HTTP.BAD_REQUEST (may be a malformed url)
   * </ul>
   */

  public Reply put (Request request) {
    try {
      byte b[] = new byte[1024];
      int  g   = 0;

      StringBuffer buffer = new StringBuffer();
      InputStream in = request.getInputStream();

      while((g = in.read(b))>0)
	buffer.append(new String(b, 0, 0, g));

      in.close();


      String req = new String(buffer);
      parser = new RequestParser(req);
      parser.parse();

      String service = parser.getOptionValue("service");
      String url = parser.getOptionValue("url");
      String label = parser.getOptionValue("label");

      if (service == null) {
	Reply error = request.makeReply(HTTP.SERVICE_UNAVAILABLE) ;
	error.setContent ("You are requesting an invalid service") ;	 
	error.setContentType(MimeType.TEXT_PLAIN);
	return error;
      }
      
      boolean gen;
      if (parser.hasOption("gen"))
	gen = parser.getOptionValue("generic").equals("true");
      else 
	gen = false;

      boolean overwrite;
      if (parser.hasOption("overwrite"))
	overwrite = parser.getOptionValue("overwrite").equals("true");
      else
	overwrite = false;

      boolean create;
      if (parser.hasOption("createservice"))
	create = parser.getOptionValue("createservice").equals("true");
      else
	create = false;


      DataBaseService s = (DataBaseService) bureau.getLabelService(service);

      if (s == null){
	if (create) {
	  DataBaseBureau dbBureau = (DataBaseBureau) bureau;
	  if (dbBureau.createService(service)) {
	    if ((label != null) && (url != null)){
	      s = (DataBaseService) bureau.getLabelService(service);
	      if (s.addLabel(url,label,gen,overwrite)) {
		Reply done = request.makeReply(HTTP.CREATED);
		done.setContent ("Service created and label registered");
		done.setContentType(MimeType.TEXT_PLAIN);
		return done;
	      }
	    }
	    Reply done = request.makeReply(HTTP.CREATED);
	    done.setContent ("Service created");
	    done.setContentType(MimeType.TEXT_PLAIN);
	    return done;
	  }
	}
	Reply error = request.makeReply(HTTP.SERVICE_UNAVAILABLE) ;
	error.setContent ("You are requesting an invalid service") ;	 
	error.setContentType(MimeType.TEXT_PLAIN);
	return error;
      }
      else{
	if ((url == null) || (label == null)) {
	  Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	  error.setContent ("You must specify url and label") ;	 
	  error.setContentType(MimeType.TEXT_PLAIN);
	  return error;
	}
	if (s.addLabel(url,label,gen,overwrite)) {
	  Reply done = request.makeReply(HTTP.CREATED);
	  done.setContent ("Label registered.");
	  done.setContentType(MimeType.TEXT_PLAIN);
	  return done;
	}
	else { // may be the url is already labeled
	  String currentLabel = s.getLabelFromUrl(url);
	  if (currentLabel.length() > 0){
	    // reply the current label
	    Reply rep = request.makeReply(HTTP.CONTINUE) ;
	    rep.setContent (currentLabel) ;
	    rep.setContentType(MimeType.TEXT_PLAIN);
	    return rep;
	  }
	  else {
	    Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
	    error.setContent ("Problem : Cannot Insert label now.") ;
	    error.setContentType(MimeType.TEXT_PLAIN);
	    return error;
	  }
	}
      } 
    } catch (InvalidRequestException ex) { 
      System.out.println(ex.getMessage());
      ex.printStackTrace();
      Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
      error.setContent ("Bad request") ;
      error.setContentType(MimeType.TEXT_PLAIN);      
      return error;
    }
    catch (java.net.MalformedURLException ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
      Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
      error.setContent ("url malformed") ;
      error.setContentType(MimeType.TEXT_PLAIN);      
      return error;
    } catch (Exception ex) {
      System.out.println(ex.getMessage());
      ex.printStackTrace();
      Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
      error.setContent ("error") ;
      error.setContentType(MimeType.TEXT_PLAIN);      
      return error;
    }
  }

  /**
   * handle get request. Here is the detailed get request specification :<BR>
   * HTTP1.0 /GET<BR>  
   * opt=[generic|normal|tree|generic+tree|insert|delete|list|services]&<BR>
   * s=<I>the label service</I>&<BR>
   * u=<I>the url to label</I>&<BR>
   * label=<I>the label in the alternative format</I>&<BR>
   * generic=[true|false]    (is the label generic?)<p>
   * <ul>
   * <li> <strong>opt=[generic|normal|tree|generic+tree] :</strong><BR>
   * classic PICS request. 
   * Require : s, u.
   * <li> <strong>opt=insert :</strong><BR> 
   * register label for url (u) in the service (s).
   * Require : s, u, label, generic.
   * <li> <strong>opt=delete :</strong><BR> 
   * delete label for url (u) in the service (s).
   * Require : s, u.
   * <li> <strong>opt=services :</strong><BR>
   * This request return the services list in the reply :<strong><br> 
   * http://www.service1.com& 
   * http://www.service2.com<br></strong> 
   * <li> <strong>opt=list : </strong><BR>
   * this request return a HTML reply witch is the labels
   * list of the service (s).
   * Require : s.
   * <ul>
   */
  
  public Reply handle (Request request, URLDecoder data) 
	throws HTTPException 
  {
    // Get opt (only generic/normal/insert/delete/list/services)
    String opt        = data.getValue("opt") ;
    if ( opt.equals ("insert") ) {
      // insert label ...
      String service = data.getValue("s");
      String url = data.getValue("u");
      String label = data.getValue("label");

      boolean gen = data.getValue("generic").equals("true");
      
      DataBaseService s = (DataBaseService) bureau.getLabelService(service);
      if (s == null){
	Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	error.setContent ("You are requesting an invalid service") ;	    
	return error;
      }
      else{
	try {
	  if (s.addLabel(url,label,gen,false)) {
	    Reply done = request.makeReply(HTTP.OK);
	    done.setContent ("Label registered.");
	    return done;
	  }
	  Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	  error.setContent ("Cannot Insert label now.") ;	    
	  return error;
	} catch (java.net.MalformedURLException ex) {
	  Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	  error.setContent ("Url malformed") ;	    
	  return error;
	}
      }
    }
    else  if ( opt.equals ("delete") ){
      String service = data.getValue("s");
      String url = data.getValue("u");

      DataBaseService s = (DataBaseService) bureau.getLabelService(service);
      if (s == null){
	Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	error.setContent ("You are requesting an invalid service") ;	    
	return error;
      }
      else{
	if (s.delLabel(url)) {
	  Reply done = request.makeReply(HTTP.OK);
	  done.setContent ("Label deleted.");
	  return done;
	}
	Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	error.setContent ("Cannot delete label now.") ;	    
	return error;
      }
    }
    else  if ( opt.equals ("list") ){
      String service = data.getValue("s");
      DataBaseService s = (DataBaseService) bureau.getLabelService(service);
      if (s == null){
	Reply error = request.makeReply(HTTP.BAD_REQUEST) ;
	error.setContent ("You are requesting an invalid service") ;	    
	return error;
      }
      else {
	String list = s.list();
	Reply done = request.makeReply(HTTP.OK);
	done.setContentType(MimeType.TEXT_HTML);
	done.setContent (list);
	return done;
      }
    }
    else  if (opt.equals("services")) {
      	// reply the services list

	DataBaseBureau dbBureau = (DataBaseBureau) bureau;
	
	String services[] = dbBureau.getLabelServicesNames();
	StringBuffer buffer = new StringBuffer();
	for (int i = 0 ; i < services.length ; i++)
	  buffer.append(services[i]+"&");


	Reply rep = request.makeReply(HTTP.OK) ;
	rep.setContent ( new String(buffer)) ;
	rep.setContentType(MimeType.TEXT_PLAIN);
	return rep;
    }
    else{
      String services[] = parseURLs (data.getMultipleValues ("s")) ;
      String urls[]     = parseURLs (data.getMultipleValues ("u")) ;
      int    iformat   = parseFormat(request,data.getValue("format"));
      // Perform request
      if ( opt.equals ("generic") ) {
	return getGenericLabels (request, iformat, urls, services, data) ;
      } else if ( opt.equals ("normal") ) {
	return getNormalLabels (request, iformat, urls, services, data) ;
      } else if ( opt.equals ("tree") ) {
	return getTreeLabels (request, iformat, urls, services, data) ;
      } else  if ( opt.equals ("generic+tree") ) {
	return getGenericTreeLabels(request,iformat, urls, services, data);
      } else {
	Reply error = request.makeReply(HTTP.BAD_REQUEST);
	error.setContent ("Invalid label bureau query, bad option: "+opt);
	throw new HTTPException (error) ;
      }
    }
  }

}





