Listing of Source cgi/DirectoryListingCGI.java
package se.entra.phantom.server.http; 

import se.entra.phantom.common.LocaleInfo;
import se.entra.phantom.server.FileListContent;
import se.entra.phantom.server.BinaryTrace;
import se.entra.phantom.server.http.HttpSession;
import se.entra.phantom.server.http.HttpResource;
import se.entra.phantom.server.http.IncludeCgiPrintInterface;

import java.io.File;
import java.io.PrintWriter;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;
import org.w3c.dom.Element;

/**
 * The <code>DirectoryListingCGI</code> class lists a directory of files
 * as specified by the tag element <code>DIR</code> relative the server
 * directory (not <code>htdocs</code>).
 * 
 * <p>It also contains a static method that other CGI's may call to produce
 * this directory listing depending e.g. of a client certificate that
 * implies a private directory on the server for the particular client.
 * 
 * <p>A typical document would look like:
 * 
 * <pre>
 * (html)
 * (head)
 * (meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1")
 * (title)Directory listing(/title)
 * (style)
 * (!--
 *   td { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 60%; }
 * --)
 * (/style)
 * (body)
 * (h2)Directory (%@include cgi="DIRLIST" dirname=1)(/h2)
 * 
 * (table border cellspacing=1 cellpadding=1)
 * 
 * (tr)
 * (td bgcolor="#C0C0C0")(b)Name(/b)(/td)
 * (td bgcolor="#C0C0C0" align="center")(b)Size(/b)(/td)
 * (td bgcolor="#C0C0C0" align="center")(b)Date(/b)(/td)
 * (/tr)
 * 
 * (%@include cgi="DIRLIST"
 *    dir     ="."
 *    fileref ="/download.cgi"
 *    tr      ="(tr)"
 *    tdparent="(td)(pdir) &nbsp;"
 *    tddir   ="(td)(dir) &nbsp;"
 *    tdfile  ="(td)(file) &nbsp;"
 *    tdsize  ="(td align='right')"
 *    tddate  ="(td)")
 * 
 * (/table)
 * 
 * (/body)
 * (/html)
 * </pre>
 * 
 * @see se.entra.phantom.server.http.FileDownloadCGI
 */
public class DirectoryListingCGI implements IncludeCgiPrintInterface
{
  /**
   * Prints the directory listing from the base directory as specified
   * by the <b>DIR</b> element. If the current document contains additional
   * directory information specified after the "?" of the URL, this will
   * be appended to the directory specified by <b>DIR</b>.
   * 
   * <p>If the element <b>DIRNAME=1</b> is specified, the subdirectory name
   * specified in the URL as parameter is returned, e.g.
   * <code>(h2)Directory (%@include cgi="DIRLIST" dirname=1)(/h2)</code>
   *
   * <p>The following elements are used to build the table:
   *
   * <ul>
   * <li><b>dir</b> - the root directory for the listing relative the server
   *     directory.
   * 
   * <li><b>fileref</b> - the base hyperlink for downloading files, e.g.
   *     /download.cgi will produce
   *     (a href="/download.cgi?%2Fselectedfile.doc")selectedfile.doc(/a).
   * 
   * <li><b>tr</b> - the tag for each line in the listing, e.g. (tr).
   *
   * <li><b>tdParent</b> - the tag for parent directory, e.g.
   *     (td)(img src="images/updir.gif" width=6 height=6
   *     alt="Parent directory").
   *     This text will preceed the name of the parent directory.
   *
   * <li><b>tdDir</b> - the tag for directories, e.g.
   *     (td)(img src="images/dir.gif" width=6 height=6 alt="Directory").
   *     This text will preceed the name of the directory.
   *
   * <li><b>tdFile</b> - the tag for file names, e.g.
   *     (td)(img src="images/file.gif" width=6 height=6 alt="File").
   *     This text will preceed the name of the file.
   *
   * <li><b>tdSize</b> - the tag for size following the file name, e.g. (td align="right").
   *
   * <li><b>tdDate</b> - the tag for date following the file size, e.g.(td).
   * </ul>
   *
   * @throws  IOException  for IO errors.
   */
  @Override
  public void performAction(HttpSession session,
                            HttpResource resource,
                            Element element,
                            PrintWriter out) throws IOException
    {
    // Perform the listing.
    listDirectory(session,element,element.getAttribute("dir"),out);
    }
  
