import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.applet.Applet;
import java.awt.Graphics;


public class blowfish extends Applet implements ActionListener{
  Button clearbutton, encryptbutton, decryptbutton;
  TextArea theText;
  TextField theKey;
  TextField theStatus;
	Label keyLabel;
	Label statusLabel;
  static final int OFFSET = 33;
  static final String CLEAR="Clear Text Area";
  static final String ENCRYPT="Encrypt";
  static final String DECRYPT="Decrypt";
  static final String STARTTAG="Start";
  static final String ENDTAG="End";
	
// Every CHARSperLINE characters, put a \n in. NB!! CHARSperLINE % 3 == 0 !!!*/

  public void init()
  {
//  Set up clear button
    clearbutton=new Button(CLEAR);
    clearbutton.setActionCommand(CLEAR);
    clearbutton.addActionListener(this);

//  Set up encrypt button
    encryptbutton=new Button(ENCRYPT);
    encryptbutton.setActionCommand(ENCRYPT);
    encryptbutton.addActionListener(this);

//  Set up decrypt button
    decryptbutton=new Button(DECRYPT);
    decryptbutton.setActionCommand(DECRYPT);
    decryptbutton.addActionListener(this);

//  Set up main text area
    theText = new TextArea(16,80);
    theText.setEditable(true);

//  Set up key field
    theKey = new TextField(20);
    theKey.setEditable(true);
    theKey.setEchoChar('*');
		keyLabel = new Label("Key:");

//  Set up status field
    theStatus = new TextField(20);
    theStatus.setEditable(false);
		statusLabel = new Label("Status:");

//  Layout and add fields

		BorderLayout layout = new BorderLayout();
		setLayout(layout);
		
    add(theText,"Center");
		
		Panel wPanel = new Panel();
		add(wPanel, "West");
		
    GridLayout wLayout = new GridLayout();
		wPanel.setLayout(wLayout);
		
		wPanel.add(encryptbutton);
		
		Panel ePanel = new Panel();
		add(ePanel, "East");
		
    GridLayout eLayout = new GridLayout();
		ePanel.setLayout(eLayout);
		
		ePanel.add(decryptbutton,"East");
		
		Panel sPanel = new Panel();
		add(sPanel, "South");

		FlowLayout sLayout = new FlowLayout();
		sPanel.setLayout(sLayout);

    sPanel.add(clearbutton);
		sPanel.add(keyLabel);
    sPanel.add(theKey);
		sPanel.add(statusLabel);
    sPanel.add(theStatus);
  }

  private boolean isKeyOK()
  {
    String theK = theKey.getText();
    int len;

    len=theK.length();
    if (len <= 5)
    {
      theStatus.setText("Key must be > 5 characters.");
      return false;
    }
    if (len > 50)
    {
      theStatus.setText("Key must be <= 50 characters.");
      return false;
    }
    if (theText.getText().length()==0)
    {
      theStatus.setText("No text.");
      return false;
    }
    return true;
  }

