/* OpenPGP for Palm III */

#pragma pack(2)
#include <Common.h>
#include <System/SysAll.h>
#include <UI/UIAll.h>
#include <System/SysEvtMgr.h>

#include "opgptst.h"
#define OpgptstAppID    'OPGP'
#define OpgptstDBType   'Data'

static int StartApplication(void);
static Boolean OpenDatabase(void);
static void EventLoop(void);
static void StopApplication(void);
static Boolean opgptst(EventPtr event);
static void RunOpenPGP(void);

FieldPtr pphrase;

DmOpenRef MemoDB;

DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags)
{
  int error;

  if (cmd == sysAppLaunchCmdNormalLaunch) {
    if (error = StartApplication())
      return error;
    EventLoop();                // Event loop

    StopApplication();          // Application stop code

  }
  return 0;
}

static int StartApplication(void)
{
  int error;

  if (error = OpenDatabase())
    return error;
  FrmGotoForm(formID_opgptst);
}

static Boolean OpenDatabase(void)
{
  LocalID memoid;

  memoid = DmFindDatabase(0, "MemoDB");
  MemoDB = DmOpenDatabase(0, memoid, dmModeReadWrite | dmModeShowSecret);
  if (!MemoDB)
    ErrDisplay("No Memo DB");
  return 0;
}

static void EventLoop(void)
{
  short err;
  int formID;
  FormPtr form;
  EventType event;

  do {
    EvtGetEvent(&event, 200);

    if (SysHandleEvent(&event))
      continue;
    if (MenuHandleEvent((void *) 0, &event, &err))
      continue;

    if (event.eType == frmLoadEvent) {
      formID = event.data.frmLoad.formID;
      form = FrmInitForm(formID);
      FrmSetActiveForm(form);
      switch (formID) {
      case formID_opgptst:
        FrmSetEventHandler(form, (FormEventHandlerPtr) opgptst);
        break;
      }
    }
    FrmDispatchEvent(&event);
  } while (event.eType != appStopEvent);
}

static VoidHand ppbuf;
static char *ptext;

static void StopApplication(void)
{
  FldSetTextHandle(pphrase, NULL);
  MemHandleUnlock(ppbuf);
  MemHandleFree(ppbuf);
  DmCloseDatabase(MemoDB);
}

static Boolean opgptst(EventPtr event)
{
  FormPtr form;
  int handled = 0;

  switch (event->eType) {
  case frmOpenEvent:

    form = FrmGetActiveForm();
    pphrase = FrmGetObjectPtr(form, FrmGetObjectIndex(form, fieldID_text));
    ppbuf = MemHandleNew(256);
    ptext = MemHandleLock(ppbuf);
    *ptext = 0;
    FldSetTextHandle(pphrase, (Handle) ppbuf);
    FrmDrawForm(form);
    handled = 1;
    break;

  case ctlSelectEvent:         // A control button was pressed and released.

    if (event->data.ctlEnter.controlID == buttonID_RunOpenPGP) {
      RunOpenPGP();
      handled = 1;
    }
    break;

  case menuEvent:
    switch (event->data.menu.itemID) {
    case menuitemID_RunOpenPGP:
      RunOpenPGP();
      break;
    case menuitemID_about:
      FrmAlert(alertID_about);
      break;
    }
    handled = 1;
    break;

  case nilEvent:
    handled = 1;
    break;
  }
  return handled;
}

#include <System/FileStream.h>
#include <libopgp.h>

#define NORMF fileModeReadWrite | fileModeAnyTypeCreator
#define ADD2F fileModeAppend | fileModeAnyTypeCreator
#define TEMPF NORMF | fileModeTemporary

const char hbnr[5][24] =
{"SHA1", "RIPEMD160", "HAVAL-5-160", "MD2", "TIGER192"};

int syma = 2, diga = 2;

