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

/** FileType
  * Implemente la gestion de fichier distant
  * @param le nom du fichier 
  * @version        0.1 Feb 1997
  * @author         Ebele Nicolas, Mallet Frederic
  */
class FileType {

static final public String ClassName = "FileType";
/** le nom du fichier */
private String Printable;
/** le 'vrai' nom du fichier (pour les liens) */
private String RealName;
/** le type du fichier ('d' -> repertoire; '-' -> fichier normal ;  'l' -> lien) */
private char Type;
/** la taille du fichier */
private int length;

/** Constructeur
  *@param Name le nom du fichier a parser (il peut venir soit d'une page HTML, soit d'une
  *            connection ftp, on parse le nom du fichier pour detetcter si c'est un repertoire,
  *            un fichier, ou unlien
  */
  FileType(String Name)
  {
    if (Name.startsWith("/"))
      Name = Name.substring(1);

    Init.println(ClassName,"FileType de " + Name + " de longueure=" + Name.length());
    StringTokenizer st = new StringTokenizer(Name);

    if (st.countTokens() > 7) {              // il y a des grandes chances pour que ce soit un resultat FTP
      String first = st.nextToken();
      
      Type = first.charAt(0); // recupere le type du fichier
      while (st.hasMoreTokens()) {
	int i = st.countTokens();
	String s = st.nextToken();

	if (i == 5) length = Integer.parseInt(s);
	if (Type == 'l') {
	  if (i == 3)
	    Printable = s;
	  if (i == 1) RealName = s;
	}
	else if (i  == 1)
	  Printable = s;
      }
    }
    else {
      String s = st.nextToken("?");// on supprime tout ce qu'il y a apres ? (cgi, ou retun value de roxen)
      Printable = s;
      length = -1;
      this.RealName = Name;
      if (Printable.charAt(Printable.length()-1) == '/') {
	Printable = Printable.substring(0,Printable.length()-1);
	this.Type = 'd';
      }
      else
	this.Type = '-';
    }
    Init.println(ClassName, "on a cree le fichier: " + Printable + " de type " + Type);

  }

/**
  * @param pas de parametres
  * @return renvoie vrai si le fichier est un repertoire
  */
  public boolean isDirectory()
  {
    return (Type == 'd'); 
  }

/** 
  * @param pas de parametres
  * @return renvoie vrai si le fichier est un fichier normal
  */
  public boolean isFile()
  {
    return (Type == '-');
  }
/** 
  * @param pas de parametres
  * @return renvoie vrai si le fichier est un lien
  */
  public boolean isLink()
  {
    return (Type == 'l');
  }
/** 
  * @param pas de parametres
  * @return renvoie la taille du fichier, ou -1 si elle n'est pas connue
  */
  public int Length()
  {
    return length;
  }
/** 
  * @param pas de parametres
  * @return renvoie le vrai nom du fichier (pour un lien)
  */
  public String RealName()
  {
    return RealName;
  }

/** 
  * @param pas de parametres
  * @return renvoie le chemin du fichier (si il existe)
  */
  public String getPath()
  {
    int i = Printable.lastIndexOf("/");
    if (i == -1) return null;
    return Printable.substring(0,i);
  }
/** 
  * @param pas de parametres
  * @return renvoie le nom du fichier
  */
  public String getName()
  {
    return Printable;
  }
}


/** FileType
  * Permet de 'parser' des fichiers sous forme brute (venant d'un LS -la d'un site Ftp, ou d'une page HTML)
  * il permet de trouver le chemin complet d'un lien sur une page HTML
  * @param le nom du fichier 
  * @version        0.1 Feb 1997
  * @author         Ebele Nicolas, Mallet Frederic
  */