  public void actionPerformed (ActionEvent e)
  {
    String command = e.getActionCommand();
    String theSub, theNum;
    String textProcess=theText.getText();
    int startpos,endpos;
    Integer alignsize, binsize;
    int uudecoded[], unencrypted[], encrypted[];
    TheBlowfishClass bf = new TheBlowfishClass();

    if (command==CLEAR)
    {
      theText.setText("");
      theStatus.setText("");
      theText.setCaretPosition(0);
      theText.requestFocus();
    }
    if (command==DECRYPT)
    {
      theStatus.setText("");
      if (!isKeyOK())
        return;

//    Trim the tags
      textProcess.trim();

      startpos=textProcess.indexOf(STARTTAG);
      endpos=textProcess.indexOf(ENDTAG);

      if (startpos <0)
      {
        theStatus.setText("No start tag");
        return;
      }

      if (endpos <0)
      {
        theStatus.setText("No end tag");
        return;
      }
      startpos+=STARTTAG.length();
      theSub=textProcess.substring(startpos,endpos);
      theSub=theSub.trim();

//    Get values for binsize and alignsize 
      startpos=theSub.indexOf(' ');
      endpos=theSub.indexOf('\n');
      if (startpos == -1 || startpos > endpos)
      {
//    Problem if first space doesn't exist, or is not on current line
        theStatus.setText("Problem finding binsize");
        return;
      }

      theNum = theSub.substring(0,startpos);
      theSub = theSub.substring(startpos);
      try
      {
        binsize=new Integer(theNum);
      }
      catch(NumberFormatException nfe)
      {
        theStatus.setText("Error: binsize");
        return;
      }

      theSub=theSub.trim();
      startpos=theSub.indexOf(' ');
      endpos=theSub.indexOf('\n');
      if (startpos == -1 && endpos != -1)
      startpos=endpos;

      if (startpos >= 0 && endpos >=0 && startpos > endpos)
      startpos = endpos;

      theNum = theSub.substring(0,startpos);
      theSub = theSub.substring(startpos);

      try
      {
        alignsize=new Integer(theNum);
      }
      catch(NumberFormatException nfe)
      {
        theStatus.setText("Error: alignsize");
        return;
      }
      theSub=theSub.trim();

/*    Now have binsize, alignsize, and the data without headers.
      Convert into original encrypted binary. */
			
      char stuff[] = new char[theSub.length()];
      theSub.getChars(0,theSub.length(),stuff,0);
      try
      {
        encrypted=uudecode(stuff, binsize.intValue());

// Now decrypt

        unencrypted= new int[binsize.intValue()];
        theText.setText("");
        bf.decode(encrypted, unencrypted, binsize.intValue(), theKey.getText().getBytes(), theKey.getText().length());

// Convert unencrypted data into a string 

        String ss = int2String(unencrypted, binsize.intValue());
        ss=ss.substring(0,binsize.intValue() - alignsize.intValue());
        theText.setText(ss);
      }
      catch (Exception eee)
      {
        theStatus.setText("Error: Corrupted data");
        return;
      }
    }

    if (command==ENCRYPT)
    {
      theStatus.setText("");
      if (!isKeyOK())
        return;

//    Now encrypt, then uuencode */
      theText.setText("");

//    textProcess= textProcess.trim().concat("\n");
      encrypted=new int[textProcess.length()];
      bf.encode(String2int(textProcess), encrypted,textProcess.length(), theKey.getText().getBytes(), theKey.getText().length());
      String ss=new String();
      ss=ss.concat(STARTTAG).concat("\n");
      if ((textProcess.length() % 3) == 0)
      {
        startpos=textProcess.length();
        ss+=startpos+" 0\n";
      }
      else
      {
        startpos=textProcess.length() + 3 - (textProcess.length() % 3);
        endpos = 3-(textProcess.length() % 3);
        ss+=startpos+" "+endpos+"\n";
      }
      ss=ss.concat(uuencode(encrypted, textProcess.length()));
      theText.setText(ss);

//   This code required to successfully append ENDTAG.
      ss=theText.getText();
      ss=ss.concat("\n").concat(ENDTAG).concat("\n");
      theText.setText(ss);
    }
  }

  private int[] String2int(String s)
  {
    int length=s.length(), rv[],i;
    char c[]=s.toCharArray();

    rv=new int[length];
    for (i=0;i<length;i++)
    {
      rv[i]=(int)c[i];
    }
    return rv;
  }

  private String int2String(int in[], int length)
  {
    int i;
    char c[]=new char[length];
    String s;

    for (i=0;i<length;i++)
    c[i]=(char)in[i];
    s=new String(c);
    return s;
  }