#define PipeSwap { \
    if( file ) FileClose(file); \
    EvtResetAutoOffTimer(); \
    FileRewind(ofile); \
    FileRead(ofile, &ctb, 1, 1, NULL); \
    FileRewind(ofile); \
    FileControl(fileOpDestructiveReadMode, ofile, NULL, NULL); \
    file = ofile; }

ULong len1;
VoidPtr ptr1;
UInt dbrec;
char passph[256];

void procmsg()
{
  FileHand file = NULL, ofile;
  unsigned char ctb;
  int i, j;
  ULong ll;
  char litfname[256];
  VoidHand hand2;
  VoidPtr ptr2;

  WinDrawChars("Processing Message  ", 20, 0, 60);

  ofile = FileOpen(0, "TEMPX", 'DATA', 'OPGP', TEMPF, NULL);
  FileWrite(ofile, ptr1, 1, len1, NULL);

  PipeSwap;
  WinDrawChars("Dearmoring          ", 20, 0, 60);
  ofile = FileOpen(0, "TEMPA", 'DATA', 'OPGP', TEMPF, NULL);
  PGP_dearmor(file, ofile);
  for (;;) {

    PipeSwap;

    if (ctb < 0xc0)
      ctb = ((ctb & 0x3c) >> 2);
    else
      ctb -= 0xc0;

    if (ctb == 1 || ctb == 3 || ctb == 10) {  /* incl 0xa8,3,PGP */
      WinDrawChars("Decrypting          ", 20, 0, 60);
      ofile = FileOpen(0, "TEMPB", 'DATA', 'OPGP', TEMPF, NULL);
/*PASSPH-seckey */
      if (PGP_decry(file, ofile, passph))
        break;
      continue;

    } else if (ctb == 8) {
      WinDrawChars("Decompressing       ", 20, 0, 60);
      ofile = FileOpen(0, "TEMPC", 'DATA', 'OPGP', TEMPF, NULL);
      PGP_unzip(file, ofile);
      continue;

    } else if (ctb == 9) {      /* naked symmetrical */
      void *hctx;
      unsigned char *bp, xfix[] =
      {4, 1, 0, 1};
      WinDrawChars("Decompressing Conv  ", 20, 0, 60);
      bp = xfix;
/*PASSPH-conventional */
      PGP_gts2k(&bp, passph, &hctx);
      PGP_cdec(file, ofile, &hctx, 1);

      continue;

    } else if (ctb == 2 || ctb == 4 || ctb == 11) {  /* lit or sig */
      WinDrawChars("Extract/Sigcheck    ", 20, 0, 60);
      ofile = FileOpen(0, "TEMPD", 'DATA', 'OPGP', TEMPF, NULL);
      i = PGP_delcs(file, ofile, &j, litfname);
      if (j) {
        if (i)
          WinDrawChars("Sigcheck Failed     ", 20, 0, 70);
        else
          WinDrawChars("Signature Verified  ", 20, 0, 70);
      }
      FileClose(file);

      ll = FileTell(ofile, NULL, NULL) - 1;
      file = ofile;
      FileRewind(file);
      FileControl(fileOpDestructiveReadMode, file, NULL, NULL);

      WinDrawChars("Saving:             ", 20, 60, 60);
      WinDrawChars(litfname, strlen(litfname), 80, 60);

      DmWrite(ptr1, 0, "DONE", 4);
      DmReleaseRecord(MemoDB, dbrec, 1);

      if (!(hand2 = DmNewRecord(MemoDB, &dbrec, ll + strlen(litfname) + 1)))
        return;
      ptr2 = MemHandleLock(hand2);

      DmWrite(ptr2, 0, litfname, strlen(litfname));
      DmWrite(ptr2, strlen(litfname), "\n", 1);

      FileDmRead(file, ptr2, strlen(litfname) + 1, 1, ll, NULL);
      FileClose(file);
      MemHandleUnlock(hand2);
      break;
    } else
      break;
  }
}

