/*
***************************************************************************
*
* Datei:
*    RSysCrypt.c
*
* Inhalt:
*    int OpenCryptWindow(void);
*    void CryptFile(void);
*
* Bemerkungen:
*    Verschlüsselungsroutinen von RSys.
*
* Erstellungsdatum:
*    07-Jul-93    Rolf Böhme
*
* Änderungen:
*    07-Jul-93    Rolf Böhme    Erstellung
*
***************************************************************************
*/

#include "RSysFunc.h"
#include "RSysDebug.h"

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

static enum
{
   GD_SourceStrGad,
   GD_CryptStrGad,
   GD_SelSourceGad,
   GD_SelDestGad,
   GD_CryptGad,
   GD_DeccryptGad,
   GD_PwdGad,
   GD_TypeCYGad
};

#define Crypt_CNT 8

   /*
    * Die Intuition-Objekte für den Aufbau des
    * Crypt-Windows.
    */
static struct Window *CryptWnd = NULL;
static struct Gadget *CryptGList = NULL;
static struct Gadget *CryptGadgets[8];
static UWORD CryptLeft = 213;
static UWORD CryptTop = 76;
static UWORD CryptWidth = 278;
static UWORD CryptHeight = 76;
static UBYTE *CryptWdt = (UBYTE *) NAME " " VERSION " - Crypting Files";

   /*
    * 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 struct _cryptdata
{
   char  cr_datafile[MAXFULLNAME],
         cr_cryptfile[MAXFULLNAME],
         cr_pwd[20];
}     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 struct 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 )
{
   struct NewGadget   ng;
   struct Gadget   *g;
   UWORD      lc, tc;
   UWORD      wleft = CryptLeft, wtop = CryptTop, ww, wh;
   int gl[] = {0,1,6};

   DPOS;

   ComputeFont( Scr,CryptWidth, CryptHeight );

   ww = ComputeX( CryptWidth );
   wh = ComputeY( CryptHeight );

   if (( wleft + ww + OffX + Scr->WBorRight ) > Scr->Width ) wleft = Scr->Width - ww;
   if (( wtop + wh + OffY + Scr->WBorBottom ) > Scr->Height ) wtop = Scr->Height - wh;

   ww = compute((UWORD) (OffX + Scr->WBorRight), FontX, (int)CryptWidth);
   wh = compute((UWORD) (OffY + Scr->WBorBottom), FontY, (int)CryptHeight);

   CenterWindow(Scr, &wtop, &wleft, 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( struct 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, ( struct TagItem * )&CryptGTags[ tc ] );

      makelabelvisible(CryptGadgets[lc]);

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

      if ( NOT g )
         return( 2L );
   }

   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_Gadgets,   CryptGList,
            WA_Title,   CryptWdt,
            WA_PubScreenFallBack,TRUE,
            WA_PubScreen, Scr,
            TAG_DONE )))
      return( 4L );

   RefreshRastPort(CryptWnd,CryptGadgets,gl, 3);

   if(Flags.autofront)
   {
      WindowToFront(CryptWnd);
      ActivateWindow(CryptWnd);
   }

   return( 0L );
}

   /*
    * refreshstrgad() erneuert den Inhalt eines Stringgadgets
    */
static void
refreshstrgad(struct 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 width of the cell ring is defined here. A word of warning: never
     * encrypt a file and forget the access password, since it will be
     * extremely hard to break the code (with 30 cells there are 256^30
     * possible values each cell may be initialized to which means that
     * you could have to try more than 1.7e+72 combinations for each single
     * password character before actually finding the matching password).
     * See `Random sequence generation by cellular automata' by Steven
     * Wolfram (Institute for Advanced Study; Advances in Applied
     * Mathematics) for more information.
     */

#define CELL_WIDTH 30

    /*
     * 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)
{
   register 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)
{
   register 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)
{
   register 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(FILE_ERR, EXIST_FAIL, NO_KILL);
         return;
      }

      if (type == R13)
      {
         extern int global_type;

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

      fsize = SizeOfFile(cd.cr_datafile);
      if (fsize == 0L)
      {
         ErrorHandle(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(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(FILE_ERR, WRITE_FAIL, NO_KILL);
               }
               else
                  ErrorHandle(FILE_ERR, READ_FAIL, NO_KILL);

               MyFreeVec(Cbuffer);
               MyFreeVec(Dbuffer);
            }

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

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

      fsize = SizeOfFile(cd.cr_cryptfile);
      if (fsize == 0L)
      {
         ErrorHandle(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(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(FILE_ERR, WRITE_FAIL, NO_KILL);
               }
               else
                  ErrorHandle(FILE_ERR, READ_FAIL, NO_KILL);

               MyFreeVec(Cbuffer);
               MyFreeVec(Dbuffer);
            }

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

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

   WindTitleInfo((char *)CryptWdt);

   return;
}

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

   DPOS;

   Flags.quit_cr = 0;

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

   if (OpenASysWindow(OpenCryptWindow,NO_KILL))
   {
      if (SysWnd)
         req = LockWindow(SysWnd);

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

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

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

            GT_ReplyIMsg(message);

            switch (class)
            {
               case IDCMP_GADGETUP:
                  switch (((struct Gadget *) object)->GadgetID)
                  {
                     case GD_SourceStrGad:
                        gadbuff = gadgetbuff(CryptGadgets[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],
                                      cd.cr_cryptfile);
                        break;

                     case GD_CryptStrGad:
                        gadbuff = gadgetbuff(CryptGadgets[GD_CryptStrGad]);
                        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],
                                         cd.cr_datafile);
                           refreshstrgad(CryptGadgets[GD_CryptStrGad],
                                         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],
                                         cd.cr_cryptfile);
                        }
                        break;

                     case GD_CryptGad:
                        reqcrypt = LockWindow(CryptWnd);

                        Crypt(TRUE);

                        if(reqcrypt)
                           UnlockWindow(reqcrypt);
                        break;

                     case GD_DeccryptGad:
                        reqcrypt = LockWindow(CryptWnd);

                        Crypt(FALSE);

                        if(reqcrypt)
                           UnlockWindow(reqcrypt);
                        break;

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

                     case GD_TypeCYGad:
                        type = (int)code;
                        GT_SetGadgetAttrs(CryptGadgets[GD_PwdGad], CryptWnd,
                                       NULL,
                                       GA_Disabled, type,
                                       TAG_DONE);

                        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],
                                         cd.cr_cryptfile);
                        }
                        break;
                  }

                  break;

               case IDCMP_VANILLAKEY:
                  if (code == '\33')
                     Flags.quit_cr = 1;
                  break;

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

      CloseASysWindow(&CryptWnd, &CryptGList, NULL);

      if (SysWnd)
      {
         UnlockWindow(req);
         RefreshMainWindowPattern();
         RefreshList(LastID);
      }
   }

   return;
}