  private String uuencode(int in[], int binlen)
  {
    char rv[];
    int i,idx;
    long j=0;
    Long k;

    idx=binlen;
    idx*=8/3;
    idx+=idx/30;
    idx+=10;
    idx*=2; /*Double the space allocation to allow the placing of spaces after a &.
		          Efficiency is not the prime concern here.*/
    idx+=STARTTAG.length();
    idx+=ENDTAG.length();
    rv = new char[idx];
    idx=0;
    for (i=0;i<binlen;i+=3)
    {
      j=0;
      if (i+3 > binlen)
      {
        if (i < binlen)
        {
          j=(in[i]&0xff)<<16;
        }
        if (i+1 < binlen)
        {
          j|=(in[i+1]&0xff)<<8;
        }
      }
      else
      {
        j=(in[i]&0xff)<<16;
        j|=(in[i+1]&0xff)<<8;
        j|=(in[i+2]&0xff); /* Read in 3 bytes */
      }
      k = new Long(OFFSET+((j >> 18) & 0x3f));
      rv[idx]=(char) (k.byteValue() & 0xff);
      if (rv[idx] == '&')
      {
         rv[++idx]=' ';
      }
      idx++;
      k = new Long(OFFSET+((j>>12) & 0x3f));
      rv[idx]=(char) (k.byteValue() & 0xff);
      if (rv[idx] == '&')
      {
         rv[++idx]=' ';
      }
      idx++;
      k = new Long(OFFSET+((j>>6) & 0x3f));
      rv[idx]=(char)(k.byteValue()&0xff);
      if (rv[idx] == '&')
      {
         rv[++idx]=' ';
      }
      idx++;
      k = new Long(OFFSET + (j&0x3f));
      rv[idx]=(char)(k.byteValue()&0xff);
      if (rv[idx] == '&')
      {
         rv[++idx]=' ';
      }
      idx++;
      if ((i+3) % 30 ==0)
        rv[idx++]='\n';
    }
    return new String(rv);
   }

  private        int[] uudecode(char in[], int binlen)
  {
    int            i, idx, j;
    int           rv[];
    Integer        k;

    rv = new int[binlen];
    idx = 0;
    for (i = 0,j=0; i < binlen; i += 3)
    {
      while ((int)in[idx] < OFFSET && (int)in[idx] != 0)
      idx++;
      if (0==(int)in[idx])
      {
        rv[i] = (int)((j >> 16) & 0xff);
        rv[i + 1] = (int)((j >> 8) & 0xff);
        rv[i + 2] = (int)(j & 0xff);
        break;
      }
      j = ((int)in[idx++] - OFFSET) << 18;

      while ((int)in[idx] < OFFSET && in[idx] != 0)
      idx++;
      if (0==(int)in[idx])
      {
        rv[i] = (int)((j >> 16) & 0xff);
        rv[i + 1] = (int)((j >> 8) & 0xff);
        rv[i + 2] = (int)(j & 0xff);
        break;
      }
      j |= ((int)in[idx++] - OFFSET) << 12;
      while ((int)in[idx] < OFFSET && (int)in[idx] != 0)
      idx++;
      if (0==(int)in[idx])
      {
        rv[i] = (int)((j >> 16) & 0xff);
        rv[i + 1] = (int)((j >> 8) & 0xff);
        rv[i + 2] = (int)(j & 0xff);
        break;
      }
      j |= ((int)in[idx++] - OFFSET) << 6;
      while ((int)in[idx] < OFFSET && (int)in[idx]!=0)
      idx++;
      if (0==(int)in[idx])
      {
        rv[i] = (int)((j >> 16) & 0xff);
        rv[i + 1] = (int)((j >> 8) & 0xff);
        rv[i + 2] = (int)(j & 0xff);
        break;
      }
      j |= ((int)in[idx++] - OFFSET);
      rv[i] = (int)((j >> 16) & 0xff);
      rv[i + 1] = (int)((j >> 8) & 0xff);
      rv[i + 2] = (int)(j & 0xff);
    }
    return rv;
  }
}