void doenc()
{
  FileHand file = NULL, ofile;
  ULong ll, ll2;
  unsigned char ctb;
  keyid_t keys[16];
  char *cp = ptr1;
  char litfname[256];
  VoidHand hand2;
  VoidPtr ptr2;

  /* parse command line - get key and optional fname */
  ofile = FileOpen(0, "TEMP1", 'DATA', 'OPGP', TEMPF, NULL);
  cp = StrStr(cp, "\n");
  ll = ++cp;
  ll2 = ptr1;
  ll -= ll2;
  FileWrite(ofile, cp, 1, len1 - ll, NULL);

  cp = ptr1;
  cp += 12;
  keys[0] = 0;

  if (!StrNCompare("OPGPENCCONV", ptr1, 11))
    ll2 = ' ';
  else
    for (;;) {
      ll2 = *cp++;
      if (ll2 < '0')
        break;
      if (ll2 >= 'a')
        ll2 -= 32;
      if (ll2 > 'F')
        break;
      ll2 -= '0';
      if (ll2 > 9)
        ll2 -= 'A' - '9' - 1;

      keys[0] = (keys[0] << 4) | ll2;
    }

  if (ll2 != ' ')
    StrCopy(litfname, "_CONSOLE");
  else {
    char *cpt;

    cpt = litfname;
    while (*cp != '\n')
      *cpt++ = *cp++;
    *cpt = 0;
  }

  PipeSwap;
  WinDrawChars("Literaling/Signing  ", 20, 0, 60);
  ofile = FileOpen(0, "TEMP2", 'DATA', 'OPGP', TEMPF, NULL);

  if (!StrNCompare("OPGPENCSIGN", ptr1, 11)) {
/*PASSPH-signing */
    /* technically, I should use 'b' since it is not canonical text */
    if (PGP_elsgm(file, ofile, litfname, 1, 't', diga, 0, passph))
      return;
  } else {
    if (PGP_elsgm(file, ofile, litfname, 0, 't', 0, 0, NULL))
      return;
  }

  PipeSwap;
  WinDrawChars("Compressing         ", 20, 0, 60);
  ofile = FileOpen(0, "TEMP3", 'DATA', 'OPGP', TEMPF, NULL);
  PGP_zip(file, ofile, 1);

  PipeSwap;
  WinDrawChars("Encrypting          ", 20, 0, 60);
  ofile = FileOpen(0, "ENCRY", 'DATA', 'OPGP', TEMPF, NULL);
  /* conventional or public */

  if (!StrNCompare("OPGPENCCONV", ptr1, 11))
    ll = PGP_encry(file, ofile, 0, -1, NULL, syma, &passph);
  else
    ll = PGP_encry(file, ofile, 1, 0, keys, syma, NULL);

  if (!ll)
    DmWrite(ptr1, 0, "DONE", 4);
  else
    return;

  PipeSwap;
  WinDrawChars("Armoring            ", 20, 0, 60);
  ofile = FileOpen(0, "ARMOUT", 'DATA', 'OPGP', TEMPF, NULL);
  PGP_armor(file, ofile, NULL, NULL);

  FileClose(file);

  ll = FileTell(ofile, NULL, NULL) - 1;
  file = ofile;
  FileRewind(file);
  FileControl(fileOpDestructiveReadMode, file, NULL, NULL);

  DmReleaseRecord(MemoDB, dbrec, 1);
  hand2 = DmNewRecord(MemoDB, &dbrec, ll);
  if (hand2 == NULL)
    return;
  ptr2 = MemHandleLock(hand2);
  if (!ptr2)
    return;
  FileDmRead(file, ptr2, 0, 1, ll, NULL);
  FileClose(file);
  MemHandleUnlock(hand2);
}