class DirEntry {
static final public String ClassName = "DirEntry";

/** la base du document ex : http://www.essi.fr/~ebele or http://www.essi.fr */
private String Base;
/** la location complete */
private String Location;
/** le contenu de la page */
private Hashtable Content;

  
/** renvoie la location
  * @param pas de parametres
  * @return renvoie l'adresse de la page
  */
  public String Location()
  {
    return Location;
  }

/** Renvoie la base d'une URl
  * @param Location : l'url a parser
  * @return renvoie la base d'une URL (par exemple http://www.essi.fr ou http://www.essi.fr/~ebele
  */
  static public String getBasePub(String Location)
  {
    int i,j;
    if (Location.toLowerCase().startsWith("www."))
      Location = "http://" + Location;
    if (Location.toLowerCase().startsWith("http://")) {
      if ((i = Location.indexOf("~")) != -1) { // recherche du nom du proproetaire de la page : ~nom 
	  j = Location.indexOf("/",i);
	  if (j == -1) j = Location.length();
	  return Location.substring(0,j);
	}
      j = Location.indexOf("/",8); //8 == sizeof(http://) +1
      if (j == -1) j = Location.length();
      return Location.substring(0,j);
    } else
      if (Location.toLowerCase().startsWith("ftp://")) {
	j= Location.indexOf("/",7);   // 7 == sizeof("ftp://");
	if (j==-1) j = Location.length();
	return Location.substring(0,j);
      }
    return null; // la location ne commence ni par http:// ni par ftp://
  }
  
/** Cree une URl
  * @param Base la base de l'URL
  * @param File le fichier a ajouter a l'URL
  * @return renvoie l'URL correspondant au 2 parametres
  *         par exemple http://www.essi.fr/~ebele + /~mallet => http://www.essi.fr/~mallet
  */
  static public String createUrl(String Base, String File)
  {
    Init.println(ClassName, "CreateUrl avec Base=" + Base + " File =" + File);

    if (File != null && File.endsWith("/.")) // on supprime le /. de fin si il existe
      File = File.substring(0,File.length()-2);
    
    if (Base != null && File != null && File.startsWith("/~")) { // si on donne un nouveau nom de proprietaire
      File = File.substring(1);
      int i;
      if ((i = Base.indexOf("/~")) != -1)
	Base = Base.substring(0,i);
      return Base + "/" +  File;  // on a remplace l'ancien nom par le nouveau
    }
    
    if (File != null && File.startsWith("/")) { // si le fichier commence par /
      if (Base == null)
	return File.substring(1);
      return Base + File;  // alors on concatene a partir de la base (fichier relatif au home de la page)
    }
    if (File != null && File.equals(".")) File = null;
    // renvoie le reste
    if (Base != null)
      {
	if (File != null)
	  return Base + "/" + File;
	return Base;
      }
    return File;
  }

/** Renvoie l'url courante
  *@param : pas de parametres
  *@return renvoie la base de l'url courante  */
public String getBase()
  {
    return Base;
  }

/** Constructeur
  * @param Loc : l'adresse de l'URL ou on a trouve Content
  * @param Content : le contenu de cette URL (chaque entree est sperare par un \n
  * @param Ftp : vrai si le contenu vient d'une page ftp
  * On ajoute que les liens qui ont rapport avec l'adresse courante, on supprime 
  * tout ce qui n'est pas local
  */
  DirEntry(String Loc, String Content, boolean Ftp)
  {
    Init.println(ClassName, "Constructeur de  " + Loc + " avec " + Content);

    if (Loc.endsWith("/"))
	Loc = Loc.substring(0,Loc.length()-1);
    this.Location = Loc;
    if (Loc.endsWith("/."))
      Loc = Loc.substring(0,Loc.length()-2);

    // si la location se termine par .html, .cgi, ou .htm on est surement en train de parser une page 
    if (Loc.endsWith(".html") ||Loc.endsWith(".cgi") || Loc.endsWith(".htm"))
      {
	int i;
	i = Loc.lastIndexOf("/");
	if (i == -1) return;            // surement une erreur, il n'a que le nom de la page
	Loc = Loc.substring(0,i);
      }


    Base = getBasePub(Loc);
    Init.println(ClassName, " new base  ==" + Base);
    
    this.Content = new Hashtable(); // nouvelle table de Hachage pour stocker des FileType
    StringTokenizer st = new StringTokenizer(Content,"\n"); // on coupe le contenu ligne / ligne 
    if (st.countTokens() >= 1)
      for(int i=0; st.hasMoreTokens();i++)
	{
	  String s = st.nextToken();
	  if (s.toLowerCase().indexOf("diract,") == -1        // le server Roxen renvoie des choses bizarres !!!
	      && !s.toLowerCase().startsWith("mailto:") 
	      && !s.toLowerCase().startsWith("news:")
	      && !s.toLowerCase().startsWith("file:")
	      && !s.endsWith(" .")
	      && !s.endsWith(" .."))
	    {
	      String B = null;
	      String F = null;
	      int j;

	      String El = null;
	      if (!Ftp) {
		B = getBasePub(s); // on transforme le fichier -> sa base + son repertoire
		if (B == null) F = s; // si il n'a pas de base alors on prend le nom lu comme fichier
		else {
		  if (B.length() >= s.length()) j = -1;
		  else {
		    j = s.lastIndexOf("/");      // on recherche le fichier (dernier /)
		    if (j != -1 && j > B.length() ) {
		      F =s.substring(j+1,s.length()); 
		      Init.println(ClassName, "F=" + F + " S" + s + " B=" + B);
		    }
		    else {
		      Init.println(ClassName, "F=" + F + " S" + s + " B=" + B);
		      F = s.substring(B.length());
		    }
		  }
		}
		String cu;
		// on recree une Url 
		if (B != null) cu = createUrl(B,F);
		else cu = createUrl(Loc,F);
		Init.println(ClassName, "fichier lu:"+cu+" Location="+Location+" b="+B+" F="+F+" base=" + Base);
		if (!cu.equals(Base) && cu.startsWith(Loc)) // on ne garde que les fichiers locaux !!!
		  El = cu.substring(Loc.length());
	      }
	      else { // Ftp
		StringTokenizer stk = new StringTokenizer(s); 
		if (stk.countTokens() > 7) // on est sur que la ligne Ftp est complete ...
		  El = s;
		Init.println(ClassName, "Ftp ->" + El);
	      }
	      if (El != null && El.trim().length() >0  && !El.equals("/")) {
		FileType ft = new FileType(El);
		Init.println(ClassName, "On a ajoute le fichier " + ft.getName());
		this.Content.put(ft.getName(),ft);
	      }
	      
	    }
	}
  }
  
/** renvoie le contenu de l'URl
  *@param : pas de parametres
  *@return renvoie un tableau de FileType represetant le contenu de la page
  */   
public Hashtable Content()
  {
    return Content;
  }

/** renvoie le contenu de l'Url
  *@param : pas de parametres
  *@return renvoie un tableau de String represetant le contenu de la page
  */ 
public String[] list()
  {
    String res[] = new String[Content.size()];
    
    int i = 0;
    for (Enumeration e = Content.keys() ; e.hasMoreElements() ;i++ ) {
      res[i] = ((FileType)Content.get((String)e.nextElement())).getName();
    }
    return res;
  }
  
}

