Listing of Source filetrans/AdminUtilServerToClientFileTransfer.java
package se.entra.phantom.server.rconsole;

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import se.entra.phantom.server.ClientSessionManager;
import se.entra.phantom.server.VirtualCUserWindow;
import se.entra.phantom.server.VirtualComponentAdapter;
import se.entra.phantom.server.VirtualControl;
import se.entra.phantom.server.VirtualPanel;
import se.entra.phantom.server.VirtualSessionManager;
import se.entra.phantom.server.VirtualUserWindowInterface;

/**
 * This class handles the transfer of an arbitrary file from the server to the client.
 */
public class AdminUtilServerToClientFileTransfer extends VirtualComponentAdapter implements AdminFileTransferListener
{
  /**
   * The file to be transfered from the server.
   */
  private File serverFile;

  /**
   * The stream used for picking up the data.
   */
  private BufferedInputStream fileInputStream;

  /**
   * The total file size.
   */
  private int totalSize;

  /**
   * Current position in file.
   */
  private int pos;

  /**
   * The full path to the file on the client side.
   */
  private String clientFilePath;

  /**
   * The session manager.
   */
  private final VirtualSessionManager vsm;

  /**
   * Flag indicating if file transfer is in progress.
   */
  private boolean inProgress;

  /**
   * Flag indicating if a 'cancel' action is to be processed.
   */
  private boolean doCancel;

  /**
   * The server file control.
   */
  private final VirtualControl serverFileControl;

  /**
   * A general header used for message boxes.
   */
  private final String messageHeader;

  /**
   * A general header used for error message boxes.
   */
  private final String errorMessageHeader;

  ///

  /**
   * Constructor.
   */
  public AdminUtilServerToClientFileTransfer(ClientSessionManager csm,VirtualPanel vp)
    {
    this.vsm=vp.getVirtualSessionManager();
    vp.setListener(this);

    // Hide the file transfer user window.
    vp.setVisible("FILETRAN",false);
    vp.setText("MESSAGE","Progress indicator:");
    vp.setText("PROGRESS","0");

    serverFileControl=vp.getControlFromID("SRVFILE");
    messageHeader="Server-to-client file transfer";
    errorMessageHeader="Error during file transfer";
    }

  /**
   * This thread is notified each time the 'old' thread is requested by the client
   * for another piece of data.
   */
  private void sendData(VirtualPanel vp)
    {
    try
      {
      final byte [] buf=new byte [4096];
      if ( fileInputStream==null )
        {
        logEvent(vp,"sendData","Unable to read from FileInputStream");
        return;
        }
      int cc=0;
      if ( totalSize>0 )
        {
        cc=fileInputStream.read(buf);
        if ( cc<0 )
          throw new EOFException();
        }

      //Get that userWindow!
      final VirtualUserWindowInterface vu=((VirtualCUserWindow)(vp.getControlFromID("FILETRAN"))).getPeer();
      final AdminFileTransferInterface userWindow=(AdminFileTransferInterface)vu;

      if ( userWindow==null )
        {
        logEvent(vp,"sendData","Unable to locate server UserWindow");
        close(vp);
        return;
        }

      userWindow.serverToClientFileTransfer(this,clientFilePath,totalSize,cc,buf);
      pos+=cc;
      }
    catch(final Exception e)
      {
      logEvent(vp,"sendData",e.toString());
      if ( fileInputStream!=null )
        {
        try { fileInputStream.close(); }
        catch(final IOException e2) {}
        fileInputStream=null;
        }
      close(vp);
      }
    }

  /**
   * Method call when download is completed
   */
  @Override
  public void onFileUploadComplete(VirtualPanel vp,AdminFileTransferInterface ft)
    {
    if ( fileInputStream!=null )
      {
      try { fileInputStream.close(); }
      catch(final Exception e) {}
      fileInputStream=null;
      }
    vp.setText("PROGRESS","100");
    inProgress=false;
    vp.messageBox(MB_OK,ICON_INFORMATION,
                  "You have received a new file ("+clientFilePath+") from the server ("+serverFile+")." ,
                  messageHeader);
    vp.setEnabled("TRANSFER",true);
    }

  /**
   * Called whenever a file transfer error occurs.
   */
  @Override
  public void onFileTransferError(VirtualPanel vp,AdminFileTransferInterface ft,String errorMessage)
    {
    vp.messageBox(MB_OK,ICON_WARNING,errorMessage,errorMessageHeader);
    vp.setText("PROGRESS","0");
    close(vp);
    return;
    }

  /**
   * A file has been selected in the dialog box (or canceled if file name is null).
   */
  @Override
  public void onFileSelected(VirtualPanel vp,AdminFileTransferInterface ft,String fileName,int size)
    {
    }

  /**
   * Returns the list of all files or directories in the requested directory.
   */
  @Override
  public void onDirectoryList(VirtualPanel vp,AdminFileTransferInterface ft,AdminConfigFileTransferItem [] files)
    {
    }

  /**
   * A piece of the download data of a file (data is null when canceled).
   */
  @Override
  public void onFileDownload(VirtualPanel vp,AdminFileTransferInterface ft,int totalSize,byte [] data)
    {
    }

  /**
   * The client is requesting a file for download
   */
  @Override
  public void onFileUpload(VirtualPanel vp,AdminFileTransferInterface ft)
    {
    // Calculate percentage of download.
    final long progress=100L*pos/totalSize;
    vp.setText("PROGRESS",Long.toString(progress));
    if ( pos<totalSize && !doCancel )
      sendData(vp);
    }