void dosign()
{
  void *hctx, *signkey;
  char *cpt, *cptr = ptr1;
  keyid_t key = 0;
  int salg;
  ULong ll;
  char linebuf[512];
  FileHand file = NULL, ofile;
  VoidHand hand2;
  VoidPtr ptr2;

/*PASSPH-signing */
  if (0 >= (salg = PGP_gtkey(&signkey, passph, &key)))
    return;

  WinDrawChars("Signing             ", 20, 0, 60);

  ofile = FileOpen(0, "TEMP1", 'DATA', 'OPGP', TEMPF, NULL);
  FileWrite(ofile, "-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA1\n\n",
            1, 47, NULL);
  hctx = PGP_hini(2);
  cptr += 9;
  ll = len1 - 9;

  while (ll) {                  /* doubled - don't hash last TWO lines */
    cpt = linebuf;
    while (ll && *cptr != '\n')
      *cpt++ = *cptr++, ll--;
    if (ll)
      *cpt++ = *cptr++, ll--;
    *cpt = 0;
    PGP_ctln(linebuf);

    PGP_hblk(hctx, linebuf, StrLen(linebuf));
    cpt = linebuf;
    cpt[strlen(cpt) - 1] = 0;
    cpt[strlen(cpt) - 1] = '\n';
    FileWrite(ofile, cpt, 1, strlen(cpt), NULL);
  }
  FileWrite(ofile, "\n", 1, 1, NULL);

  DmWrite(ptr1, 0, "DONE", 4);

  /* 2 hardcoded in next because SHA1 is hardcoded above */
  ll = PGP_sigmk(signkey, hctx, linebuf, key, 2, 1, salg, NULL);
  file = FileOpen(0, "TEMP2", 'DATA', 'OPGP', TEMPF, NULL);
  FileWrite(file, linebuf, 1, ll, NULL);
  FileRewind(file);
  FileControl(fileOpDestructiveReadMode, file, NULL, NULL);
  PGP_armor(file, ofile, "SIGNATURE", NULL);
  FileClose(file);
  ll = FileTell(ofile, NULL, NULL) - 1;
  file = ofile;
  FileRewind(file);
  FileControl(fileOpDestructiveReadMode, file, NULL, NULL);

  hand2 = DmNewRecord(MemoDB, &dbrec, ll);
  if (hand2 == NULL)
    return;

  ptr2 = MemHandleLock(hand2);
  if (!ptr2)
    return;
  FileDmRead(file, ptr2, 0, 1, ll, NULL);
  FileClose(file);
  MemHandleUnlock(hand2);
  DmReleaseRecord(MemoDB, dbrec, 1);

}

void dover()
{
  int cnt, halg = 1;
  int i;
  void *hctx;
  char *cpt, *cptr = ptr1;
  FileHand file = NULL, ofile;
  ULong ll, ll2;
  unsigned char ctb;
  char linebuf[512];

  WinDrawChars("Checking Signature  ", 20, 0, 60);

  if (cpt = StrStr(cptr, "\nHash:")) {
    cpt += 7;
    for (cnt = 0; cnt < 5; cnt++)
      if (!StrNCompare(cpt, hbnr[cnt], 4))
        halg = cnt + 2;
  }
  if (cptr = StrStr(cptr, "\n\n"))
    cptr += 2;
  else
    return;

  hctx = PGP_hini(halg);

  while (*cptr) {               /* doubled - don't hash last TWO lines */
    if (!StrNCompare(cptr, "\n-----BEGIN PGP SIGNATURE", 25))
      break;
    cpt = linebuf;
    while (*cptr && *cptr != '\n')
      *cpt++ = *cptr++;
    if (!*cptr)
      break;
    *cpt++ = *cptr++;
    *cpt = 0;
    PGP_ctln(linebuf);
    PGP_hblk(hctx, linebuf, StrLen(linebuf));
  }
  if (!*cptr)
    return;

  cptr++;
  ofile = FileOpen(0, "TEMPX", 'DATA', 'OPGP', TEMPF, NULL);
  ll = cptr;
  ll2 = ptr1;
  FileWrite(ofile, cptr, 1, len1 - (ll - ll2), NULL);

  PipeSwap;

  ofile = FileOpen(0, "TEMPA", 'DATA', 'OPGP', TEMPF, NULL);
  PGP_dearmor(file, ofile);

  FileRewind(ofile);
  FileControl(fileOpDestructiveReadMode, ofile, NULL, NULL);
  FileRead(ofile, linebuf, 1, 512, NULL);
  FileClose(ofile);

  i = PGP_sigck(&linebuf[1 + (1 << (linebuf[0] & 3))], hctx);

  if (i && i != -1 && i != -5)
    i = -4;
  if (i == -1)
    WinDrawChars("BAD SIGNATURE       ", 20, 0, 60),
      DmWrite(ptr1, 0, "BAD", 3);
  else if (!i)
    WinDrawChars("Signature Verified  ", 20, 0, 60),
      DmWrite(ptr1, 0, "GOOD", 4);
  else
    WinDrawChars("Sig Did Not Verify  ", 20, 0, 60),
      DmWrite(ptr1, 0, "DONE", 4);
}