/** DirCache
  * Implemente la gestion d'un cache, il y a un cache par page (adresse)
  * @param le nom du fichier 
  * @version        0.1 Feb 1997
  * @author         Ebele Nicolas, Mallet Frederic
  */
  
public class DirCache {
/** le cache */
private Vector Cache;
/** la taille limite du cache */
private int LimitSize;

/** Constructeur
  *@param LimitSize : la taille limite du cache
  */
  DirCache(int LimitSize)
  {
    Cache = new Vector(LimitSize);
    this.LimitSize = LimitSize;
  }

/** 
  *@param Url l'adresse a tester
  *@return renvoie vrai si l'url est dans le cache
  */
public boolean isIn(String Url) 
  {
    return (indexOf(Url) != -1);
  } 
  
/** Renvoie la position de l'Url dans le cache
  *@param Url l'adresse a tester
  *@return renvoie la position de l'url dans le cache, -1 si elle n'existe pas
  */
public int indexOf (String Url)
  {
    if (Url == null || Cache == null) return -1;

    for(int i=0;i<Cache.size();i++){
      if (Url.equals(((DirEntry)Cache.elementAt(i)).Location()))
	return i;
      }
    return -1;
  }
/** Ajoute un element
  *@param Url l'adresse de l'URL (sert aussi pour l'indexation)
  *@param Rep le contenu de la page
  *@param Ftp vrai si le contenu vient d'un Ftp
  *@return pas de valeur de retour
  * implemente un cache de type FIFO
  */
public void addElement(String Url, String Rep, boolean Ftp)
  {
    int i;
    if (Url == null || Rep == null)
      return;


    if (( i = Cache.indexOf(Url)) != -1) // si l'url existe deja on l'update ...
      {
	Cache.removeElementAt(i);
	Cache.addElement(new DirEntry(Url,Rep,Ftp));
	return;
      }
    if (Cache.size() >= LimitSize)      // si le vecteur (cache) est plein
      Cache.removeElementAt(0);         // eneleve le plus vieux (plus ancien)

    Cache.addElement(new DirEntry(Url,Rep,Ftp)); // on l'ajoute
  }
/** Renvoie le contenu d'une Url
  *@param Url : l'adresse
  *@return renvoie une table de hachage representant l'url, ou null si il n'existe pas
  */ 
public Hashtable Element (String Url)
  {
    int i;
    if (( i =  this.indexOf(Url)) != -1)
      return ((DirEntry)Cache.elementAt(i)).Content();

  return null;
  } 
/** Renvoie le contenu d'une Url
  *@param Url : l'adresse
  *@return renvoie un tableau de String representant le contenu de l'Url
  */ 
public String[] list (String Url)
{
    int i;
    if (( i =  this.indexOf(Url)) != -1)
	return ((DirEntry)Cache.elementAt(i)).list();

    return new String[0];
  } 
}