  /**
   * A file has been chosen on the client
   */
  @Override
  public void onTargetFileSelected(VirtualPanel vp,AdminFileTransferInterface ft,String directory)
    {
    inProgress=false;
    vp.setText("CLNTDIR",directory);
    }

  /**
   * Push buttons and menu items generates this event when selected.
   */
  @Override
  public void onAction(VirtualPanel vp,String controlID)
    {
    if      ( controlID.equals("CANCEL"  ) )  onPanelClosing(vp);
    else if ( controlID.equals("DIR"     ) )  selectTargetFile(vp);
    else if ( controlID.equals("FILE"    ) )  new AdminConfigSelectFile(vsm,serverFileControl,null);
    else if ( controlID.equals("TRANSFER") )  performTransfer(vp);
    }

  /**
   * Method used to initiate a file selection on the client
   */
  private void selectTargetFile(VirtualPanel vp)
    {
    final VirtualUserWindowInterface vu=((VirtualCUserWindow)(vp.getControlFromID("FILETRAN"))).getPeer();
    final AdminFileTransferInterface userWindow=(AdminFileTransferInterface)vu;

    doCancel=false;
    if ( !userWindow.selectTargetFile(this) )
      vp.messageBox(MB_OK,ICON_WARNING,"Selection of file to transfer failed.",messageHeader);
    }

  /**
   * Method called when the 'TRANSFER' button is pressed
   */
  private void performTransfer(VirtualPanel vp)
    {
    clientFilePath=vp.getText("CLNTDIR");
    final String serverFilePath=vp.getText("SRVFILE");

    // Check access rights.
    try
      {
      AdminConfigFileManager.checkAccessRights(serverFilePath);
      }
    catch(final IOException e)
      {
      vp.messageBox(MB_OK,ICON_CRITICAL,"Error checking the specified server file: "+e,messageHeader);
      return;
      }

    String cfpEnding="";
    String sfpEnding="";

    int index=clientFilePath.lastIndexOf('.');
    if ( index>=0 )
      cfpEnding=clientFilePath.substring(index);
    index=serverFilePath.lastIndexOf('.');
    if ( index>=0 )
      sfpEnding=serverFilePath.substring(index);

    // Validation.
    if ( clientFilePath.isEmpty() || serverFilePath.isEmpty() )
      {
      vp.messageBox(MB_OK,ICON_WARNING,"Please specify both a target and a source file.",messageHeader);
      return;
      }

    serverFile=new File(serverFilePath);
    if ( !serverFile.isFile() )
      {
      vp.messageBox(MB_OK,ICON_WARNING,"The file specified does not exist on the server.",messageHeader);
      return;
      }

    if ( !cfpEnding.equals(sfpEnding) )
      {
      final int answer=vp.messageBox(MB_YESNO,ICON_WARNING,
                               "Please note that You are about to save the file with a different extension than the original file on the server.\n\n"+
                               "Do You want to continue?",messageHeader);
      if ( answer!=MBID_YES )
        return;
      }
    try
      {
      fileInputStream=new BufferedInputStream(new FileInputStream(serverFile));
      totalSize=fileInputStream.available();
      }
    catch(final Exception e)
      {
      logEvent(vp,"performTransfer","Error when creating input stream ("+e+").");
      return;
      }

    pos=0;
    vp.setEnabled("TRANSFER",false);
    vp.setText("PROGRESS","-1"); // Set undetermined state.
    doCancel=false;
    inProgress=true;
    sendData(vp);
    }

  /**
   * Closes this panel when no progress takes place.
   * If transfer is in progress, set cancel flag.
   */
  private void close(VirtualPanel vp)
    {
    pos=0;
    if ( fileInputStream!=null )
      {
      try { fileInputStream.close(); }
      catch(final IOException e2) {}
      fileInputStream=null;
      }

    if ( !inProgress )
      vsm.stopSession(vp.getPanelSession().getIndex());
    else
      {
      // Get that userWindow!
      final VirtualUserWindowInterface vu=((VirtualCUserWindow)(vp.getControlFromID("FILETRAN"))).getPeer();
      final AdminFileTransferInterface userWindow=(AdminFileTransferInterface)vu;
      userWindow.closeFileTransfer();
      vp.setEnabled("TRANSFER",true);
      vp.setText("PROGRESS","0");
      inProgress=false;
      doCancel=true;
      }
    }

  /**
   * Called when the close button of a panel is pressed,
   * but before any other processing has been done. If this
   * function returns false, the application close object
   * will be called (if defined), otherwise a faked key-press
   * of the Escape key is performed. If these two actions
   * failed (because there is no close object or no button or
   * menu item is connected to the Escape key), then the
   * function <code>onPanelClose</code> is called.
   *
   * @return  true  to cancel all default processing.
   */
  @Override
  public boolean onPanelClosing(VirtualPanel vp)
    {
    if ( inProgress )
      vp.messageBox(MB_OK,ICON_WARNING,
                    "The file transfer was cancelled. The created file is corrupt.",
                    errorMessageHeader);
    doCancel=true;
    close(vp);
    return true;
    }

  /**
   * Called to inform the listener that the panel is destroyed.
   *
   * <p>Closes any currently opened file transfer.
   */
  @Override
  public void onPanelDestroy(VirtualPanel vp)
    {
    if ( fileInputStream!=null )
      {
      try { fileInputStream.close(); }
      catch(final IOException e2) {}
      fileInputStream=null;
      }
    }

  /**
   * Method to display an error message
   */
  private void logEvent(VirtualPanel vp,String func,String msg)
    {
    if ( vp!=null )
      vp.messageBox(MB_OK,ICON_WARNING,"Error in "+func+": "+msg,errorMessageHeader);
    }
}