/*
   ***************************************************************************
   *
   * Datei:
   *      RSysCrypt.c
   *
   * Inhalt:
   *
   *      --- Globale Routinen ---
   *
   *    int OpenCryptWindow ( void );
   *    void CryptFile ( void );
   *
   *      --- Lokale  Routinen ---
   *
   *    static UBYTE *Decrypt ( UBYTE *Source , long SourceLen , UBYTE *Destination , UBYTE *Key , WORD KeyLen );
   *    static UBYTE *Encrypt ( UBYTE *Source , long SourceLen , UBYTE *Destination , UBYTE *Key , WORD KeyLen );
   *    static UBYTE Automaton ( void );
   *    static void Crypt ( int encrypt );
   *    static void MakeROT13 ( UBYTE *buff , UBYTE *dest );
   *    static void refreshstrgad ( GADGET *gad , char *str );
   *    static void WindTitleInfo ( char *msg );
   *
   * Bemerkungen:
   *      Verschlüsselungsroutinen von RSys.
   *
   * Erstellungsdatum:
   *      07-Jul-93     Rolf Böhme
   *
   * Änderungen:
   *      07-Jul-93     Rolf Böhme        Erstellung
   *
   ***************************************************************************
 */

#include "RSys.h"
#include "protos.h"

 /*
  * Standardtyp der Verschlüsselungart.
  */
static int type = CRY;

 /*
  * Die Intuition-Objekte für den Aufbau des
  * Crypt-Windows.
  */
static WINDOW *CryptWnd = NULL;
static GADGET *CryptGList = NULL;
static GADGET *CryptGadgets[8];
static UWORD CryptLeft = 213;
static UWORD CryptTop = 76;
static UWORD CryptWidth = 278;
static UWORD CryptHeight = 76;
static UBYTE CryptWdt[100];	/*GMD */

 /*
  * Diese Datei-lokale Struktur beinhaltet die Namen
  * der Original- und der verschlüsselten Datei, sowie
  * das Paßwort im Falle der Verschlüsselung mit dem
  * "zellulären Automaten".
  */

static RSYS_cryptdata cd =

{
  "", "", "tiger"
};

static UBYTE *TypeCYGad0Labels[] =
{
  (UBYTE *) "Crypt",
  (UBYTE *) "ROT 13",
  NULL};

static UWORD CryptGTypes[] =
{
  STRING_KIND,
  STRING_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  BUTTON_KIND,
  STRING_KIND,
  CYCLE_KIND
};

static NEWGADGET CryptNGad[] =
{
  66, 5, 132, 13, (UBYTE *) "Source", NULL, GD_SourceStrGad, PLACETEXT_LEFT, NULL, NULL,
  66, 19, 132, 13, (UBYTE *) "Dest", NULL, GD_CryptStrGad, PLACETEXT_LEFT, NULL, NULL,
  204, 5, 69, 13, (UBYTE *) "Select", NULL, GD_SelSourceGad, PLACETEXT_IN, NULL, NULL,
  204, 19, 69, 13, (UBYTE *) "Select", NULL, GD_SelDestGad, PLACETEXT_IN, NULL, NULL,
  98, 59, 81, 13, (UBYTE *) "Encrypt", NULL, GD_CryptGad, PLACETEXT_IN, NULL, NULL,
  192, 59, 81, 13, (UBYTE *) "Decrypt", NULL, GD_DeccryptGad, PLACETEXT_IN, NULL, NULL,
  66, 39, 207, 13, (UBYTE *) "Passwd", NULL, GD_PwdGad, PLACETEXT_LEFT, NULL, NULL,
  4, 59, 81, 13, NULL, NULL, GD_TypeCYGad, 0, NULL, NULL
};