  /**
   * Prints the directory listing to the print writer. If the
   * parameter <code>fileRef</code> is null or empty, no hyperlinks
   * for the files will be created.
   */
  @SuppressWarnings("deprecation")
  public static void listDirectory(HttpSession session,
                                   Element element,
                                   String dir,
                                   PrintWriter out)
    {
    // Get the HTML variables.
    String fileRef=element.getAttribute("fileRef");
    String tr     =element.getAttribute("tr"     );
    String tdDir  =element.getAttribute("tdDir"  );
    String tdFile =element.getAttribute("tdFile" );
    String tdSize =element.getAttribute("tdSize" );
    String tdDate =element.getAttribute("tdDate" );
    String dirDown=element.getAttribute("dirDown");

    // Get the document base reference for the links.
    String baseRef=session.resourceName;

    // Get the sub-directory name.
    String subDir="";
    String params=session.uriParams;
    if ( params!=null && params.length()>0 )
      {
      // Convert to string and make sure it's not trying to
      // access something above the root directory.
      params=InternetCharacterConversion.urlDecode(params).replace('\\','/');
      if ( params.indexOf("/..")<0 )
        subDir=params;
      }
    
    // Check for special decode of the directory name.
    if ( element.getAttribute("dirname").length()>0 )
      {
      out.println(subDir.length()==0? "/": subDir);
      return;
      }

    // Add the parent directory to table.
    if ( subDir.length()>1 )
      {
      int d=subDir.lastIndexOf('/');
      String parent=(d==-1)? "/": subDir.substring(0,d);
      if ( parent.length()==0 )
        parent="/";
        
      out.println(element.getAttribute("tdParent")
                 +"<a href=\""+baseRef+"?"+java.net.URLEncoder.encode(parent)+"\">..</a>"
                 +"</td>"+tdSize+"-</td>"+tdDate+"-</td></tr>");
      }

    // Build complete directory name.
    if ( subDir.length()>0 && !subDir.equals("/") )
      {
      if ( subDir.charAt(0)!='/' )
        dir+="/";
      dir+=subDir;
      }

    // Check that directory spec is OK.
    File f=new File(dir);
    if ( !f.exists() )
      {
      out.println(tr+"<td>Directory \""+dir+"\" not found</td></tr>");
      return;
      }
    if ( !f.isDirectory() )
      {
      out.println(tr+"<td>\""+dir+"\" is not a directory</td></tr>");
      return;
      }
    String [] list=f.list();
    if ( list==null )
      {
      out.println(tr+"<td>\""+dir+"\" is empty</td></tr>");
      return;
      }
    Vector<String> dirs =new Vector<String>();
    Vector<String> files=new Vector<String>();
    for ( int ii=0, cc=list.length; ii<cc; ++ii )
      {
      String fn=list[ii];
      f=new File(dir+"/"+fn);
      if ( !f.exists() )
        continue;
      if ( f.isDirectory() )
        dirs.addElement(fn);
      else
        files.addElement(fn);
      }
    
    // Output the sorted directories.
    dirs=FileListContent.sortList(dirs);
    for ( Enumeration<String> e=dirs.elements(); e.hasMoreElements(); )
      {
      // Build the hyperlink.
      String fn=e.nextElement();
      String sdir=subDir.equals("/")? "/"+fn: subDir+"/"+fn;
      String ref=baseRef+"?"+java.net.URLEncoder.encode(sdir);
      
      // Get the date.
      f=new File(dir+"/"+fn);
      String date="-";
      long l=f.lastModified();
      if ( l>0 )
        {
        Date d=new Date(l);
        date=InternetCharacterConversion.stringConvertToHTMLString(LocaleInfo.getDate(d)+" "+LocaleInfo.getTimeHourMinute(d));
        }
      
      out.println(tr+tdDir+"<a href=\""+ref+"\">"+InternetCharacterConversion.stringConvertToHTMLString(fn)+"</a></td>"+tdSize+"-</td>"+tdDate+date+"</td></tr>");
      }
    
    // Output the sorted files.
    files=FileListContent.sortList(files);
    for ( Enumeration<String> e=files.elements(); e.hasMoreElements(); )
      {
      // Set the file name and possible hyperlink.
      String fn=e.nextElement();
      f=new File(dir+"/"+fn);
      if ( fileRef!=null && fileRef.length()>0 )
        {
        if ( dirDown.length()>0 )
          {
          String fndir=dirDown;
          if ( !fndir.endsWith("/") ) fndir+="/";
          String sdir=subDir;
          if ( sdir.startsWith("/") ) sdir=sdir.substring(1);
          if ( !sdir.endsWith ("/") ) sdir+="/";
          if ( sdir.equals    ("/") ) sdir="";
          fn=InternetCharacterConversion.urlDecode(fn).replace('\\','/');
          fn="<a href=\""+fndir+sdir+fn+"\">"
            +InternetCharacterConversion.stringConvertToHTMLString(fn)+"</a>";
          }
        else
          {
          String fndir=subDir.startsWith("/")? subDir: "/"+subDir;
          fndir=fndir.equals("/")? "/"+fn: fndir+"/"+fn;
          fn="<a href=\""+fileRef+"?"+java.net.URLEncoder.encode(fndir)+"\">"
            +InternetCharacterConversion.stringConvertToHTMLString(fn)+"</a>";
          }
        }
      else
        fn=InternetCharacterConversion.stringConvertToHTMLString(fn);

      // Get the date.
      String date="-";
      long l=f.lastModified();
      if ( l>0 )
        {
        Date d=new Date(l);
        date=InternetCharacterConversion.stringConvertToHTMLString(LocaleInfo.getDate(d)+" "+LocaleInfo.getTimeHourMinute(d));
        }

      // Get the length.
      String length="-";
      l=f.length();
      if ( l>=0 )
        length=BinaryTrace.getThousandFormattedNumber(l);

      out.println(tr+tdFile+fn+"</td>"+tdSize+length+"</td>"+tdDate+date+"</td></tr>");
      }
    }
}