msg2file(char *fname, ULong ftype)
{
  FileHand file, ofile;

/* add2f for pubring? */
  file = FileOpen(0, fname, ftype, 'OPGP', NORMF, NULL);
  ofile = FileOpen(0, "RingTemp", 'DATA', 'OPGP', TEMPF, NULL);
  FileWrite(ofile, ptr1, 1, len1, NULL);
  DmWrite(ptr1, 0, "DONE", 4);
  FileRewind(ofile);
  PGP_dearmor(ofile, file);
  FileClose(file);
  FileClose(ofile);
}

static void RunOpenPGP(void)
{
  FileHand file, ofile;
  int i;
  unsigned int j;
  char *cp;
  VoidHand hand1;

  cp = FldGetTextPtr(pphrase);
  i = FldGetTextLength(pphrase);
  StrNCopy(passph, cp, i);
  *ptext = 0;
  FldRecalculateField(pphrase, true);
  FrmDrawForm(FrmGetActiveForm());
  passph[i] = 0;

  for (dbrec = 0; dbrec < DmNumRecords(MemoDB); dbrec++) {

#if 0
    ULong big, tot;
    char testdat[40];
    j = MemHandleHeapID(ppbuf);
    MemHeapFreeBytes(j, &tot, &big);
    StrPrintF(testdat, "%d %ld %ld", dbrec, tot, big);
    WinDrawChars(testdat, strlen(testdat), 0, 100);
#endif

    if (!(hand1 = DmGetRecord(MemoDB, dbrec)))
      return;

    ptr1 = MemHandleLock(hand1);
    len1 = MemHandleSize(hand1);

    if (!StrNCompare("-----BEGIN PGP PUBLIC", ptr1, 21)) {
      WinDrawChars("Dearmoring Pubring  ", 20, 0, 60);  /* ADD2F */
      msg2file("pubring.pkr", 'OPKR');
    }
    if (!StrNCompare("-----BEGIN PGP PRIVATE", ptr1, 22)) {
      WinDrawChars("Dearmoring Secring  ", 20, 0, 60);
      msg2file("secring.skr", 'OSKR');
    }
    if (!StrNCompare("-----BEGIN PGP MESSAGE", ptr1, 22))
      procmsg();
    if (!StrNCompare("OPGPENC", ptr1, 7))
      doenc();
    if (!StrNCompare("OPGPSIGN\n", ptr1, 9))
      dosign();
    if (!StrNCompare("-----BEGIN PGP SIGNED", ptr1, 21))
      dover();

    MemHandleUnlock(hand1);
    DmReleaseRecord(MemoDB, dbrec, 1);
  }
  WinDrawChars("Processing Complete ", 20, 0, 60);
}