static ULONG *CryptGTags[] =
{
  (ULONG *) (GTST_MaxChars), (ULONG *) MAXLINESIZE, (ULONG *) (TAG_DONE),
  (ULONG *) (GTST_MaxChars), (ULONG *) MAXLINESIZE, (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (TAG_DONE),
  (ULONG *) (GTST_MaxChars), (ULONG *) 256, (ULONG *) (TAG_DONE),
  (ULONG *) (GTCY_Labels), (ULONG *) & TypeCYGad0Labels[0], (ULONG *) (TAG_DONE)
};

 /*
  * Die Prozedur WindTitleInfo() läßt eine Nachricht in
  * der Titelzeile des Crypt-Fensters erscheinen.
  */
static void
WindTitleInfo (char *msg)
{
  SetWindowTitles (CryptWnd, (UBYTE *) (msg), (UBYTE *) (-1));
  return;
}

 /*
  * OpenCryptWindow() öffnet ein Fenster mit allen Kontrollelementen
  */
int
OpenCryptWindow (void)
{
  NEWGADGET ng;
  GADGET *g;
  UWORD lc, tc;
  UWORD wleft = CryptLeft, wtop = CryptTop, ww, wh;
  int gl[] =
  {0, 1, 6};

  DPOS;

  AdjustWindowDimensions (Scr, CryptLeft, CryptTop, CryptWidth, CryptHeight,
			  &wleft, &wtop, &ww, &wh);

  if (!(g = CreateContext (&CryptGList)))
    return 1L;

  for (lc = 0, tc = 0; lc < Crypt_CNT; lc++)
    {

      CopyMem ((char *) &CryptNGad[lc], (char *) &ng,
	       (long) sizeof (NEWGADGET));

      ng.ng_VisualInfo = VisualInfo;
      ng.ng_TextAttr = Font;
      ng.ng_LeftEdge = OffX + ComputeX (ng.ng_LeftEdge);
      ng.ng_TopEdge = OffY + ComputeY (ng.ng_TopEdge);
      ng.ng_Width = ComputeX (ng.ng_Width);
      ng.ng_Height = ComputeY (ng.ng_Height);

      CryptGadgets[lc] = g = CreateGadgetA ((ULONG) CryptGTypes[lc], g, &ng, (TAGITEM *) & CryptGTags[tc]);

      makelabelvisible (CryptGadgets[lc]);

      while (CryptGTags[tc])
	tc += 2;
      tc++;

      if (NOT g)
	return 2L;
    }

  strcpy (CryptWdt, get_vers (" - Crypting Files"));	/*GMD */

  if (!(CryptWnd = OpenWindowTags (NULL,
				   WA_Left, wleft,
				   WA_Top, wtop,
				   WA_Width, ww,
				   WA_Height, wh,
				   WA_IDCMP, IDCMP_VANILLAKEY |
				   STRINGIDCMP |
				   BUTTONIDCMP |
				   IDCMP_CLOSEWINDOW |
				   IDCMP_REFRESHWINDOW,
				   WA_Flags, WFLG_DRAGBAR |
				   WFLG_DEPTHGADGET |
				   WFLG_CLOSEGADGET |
				   WFLG_SMART_REFRESH |
				   WFLG_ACTIVATE |
				   WFLG_RMBTRAP,
				   WA_Title, CryptWdt,
				   WA_PubScreenFallBack, TRUE,
				   WA_PubScreen, Scr,
				   TAG_DONE)))
    return 4L;

  RefreshRastPort (CryptWnd, CryptGadgets, gl, 3, FALSE, CryptGList);

  return NULL;
}

 /*
  * refreshstrgad() erneuert den Inhalt eines Stringgadgets
  */
static void
refreshstrgad (GADGET * gad, char *str)
{
  GT_SetGadgetAttrs (gad, CryptWnd, NULL, GTST_String, (UBYTE *) str, TAG_DONE);

  return;
}

 /*
  * Die nächsten Kommentare sind dem Programm-Quelltext von TERM entnommen.
  * Olsen, danke für die Comments :-)
  */

 /*
  * The cell ring and the ring index pointers.
  */

static UBYTE Cell[2][CELL_WIDTH + 2], From, To;

 /*
  *  Automaton():
  *
  *     A cellular automaton working on a ring of celles, producing
  *     random data in each single cell .
  */

static UBYTE
Automaton (void)
{
  WORD i;

  /*
   * Operate on the cell ring...
   */

  for (i = 1; i <= CELL_WIDTH; i++)
    Cell[To][i] = Cell[From][i - 1] ^ (Cell[From][i] | Cell[From][i + 1]);

  /*
   * Operate on first and last element.
   */

  Cell[To][0] = Cell[From][CELL_WIDTH + 1] ^ (Cell[From][0] | Cell[From][1]);
  Cell[To][CELL_WIDTH + 1] = Cell[From][CELL_WIDTH] ^ (Cell[From][CELL_WIDTH + 1] | Cell[From][0]);

  /*
   * Swap cell rings.
   */
  To = From;
  From ^= 1;

  /*
   * Return contents of first cell.
   */
  return (Cell[From][0]);
}

 /*
  * Encrypt(UBYTE *Source,UBYTE *Destination,UBYTE *Key):
  * Encrypt data using cellular automaton as a random number generator.
  */

static UBYTE *
Encrypt (UBYTE * Source, long SourceLen, UBYTE * Destination, UBYTE * Key, WORD KeyLen)
{
  long i, j;

  /*
   * Set up cell ring index pointers.
   */
  From = 0;
  To = 1;

  /*
   * Initialize the cell ring with the key contents.
   */
  for (i = 0, j = KeyLen - 1; i < CELL_WIDTH + 2; i++)
    {
      Cell[0][i] = Key[j];

      if (j)
	j--;
      else
	j = KeyLen - 1;
    }

  /*
   * Encrypt the source data.
   */
  for (i = 0; i < SourceLen; i++)
    Destination[i] = (UBYTE) (((WORD) Source[i] + Automaton ()) % 256);

  /*
   * Return result.
   */
  return Destination;
}

 /*
  * Decrypt(UBYTE *Source,UBYTE *Destination,UBYTE *Key):
  *
  *     Decrypt data using cellular automaton as a random number generator.
  */

static UBYTE *
Decrypt (UBYTE * Source, long SourceLen, UBYTE * Destination, UBYTE * Key, WORD KeyLen)
{
  long i, j, Code;

  /*
   * Set up cell ring index pointers.
   */
  From = 0;
  To = 1;

  /*
   * Initialize the cell ring with the key contents.
   */
  for (i = 0, j = KeyLen - 1; i < CELL_WIDTH + 2; i++)
    {
      Cell[0][i] = Key[j];

      if (j)
	j--;
      else
	j = KeyLen - 1;
    }

  /*
   * Decrypt the source data.
   */
  for (i = 0; i < SourceLen; i++)
    {
      if ((Code = Source[i] - Automaton ()) < 0)
	Code = 256 + Code;

      Destination[i] = (UBYTE) Code;
    }

  return Destination;
}

 /*
  * Die Prozedur MakeROT13() erzeugt aus einem
  * Eingabestring buff eine Ausgabestring dest
  * dergestalt, daß jeder Buchstabe um 13 Zeichen nach
  * vorne oder hinten verschoben wird. Hier die
  * Translationsliste:
  *
  * Input:              abcdefghijklmnopqrstuvwxyz
  * Output:             nopqrstuvwxyzabcdefghijklm
  *
  * Input:              ABCDEFGHIJKLMNOPQRSTUVWXYZ
  * Output:             NOPQRSTUVWXYZABCDEFGHIJKLM
  */
static void
MakeROT13 (UBYTE * buff, UBYTE * dest)
{
  UBYTE *destptr = &dest[0];
  char ch;

  strcpy ((char *) dest, (char *) buff);

  while (*destptr)
    {
      ch = (char) (*destptr);

      if (isalpha ((int) ch))
	{
	  if (((ch >= 'A') && (ch < 'N')) || ((ch >= 'a') && (ch < 'n')))
	    *destptr = (UBYTE) (ch + 13);

	  if (((ch >= 'N') && (ch <= 'Z')) || ((ch >= 'n') && (ch <= 'z')))
	    *destptr = (UBYTE) (ch - 13);
	}

      destptr++;
    }

  return;
}

 /*
  * Crypt() ver- oder entschlüsselt eine Datei
  */
static void
Crypt (int encrypt)
{
  long fsize;
  char pwd[PWDLEN + 1], cpwd[PWDLEN + 1];
  BPTR Dfile, Cfile;
  UBYTE *Dbuffer, *Cbuffer;
  int pwdlen;

  if (strlen (cd.cr_pwd) == 0)
    strcpy (pwd, "tiger");
  else
    strncpy (pwd, cd.cr_pwd, PWDLEN);

  pwdlen = strlen (pwd);

  if (encrypt)
    {
      if (NOT (exist (cd.cr_datafile)))
	{
	  ErrorHandle (cd.cr_datafile, FILE_ERR, EXIST_FAIL, NO_KILL);
	  return;
	}

      if (type == R13)
	{

	  filetype (cd.cr_datafile);
	  if (global_type != TYPE_TEXT)
	    {
	      ErrorHandle (cd.cr_datafile, FILE_ERR, TYPE_FAIL, NO_KILL);
	      return;
	    }
	}

      fsize = SizeOfFile (cd.cr_datafile);
      if (fsize == 0L)
	{
	  ErrorHandle (cd.cr_datafile, FILE_ERR, SIZE_FAIL, NO_KILL);
	  return;
	}

      if (exist (cd.cr_cryptfile) && NOT (Question (CryptWnd, "Crypt file exist!\n"
				    "Do you want to overwrite them?", YES)))
	return;

      if (Dfile = Open ((UBYTE *) cd.cr_datafile, MODE_OLDFILE))
	{
	  if (Cfile = Open ((UBYTE *) cd.cr_cryptfile, MODE_NEWFILE))
	    {
	      if ((Dbuffer = MyAllocVec ((fsize + 1) * sizeof (UBYTE), MEMF_CLEAR, NO_KILL)) &&
		  (Cbuffer = MyAllocVec ((fsize + 1) * sizeof (UBYTE), MEMF_CLEAR, NO_KILL)))
		{
		  WindTitleInfo ("Reading datafile...");
		  if (fsize == Read (Dfile, Dbuffer, fsize))
		    {
		      if (NOT (type))
			{
			  WindTitleInfo ("Encrypt password...");
			  Encrypt ((UBYTE *) pwd, (long) pwdlen, (UBYTE *) cpwd,
				   (UBYTE *) pwd, (WORD) pwdlen);

			  if (pwdlen != Write (Cfile, cpwd, pwdlen))
			    ErrorHandle (cd.cr_cryptfile, FILE_ERR, WRITE_FAIL, NO_KILL);
			  else
			    {
			      WindTitleInfo ("Encrypt data file...");
			      Cbuffer = Encrypt (Dbuffer, (long) fsize,
				     Cbuffer, (UBYTE *) pwd, (WORD) pwdlen);
			    }
			}
		      else
			{
			  WindTitleInfo ("Rotate data file...");
			  MakeROT13 (Dbuffer, Cbuffer);
			}

		      WindTitleInfo (type ? "Writing ROT13 file..." : "Writing crypt file...");
		      if (fsize != Write (Cfile, Cbuffer, fsize))
			ErrorHandle (cd.cr_cryptfile, FILE_ERR, WRITE_FAIL, NO_KILL);
		    }
		  else
		    ErrorHandle (cd.cr_datafile, FILE_ERR, READ_FAIL, NO_KILL);

		  MyFreeVec (Cbuffer);
		  MyFreeVec (Dbuffer);
		}

	      Close (Cfile);
	    }
	  else
	    ErrorHandle (cd.cr_cryptfile, FILE_ERR, OPEN_FAIL, NO_KILL);

	  Close (Dfile);
	}
      else
	ErrorHandle (cd.cr_datafile, FILE_ERR, OPEN_FAIL, NO_KILL);
    }
  else
    {
      if (NOT (exist (cd.cr_cryptfile)))
	{
	  ErrorHandle (cd.cr_cryptfile, FILE_ERR, EXIST_FAIL, NO_KILL);
	  return;
	}

      fsize = SizeOfFile (cd.cr_cryptfile);
      if (fsize == 0L)
	{
	  ErrorHandle (cd.cr_cryptfile, FILE_ERR, SIZE_FAIL, NO_KILL);
	  return;
	}

      if (exist (cd.cr_datafile) &&
	  NOT (Question (CryptWnd, "Data file exist!\n" "Do you want to overwrite them?", YES)))
	return;

      if (Dfile = Open ((UBYTE *) cd.cr_datafile, MODE_NEWFILE))
	{
	  if (Cfile = Open ((UBYTE *) cd.cr_cryptfile, MODE_OLDFILE))
	    {
	      if ((Dbuffer = MyAllocVec ((fsize + 1) * sizeof (UBYTE), MEMF_CLEAR, NO_KILL)) &&
		  (Cbuffer = MyAllocVec ((fsize + 1) * sizeof (UBYTE), MEMF_CLEAR, NO_KILL)))
		{
		  WindTitleInfo (type ? "Reading ROT13 file..." : "Reading crypt file...");
		  if (fsize == Read (Cfile, Cbuffer, fsize))
		    {
		      if (NOT (type))
			{
			  WindTitleInfo ("Decrypt password...");

			  strncpy (cpwd, (char *) Cbuffer, pwdlen);
			  Decrypt ((UBYTE *) cpwd, (long) pwdlen,
			       (UBYTE *) pwd, (UBYTE *) pwd, (WORD) pwdlen);

			  if (strcmp (cd.cr_pwd, pwd))
			    ErrorHandle ("Password", MISC_ERR, WRONG_FAIL, NO_KILL);
			  else
			    {
			      WindTitleInfo ("Decrypt crypt file...");
			      Dbuffer = Decrypt (&Cbuffer[pwdlen], (long) (fsize - pwdlen),
				     Dbuffer, (UBYTE *) pwd, (WORD) pwdlen);
			    }
			}
		      else
			{
			  WindTitleInfo ("Derotate ROT13-File...");
			  MakeROT13 (Cbuffer, Dbuffer);
			}

		      WindTitleInfo ("Writing data file...");

		      fsize -= (type ? 0 : pwdlen);
		      if (fsize != Write (Dfile, Dbuffer, fsize))
			ErrorHandle (cd.cr_datafile, FILE_ERR, WRITE_FAIL, NO_KILL);
		    }
		  else
		    ErrorHandle (cd.cr_cryptfile, FILE_ERR, READ_FAIL, NO_KILL);

		  MyFreeVec (Cbuffer);
		  MyFreeVec (Dbuffer);
		}

	      Close (Cfile);
	    }
	  else
	    ErrorHandle (cd.cr_cryptfile, FILE_ERR, OPEN_FAIL, NO_KILL);

	  Close (Dfile);
	}
      else
	ErrorHandle (cd.cr_datafile, FILE_ERR, OPEN_FAIL, NO_KILL);
    }

  strcpy (CryptWdt, get_vers (" - Crypting Files"));	/*GMD */

  WindTitleInfo ((char *) CryptWdt);

  return;
}

 /*
  * CryptFile() bietet eine kleine Benutzeroberfläche an und
  * kontrolliert die Ein- und Ausgabedateien sowie das Paßwort
  */
void
CryptFile (void)
{
  INTUIMESSAGE *message;
  ULONG class, code;
  APTR object;
  APTR reqcrypt = NULL;
  char *gadbuff;
  int GID;

  DPOS;

  HandleHelp (MN_CryptFile);

  Flags.quit_cr = 0;

  cd.cr_datafile[0] = STRINGEND;
  cd.cr_cryptfile[0] = STRINGEND;
  strcpy (cd.cr_pwd, "tiger");

  if (OpenASysWindow (OpenCryptWindow, NO_KILL))
    {
      LockMainWindow (WIN_LOCK);

      refreshstrgad (CryptGadgets[GD_SourceStrGad - GD_SourceStrGad], cd.cr_datafile);
      refreshstrgad (CryptGadgets[GD_CryptStrGad - GD_SourceStrGad], cd.cr_cryptfile);
      refreshstrgad (CryptGadgets[GD_PwdGad - GD_SourceStrGad], cd.cr_pwd);

      do
	{
	  Wait (1L << CryptWnd->UserPort->mp_SigBit);

	  while ((message = (INTUIMESSAGE *)
		  GT_GetIMsg (CryptWnd->UserPort)) != NULL)
	    {
	      object = message->IAddress;
	      class = message->Class;
	      code = message->Code;

	      GT_ReplyIMsg (message);

	      switch (class)
		{
		case IDCMP_GADGETUP:
		  GID = ((GADGET *) object)->GadgetID;

		  HandleHelp ((enum RSysNumbers) GID);

		  switch (GID)
		    {
		    case GD_SourceStrGad:
		      gadbuff = gadgetbuff (CryptGadgets[GD_SourceStrGad - GD_SourceStrGad]);

		      strncpy (cd.cr_datafile, gadbuff, MAXFULLNAME);
		      strncpy (cd.cr_cryptfile, gadbuff, MAXFULLNAME);
		      strcat (cd.cr_cryptfile, (type ? ".ROT" : ".CPT"));

		      refreshstrgad (CryptGadgets[GD_CryptStrGad - GD_SourceStrGad],
				     cd.cr_cryptfile);
		      break;

		    case GD_CryptStrGad:
		      gadbuff = gadgetbuff (CryptGadgets[GD_CryptStrGad - GD_SourceStrGad]);
		      strncpy (cd.cr_cryptfile, gadbuff, MAXFULLNAME);
		      break;

		    case GD_SelSourceGad:
		      if (GetFile (CryptWnd, NULL, NULL, NULL,
				   "Select File to crypt", "Select"))
			{
			  strncpy (cd.cr_datafile, (char *) _fullpath,
				   MAXFULLNAME);

			  strncpy (cd.cr_cryptfile, (char *) _fullpath,
				   MAXFULLNAME - 4);
			  strcat (cd.cr_cryptfile, (type ? ".ROT" : ".CPT"));

			  refreshstrgad (CryptGadgets[GD_SourceStrGad - GD_SourceStrGad],
					 cd.cr_datafile);
			  refreshstrgad (CryptGadgets[GD_CryptStrGad - GD_SourceStrGad],
					 cd.cr_cryptfile);
			}

		      break;

		    case GD_SelDestGad:
		      if (GetFile (CryptWnd, NULL, NULL,
				   (type ? "#?.ROT" : "#?.CPT"),
			 (type ? "Select ROT13 file" : "Select Crypt file"),
				   "Select"))
			{
			  strncpy (cd.cr_cryptfile, (char *) _fullpath, MAXFULLNAME);
			  refreshstrgad (CryptGadgets[GD_CryptStrGad - GD_SourceStrGad],
					 cd.cr_cryptfile);
			}
		      break;

		    case GD_CryptGad:
		      reqcrypt = LockWindow (CryptWnd);

		      Crypt (TRUE);

		      UnlockWindow (reqcrypt);
		      break;

		    case GD_DeccryptGad:
		      reqcrypt = LockWindow (CryptWnd);

		      Crypt (FALSE);

		      UnlockWindow (reqcrypt);
		      break;

		    case GD_PwdGad:
		      strncpy (cd.cr_pwd, gadgetbuff (CryptGadgets[GD_PwdGad - GD_SourceStrGad]), 20);
		      break;

		    case GD_TypeCYGad:
		      type = (int) code;
		      EnableGadget (CryptWnd, CryptGadgets[GD_PwdGad - GD_SourceStrGad],
				    NOT (type));

		      if (cd.cr_datafile[0])
			{
			  strncpy (cd.cr_cryptfile, cd.cr_datafile, MAXFULLNAME - 4);
			  strcat (cd.cr_cryptfile, (type ? ".ROT" : ".CPT"));

			  refreshstrgad (CryptGadgets[GD_CryptStrGad - GD_SourceStrGad],
					 cd.cr_cryptfile);
			}
		      break;
		    }

		  break;

		case IDCMP_VANILLAKEY:
		  if (code == ESC)
		    Flags.quit_cr = 1;
		  break;

		case IDCMP_CLOSEWINDOW:
		  Flags.quit_cr = 1;
		  break;
		}
	    }
	}
      while (NOT (Flags.quit_cr));

      CloseASysWindow (&CryptWnd, &CryptGList, NULL);

      LockMainWindow (WIN_UNLOCK);
    }

  return;
}
