Index: .gitignore
===================================================================
--- .gitignore	(revision 1703f7231e2822e81916109bafcfebee606f7beb)
+++ .gitignore	(revision 7d03c8d7261007b87c9466976c6efd8e1a572d76)
@@ -15,4 +15,5 @@
 autom4te.cache
 barnowl.bin
+zcrypt
 blib
 config.cache
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 10557e655b3e9d7d904e1c92e4e79e331de9c01f)
+++ Makefile.am	(revision 7d03c8d7261007b87c9466976c6efd8e1a572d76)
@@ -5,9 +5,15 @@
 
 bin_PROGRAMS = barnowl.bin
+if ENABLE_ZCRYPT
+bin_PROGRAMS += zcrypt
+endif
+
+zcrypt_SOURCES = zcrypt.c filterproc.c
+
 check_PROGRAMS = tester perl_tester
 
 barnowl_bin_SOURCES = $(BASE_SRCS) \
      owl.h owl_perl.h config.h \
-     owl.c \
+     owl.c filterproc.c \
      $(GEN_C) $(GEN_H)
 
@@ -38,5 +44,5 @@
      perlconfig.c keys.c functions.c zwrite.c viewwin.c help.c filter.c \
      regex.c history.c view.c dict.c variable.c filterelement.c pair.c \
-     keypress.c keymap.c keybinding.c cmd.c context.c zcrypt.c \
+     keypress.c keymap.c keybinding.c cmd.c context.c \
      aim.c buddy.c buddylist.c style.c errqueue.c \
      zbuddylist.c popexec.c obarray.c select.c wcwidth.c \
Index: barnowl
===================================================================
--- barnowl	(revision d5ef539f348e22f4dfe00fa09ae91cccbc0147e8)
+++ barnowl	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
@@ -13,3 +13,4 @@
 
 export BARNOWL_DATA_DIR="$SRCDIR/perl/"
+export BARNOWL_BIN_DIR="$SRCDIR/"
 exec "$EXE" "$@"
Index: commands.c
===================================================================
--- commands.c	(revision 8a5b5a12327c205b798d00f5cc5505f787e55331)
+++ commands.c	(revision 8a5b5a12327c205b798d00f5cc5505f787e55331)
@@ -2022,5 +2022,4 @@
 char *owl_command_zcrypt(int argc, const char *const *argv, const char *buff)
 {
-#ifdef OWL_ENABLE_ZCRYPT
   owl_zwrite z;
 
@@ -2044,8 +2043,4 @@
   }
   return(NULL);
-#else
-  owl_function_makemsg("This Owl does not support zcrypt");
-  return NULL;
-#endif
 }
 
Index: configure.ac
===================================================================
--- configure.ac	(revision 2d3ed95eba3d5651a17d8ec24cd534352c8a2070)
+++ configure.ac	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
@@ -65,6 +65,7 @@
 
 AC_CHECK_FUNCS([use_default_colors resizeterm])
-AC_CHECK_FUNCS([des_string_to_key des_key_sched des_ecb_encrypt])
-AC_CHECK_FUNCS([DES_string_to_key DES_ecb_encrypt DES_key_sched])
+AC_CHECK_FUNCS([des_string_to_key DES_string_to_key], [HAVE_DES_STRING_TO_KEY=1])
+AC_CHECK_FUNCS([des_ecb_encrypt DES_ecb_encrypt], [HAVE_DES_ECB_ENCRYPT=1])
+AC_CHECK_FUNCS([des_key_sched DES_key_sched], [HAVE_DES_KEY_SCHED=1])
 
 dnl Checks for header files.
@@ -118,4 +119,6 @@
 AC_DEFINE_UNQUOTED([DATADIR],["${prefix}/share/${PACKAGE}"],
                    [Package data directory])
+AC_DEFINE_UNQUOTED([BINDIR],["${prefix}/bin"],
+                   [Binary directory])
 
 dnl Checks for typedefs, structures, and compiler characteristics.
@@ -126,4 +129,8 @@
 AX_C_CHECK_FLAG([-Wno-pointer-sign],[],[],
   [LIBFAIM_CFLAGS="$LIBFAIM_CFLAGS -Wno-pointer-sign"])
+
+AM_CONDITIONAL([ENABLE_ZCRYPT], [test "$HAVE_DES_STRING_TO_KEY" && dnl
+                                 test "$HAVE_DES_KEY_SCHED" && dnl
+                                 test "$HAVE_DES_ECB_ENCRYPT"])
 
 AC_SUBST([LIBFAIM_CFLAGS])
Index: filterproc.c
===================================================================
--- filterproc.c	(revision 8bd190d63b2f2c09ea416e7be7c51d974f56bfaf)
+++ filterproc.c	(revision 8bd190d63b2f2c09ea416e7be7c51d974f56bfaf)
@@ -0,0 +1,118 @@
+#include <signal.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <glib.h>
+
+int send_receive(int rfd, int wfd, const char *out, char **in)
+{
+  GString *str = g_string_new("");
+  char buf[1024];
+  nfds_t nfds;
+  int err = 0;
+  struct pollfd fds[2];
+  struct sigaction sig = {.sa_handler = SIG_IGN}, old;
+
+  fcntl(rfd, F_SETFL, O_NONBLOCK | fcntl(rfd, F_GETFL));
+  fcntl(wfd, F_SETFL, O_NONBLOCK | fcntl(wfd, F_GETFL));
+
+  fds[0].fd = rfd;
+  fds[0].events = POLLIN;
+  fds[1].fd = wfd;
+  fds[1].events = POLLOUT;
+
+  sigaction(SIGPIPE, &sig, &old);
+  
+  while(1) {
+    if(out && *out) {
+      nfds = 2;
+    } else {
+      nfds = 1;
+    }
+    err = poll(fds, nfds, -1);
+    if(err < 0) {
+      break;
+    }
+    if(out && *out) {
+      if(fds[1].revents & POLLOUT) {
+        err = write(wfd, out, strlen(out));
+        if(err > 0) {
+          out += err;
+        }
+        if(err < 0) {
+          out = NULL;
+        }
+      }
+      if(!out || !*out || fds[1].revents & (POLLERR | POLLHUP)) {
+        close(wfd);
+        out = NULL;
+      }
+    }
+    if(fds[0].revents & POLLIN) {
+      err = read(rfd, buf, sizeof(buf));
+      if(err <= 0) {
+        break;
+      }
+      g_string_append_len(str, buf, err);
+    } else if(fds[0].revents & (POLLHUP | POLLERR)) {
+      err = 0;
+      break;
+    }
+  }
+
+  *in = g_string_free(str, err < 0);
+  sigaction(SIGPIPE, &old, NULL);
+  return err;
+}
+
+int call_filter(const char *prog, const char *const *argv, const char *in, char **out, int *status)
+{
+  int err = 0;
+  pid_t pid;
+  int rfd[2];
+  int wfd[2];
+
+  if((err = pipe(rfd))) goto out;
+  if((err = pipe(wfd))) goto out_close_rfd;
+
+  pid = fork();
+  if(pid < 0) {
+    err = pid;
+    goto out_close_all;
+  }
+  if(pid) {
+    /* parent */
+    close(rfd[1]);
+    close(wfd[0]);
+    err = send_receive(rfd[0], wfd[1], in, out);
+    if(err == 0) {
+      waitpid(pid, status, 0);
+    }
+  } else {
+    /* child */
+    close(rfd[0]);
+    close(wfd[1]);
+    dup2(rfd[1], 1);
+    dup2(wfd[0], 0);
+    close(rfd[1]);
+    close(wfd[0]);
+
+    if(execvp(prog, (char *const *)argv)) {
+      _exit(-1);
+    }
+  }
+
+ out_close_all:
+  close(wfd[0]);
+  close(wfd[1]);
+ out_close_rfd:
+  close(rfd[0]);
+  close(rfd[1]);
+ out:
+  return err;
+}
Index: filterproc.h
===================================================================
--- filterproc.h	(revision 8bd190d63b2f2c09ea416e7be7c51d974f56bfaf)
+++ filterproc.h	(revision 8bd190d63b2f2c09ea416e7be7c51d974f56bfaf)
@@ -0,0 +1,9 @@
+#ifndef __FILTER_PROC_H__
+#define __FILTER_PROC_H__
+
+int call_filter(const char *prog,
+                const char *const *argv,
+                const char *in,
+                char **out, int *status);
+
+#endif
Index: functions.c
===================================================================
--- functions.c	(revision 8a5b5a12327c205b798d00f5cc5505f787e55331)
+++ functions.c	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
@@ -12,4 +12,5 @@
 #include <signal.h>
 #include "owl.h"
+#include "filterproc.h"
 
 char *owl_function_command(const char *cmdbuff)
@@ -399,4 +400,7 @@
   char *cryptmsg;
   owl_message *m;
+  const char *argv[7];
+  char *zcrypt;
+  int rv, status;
 
   /* create the zwrite and send the message */
@@ -408,7 +412,18 @@
 
   mymsg=owl_zwrite_get_message(&z);
-#ifdef OWL_ENABLE_ZCRYPT
-  cryptmsg = owl_zcrypt_encrypt(mymsg, owl_zwrite_get_class(&z), owl_zwrite_get_instance(&z));
-  if (!cryptmsg) {
+
+  zcrypt = owl_sprintf("%s/zcrypt", owl_get_bindir());
+  argv[0] = "zcrypt";
+  argv[1] = "-E";
+  argv[2] = "-c"; argv[3] = owl_zwrite_get_class(&z);
+  argv[4] = "-i"; argv[5] = owl_zwrite_get_instance(&z);
+  argv[6] = NULL;
+
+  rv = call_filter(zcrypt, argv, mymsg, &cryptmsg, &status);
+
+  owl_free(zcrypt);
+
+  if (rv || status) {
+    if(cryptmsg) owl_free(cryptmsg);
     owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
     owl_function_beep();
@@ -416,7 +431,4 @@
     return;
   }
-#else
-  cryptmsg=owl_strdup(mymsg);
-#endif
 
   owl_zwrite_set_message(&z, cryptmsg);
Index: message.c
===================================================================
--- message.c	(revision 66a8cd6f82c4a4dc47992bd57b12563500abb001)
+++ message.c	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
@@ -10,4 +10,5 @@
 #include <time.h>
 #include "owl.h"
+#include "filterproc.h"
 
 static owl_fmtext_cache fmtext_cache[OWL_FMTEXT_CACHE_SIZE];
@@ -838,14 +839,34 @@
   owl_free(tmp);
 
-#ifdef OWL_ENABLE_ZCRYPT
   /* if zcrypt is enabled try to decrypt the message */
   if (owl_global_is_zcrypt(&g) && !strcasecmp(n->z_opcode, "crypt")) {
-    char *out = owl_zcrypt_decrypt(owl_message_get_body(m), owl_message_get_class(m), owl_message_get_instance(m));
-    if (out) {
+    const char *argv[] = {
+      "zcrypt",
+      "-D",
+      "-c", owl_message_get_class(m),
+      "-i", owl_message_get_instance(m),
+      NULL
+    };
+    char *out;
+    int rv;
+    int status;
+    char *zcrypt;
+
+    zcrypt = owl_sprintf("%s/zcrypt", owl_get_bindir());
+
+    rv = call_filter(zcrypt, argv, owl_message_get_body(m), &out, &status);
+    owl_free(zcrypt);
+
+    if(!rv && !status) {
+      int len = strlen(out);
+      if(len >= 8 && !strcmp(out + len - 8, "**END**\n")) {
+        out[len - 8] = 0;
+      }
       owl_message_set_body(m, out);
       owl_free(out);
+    } else if(out) {
+      owl_free(out);
     }
   }
-#endif  
 }
 #else
Index: tester.c
===================================================================
--- tester.c	(revision 30bb10a729361f227f11994cb8b0d640b25b7f57)
+++ tester.c	(revision d564c3d0a07e5929b3445a63f13cb97eb6b52715)
@@ -13,7 +13,4 @@
 int owl_obarray_regtest(void);
 int owl_editwin_regtest(void);
-#ifdef OWL_ENABLE_ZCRYPT
-int owl_zcrypt_regtest(void);
-#endif
 
 int main(int argc, char **argv, char **env)
@@ -36,7 +33,4 @@
   numfailures += owl_obarray_regtest();
   numfailures += owl_editwin_regtest();
-#ifdef OWL_ENABLE_ZCRYPT
-  numfailures += owl_zcrypt_regtest();
-#endif
   if (numfailures) {
       fprintf(stderr, "# *** WARNING: %d failures total\n", numfailures);
@@ -367,23 +361,2 @@
   return numfailed;
 }
-
-#ifdef OWL_ENABLE_ZCRYPT
-int owl_zcrypt_regtest(void)
-{
-  int numfailed = 0;
-  char *encrypted, *decrypted;
-
-  printf("# BEGIN testing owl_zcrypt\n");
-
-  encrypted = owl_zcrypt_encrypt_with_key("Hello, world!", "seekritkey");
-  FAIL_UNLESS("zcrypt encrypt", strcmp(encrypted, "TLJMKQSIGKSJRIJRSIIPIJFFULKRJSPK") == 0);
-  decrypted = owl_zcrypt_decrypt_with_key(encrypted, "seekritkey");
-  FAIL_UNLESS("zcrypt decrypt", strcmp(decrypted, "Hello, world!") == 0);
-  owl_free(decrypted);
-  owl_free(encrypted);
-
-  printf("# END testing owl_zcrypt (%d failures)\n", numfailed);
-
-  return numfailed;
-}
-#endif
Index: util.c
===================================================================
--- util.c	(revision f449096479541df1434d1d796890d0bd2939f072)
+++ util.c	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
@@ -609,4 +609,12 @@
 }
 
+const char * owl_get_bindir(void)
+{
+  const char * bindir = getenv("BARNOWL_BIN_DIR");
+  if(bindir != NULL)
+    return bindir;
+  return BINDIR;
+}
+
 /* Strips format characters from a valid utf-8 string. Returns the
    empty string if 'in' does not validate. */
Index: zcrypt.c
===================================================================
--- zcrypt.c	(revision f8074e9c91e6b8e560ef58024ee77e7dc85b6b34)
+++ zcrypt.c	(revision e832a52bbcd4f2a74be276d35bb4d891c9ca4879)
@@ -1,4 +1,2 @@
-/* This file is stolen and slightly modified code */
-
 /* zcrypt.c -- Read in a data stream from stdin & dump a decrypted/encrypted *
  *   datastream.  Reads the string to make the key from from the first       *
@@ -10,17 +8,13 @@
 
 #include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include "owl.h"
-
-#ifdef OWL_ENABLE_ZCRYPT
-
-#define BASE_CODE 70
-#define LAST_CODE (BASE_CODE + 15)
-#define OUTPUT_BLOCK_SIZE 16
+
 #include <unistd.h>
 #include <sys/types.h>
+#include <zephyr/zephyr.h>
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <ctype.h>
 
 #ifdef HAVE_KERBEROS_IV
@@ -30,5 +24,16 @@
 #endif
 
-#define MAX_KEY 128
+#include "filterproc.h"
+
+#define MAX_KEY      128
+#define MAX_LINE     128
+#define MAX_RESULT   4096
+
+#ifndef TRUE
+#define TRUE -1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
 
 #define ZWRITE_OPT_NOAUTH     (1<<0)
@@ -48,4 +53,17 @@
 
 char *GetZephyrVarKeyFile(const char *whoami, const char *class, const char *instance);
+int ParseCryptSpec(const char *spec, const char **keyfile);
+char *BuildArgString(char **argv, int start, int end);
+char *read_keystring(const char *keyfile);
+
+int do_encrypt(int zephyr, const char *class, const char *instance,
+               ZWRITEOPTIONS *zoptions, const char* keyfile, int cipher);
+int do_encrypt_des(const char *keyfile, const char *in, int len, FILE *out);
+int do_encrypt_aes(const char *keyfile, const char *in, int len, FILE *out);
+
+int do_decrypt(const char *keyfile, int cipher);
+int do_decrypt_aes(const char *keyfile);
+int do_decrypt_des(const char *keyfile);
+
 
 #define M_NONE            0
@@ -56,5 +74,21 @@
 #define M_SETKEY          5
 
-static void owl_zcrypt_string_to_schedule(const char *keystring, des_key_schedule *schedule) {
+enum cipher_algo {
+  CIPHER_DES,
+  CIPHER_AES,
+  NCIPHER
+};
+
+typedef struct {
+  int (*encrypt)(const char *keyfile, const char *in, int len, FILE *out);
+  int (*decrypt)(const char *keyfile);
+} cipher_pair;
+
+cipher_pair ciphers[NCIPHER] = {
+  [CIPHER_DES] { do_encrypt_des, do_decrypt_des},
+  [CIPHER_AES] { do_encrypt_aes, do_decrypt_aes},
+};
+
+static void owl_zcrypt_string_to_schedule(char *keystring, des_key_schedule *schedule) {
 #ifdef HAVE_KERBEROS_IV
   des_cblock key;
@@ -67,148 +101,307 @@
 }
 
-/* The 'owl_zcrypt_decrypt' function was written by kretch for Owl.
- * Decrypt the message in 'in' on class 'class' and instance
- * 'instance'.  Return must be freed by caller.
- */
-char *owl_zcrypt_decrypt(const char *in, const char *class, const char *instance)
-{
-  char *fname, keystring[MAX_KEY];
-  FILE *fkey;
-  
-  fname=GetZephyrVarKeyFile("zcrypt", class, instance);
-  if (!fname) return NULL;
-  fkey=fopen(fname, "r");
-  owl_free(fname);
-  if (!fkey) return NULL;
-  if (!fgets(keystring, MAX_KEY-1, fkey)) {
-    fclose(fkey);
-    return NULL;
-  }
-  fclose(fkey);
-
-  return owl_zcrypt_decrypt_with_key(in, keystring);
-}
-
-char *owl_zcrypt_decrypt_with_key(const char *in, const char *keystring)
-{
-  const char *inptr, *endptr;
-  char *out;
-  des_key_schedule schedule;
-  unsigned char input[8], output[8];
-  int i, c1, c2;
-
-  /* We read in some number of full 16-byte blocks and write out the
-   * same number of 8-byte blocks, plus a null terminator.
-   */
-  out = owl_malloc((strlen(in) / 16) * 8 + 1);
-
-  strcpy(out, "");
-
-  output[0] = '\0';    /* In case no message at all                 */
-
-  owl_zcrypt_string_to_schedule(keystring, &schedule);
-
-  inptr=in;
-  endptr = in + strlen(in);
-  while (inptr + 16 <= endptr) {
-    for (i=0; i<8; i++) {
-      c1=(inptr[0])-BASE_CODE;
-      c2=(inptr[1])-BASE_CODE;
-      input[i]=c1 * 0x10 + c2;
-      inptr+=2;
-    }
-    des_ecb_encrypt(&input, &output, schedule, FALSE);
-    strncat(out, (const char *)output, 8);
-  }
-
-  return out;
-}
-
-char *owl_zcrypt_encrypt(const char *in, const char *class, const char *instance)
-{
-  char *fname, keystring[MAX_KEY];
-  FILE *fkey;
-
-  fname=GetZephyrVarKeyFile("zcrypt", class, instance);
-  if (!fname) return NULL;
-  fkey=fopen(fname, "r");
-  owl_free(fname);
-  if (!fkey) return NULL;
-  if (!fgets(keystring, MAX_KEY-1, fkey)) {
-    fclose(fkey);
-    return NULL;
-  }
-  fclose(fkey);
-
-  return owl_zcrypt_encrypt_with_key(in, keystring);
-}
-
-char *owl_zcrypt_encrypt_with_key(const char *in, const char *keystring)
-{
-  des_key_schedule schedule;
-  char *out;
-  unsigned char input[8], output[8];
-  int size, length, i;
-  const char *inbuff = NULL, *inptr;
-  int num_blocks=0, last_block_size=0;
-
-  owl_zcrypt_string_to_schedule(keystring, &schedule);
-
-  /* Allocate enough space for the crypted message. For each byte of
-   * the message, the encoded cyphertext will have two bytes. Block
-   * size is 8 bytes of input, or 16 bytes of output, so make sure we
-   * have at least one block worth of space allocated. If the message
-   * is empty, no blocks are sent, but we still allocate one
-   * block. The additional 16 bytes also provide space for the null
-   * terminator, as we will never use all of it for cyphertext.
-   */
-  out = owl_malloc((strlen(in) * 2) + 16);
-
-  inbuff=in;
-  length=strlen(inbuff);
-  num_blocks=(length+7)/8;
-  last_block_size=((length+7)%8)+1;
-
-  strcpy(out, "");
-  
-  inptr=inbuff;
-  while (TRUE) {
-    /* Get 8 bytes from buffer */
-    if (num_blocks > 1) {
-      size = 8;
-      memcpy(input, inptr, size);
-      inptr+=8;
-      num_blocks--;
-    } else if (num_blocks == 1) {
-      size=last_block_size;
-      memcpy(input, inptr, size);
-      num_blocks--;
-    } else {
-      size=0;
-    }
-
-    /* Check for EOF and pad the string to 8 chars, if needed */
-    if (size == 0) break;     /* END OF INPUT: BREAK FROM while LOOP! */
-      
-    if (size<8) memset(input + size, 0, 8 - size);
-
-    /* Encrypt and output the block */
-    des_ecb_encrypt(&input, &output, schedule, TRUE);
-
-    for (i = 0; i < 8; i++) {
-      sprintf(out + strlen(out), "%c", ((output[i] & 0xf0) >> 4) + BASE_CODE);
-      sprintf(out + strlen(out), "%c", (output[i] & 0x0f)        + BASE_CODE);
-    }
-
-    if (size < 8) break;
-  }
-  return out;
-}
-
+int main(int argc, char *argv[])
+{
+  char *cryptspec = NULL;
+  const char *keyfile;
+  int cipher;
+  int error = FALSE;
+  int zephyr = FALSE;
+  char *class = NULL, *instance = NULL;
+  int mode = M_NONE;
+
+  extern int optind, opterr;
+  extern char *optarg;
+  char c;
+
+  int messageflag = FALSE;
+  ZWRITEOPTIONS zoptions;
+  zoptions.flags = 0;
+
+  while ((c = getopt(argc, argv, "ZDERSF:c:i:advqtluons:f:m")) != (char)EOF)
+  {
+    switch(c)
+    {
+      case 'Z':
+        /* Zephyr encrypt */
+        mode = M_ZEPHYR_ENCRYPT;
+        break;
+      case 'D':
+        /* Decrypt */
+        mode = M_DECRYPT;
+        break;
+      case 'E':
+        /* Encrypt */
+        mode = M_ENCRYPT;
+        break;
+      case 'R':
+        /* Randomize the keyfile */
+        mode = M_RANDOMIZE;
+        break;
+      case 'S':
+        /* Set a new key value from stdin */
+        mode = M_SETKEY;
+        break;
+      case 'F':
+        /* Specify the keyfile explicitly */
+        if (cryptspec != NULL) error = TRUE;
+        cryptspec = optarg;
+        break;
+      case 'c':
+        /* Zwrite/zcrypt: class name */
+        if (class != NULL) error = TRUE;
+        class = optarg;
+        break;
+      case 'i':
+        /* Zwrite/zcrypt: instance name */
+        if (instance != NULL) error = TRUE;
+        instance = optarg;
+        break;
+      case 'a':
+        /* Zwrite: authenticate (default) */
+        zoptions.flags &= ~ZWRITE_OPT_NOAUTH;
+        break;
+      case 'd':
+        /* Zwrite: do not authenticate */
+        zoptions.flags |= ZWRITE_OPT_NOAUTH;
+        break;
+      case 'v':
+        /* Zwrite: verbose */
+        zoptions.flags |= ZWRITE_OPT_VERBOSE;
+        break;
+      case 'q':
+        /* Zwrite: quiet */
+        zoptions.flags |= ZWRITE_OPT_QUIET;
+        break;
+      case 't':
+        /* Zwrite: no expand tabs (ignored) */
+        break;
+      case 'l':
+        /* Zwrite: ignore '.' on a line by itself (ignored) */
+        zoptions.flags |= ZCRYPT_OPT_IGNOREDOT;
+        break;
+      case 'u':
+        /* Zwrite: urgent message */
+        instance = "URGENT";
+        break;
+      case 'o':
+        /* Zwrite: ignore zephyr variables zwrite-class, zwrite-inst, */
+        /*         zwrite-opcode */
+        zoptions.flags |= ZWRITE_OPT_IGNOREVARS;
+        break;
+      case 'n':
+        /* Zwrite: prevent PING message (always used) */
+        break;
+      case 's':
+        /* Zwrite: signature */
+        zoptions.flags |= ZWRITE_OPT_SIGNATURE;
+        zoptions.signature = optarg;
+        break;
+      case 'f':
+        /* Zwrite: file system specification (ignored) */
+        break;
+      case 'm':
+        /* Message on rest of line*/
+        messageflag = TRUE;
+        break;
+      case '?':
+        error = TRUE;
+        break;
+    }
+    if (error || messageflag)
+      break;
+  }
+
+  if (class != NULL || instance != NULL)
+    zephyr = TRUE;
+
+  if (messageflag)
+  {
+    zoptions.flags |= ZCRYPT_OPT_MESSAGE;
+    zoptions.message = BuildArgString(argv, optind, argc);
+    if (!zoptions.message)
+    {
+      fprintf(stderr, "Memory allocation error.\n");
+      error = TRUE;
+    }
+  }
+  else if (optind < argc)
+  {
+    error = TRUE;
+  }
+
+  if (mode == M_NONE)
+    mode = (zephyr?M_ZEPHYR_ENCRYPT:M_ENCRYPT);
+
+  if (mode == M_ZEPHYR_ENCRYPT && !zephyr)
+    error = TRUE;
+
+  if (!error && cryptspec == NULL && (class != NULL || instance != NULL)) {
+    cryptspec = GetZephyrVarKeyFile(argv[0], class, instance);
+    if(!cryptspec) {
+      fprintf(stderr, "Unable to find keyfile for ");
+      if(class != NULL) {
+        fprintf(stderr, "-c %s ", class);
+      }
+      if(instance != NULL) {
+        fprintf(stderr, "-i %s ", instance);
+      }
+      fprintf(stderr, "\n");
+      exit(-1);
+    }
+  }
+
+  if (error)
+  {
+    fprintf(stderr, "Usage: %s [-Z|-D|-E|-R|-S] [-F Keyfile] [-c class] [-i instance]\n", argv[0]);
+    fprintf(stderr, "       [-advqtluon] [-s signature] [-f arg] [-m message]\n");
+    fprintf(stderr, "  One or more of class, instance, and keyfile must be specified.\n");
+    exit(1);
+  }
+
+  cipher = ParseCryptSpec(cryptspec, &keyfile);
+  if(cipher < 0) {
+    fprintf(stderr, "Invalid cipher specification: %s\n", cryptspec);
+    exit(1);
+  }
+
+
+  if (mode == M_RANDOMIZE)
+  {
+    /* Choose a new, random key */
+    /*
+      FILE *fkey = fopen(fname, "w");
+      if (!fkey)
+      printf("Could not open key file for writing: %s\n", fname);
+      else
+      {
+      char string[100];
+      fputs(fkey, string);
+      fclose(fkey);
+      }
+    */
+    fprintf(stderr, "Feature not yet implemented.\n");
+  }
+  else if (mode == M_SETKEY)
+  {
+    /* Set a new, user-entered key */
+    char newkey[MAX_KEY];
+    FILE *fkey;
+
+    if (isatty(0))
+    {
+      printf("Enter new key: ");
+      /* Really should read without echo!!! */
+    }
+    if(!fgets(newkey, MAX_KEY - 1, stdin)) {
+      fprintf(stderr, "Error reading key.\n");
+      return 1;
+    }
+
+    fkey = fopen(keyfile, "w");
+    if (!fkey)
+      fprintf(stderr, "Could not open key file for writing: %s\n", keyfile);
+    else
+    {
+      if (fputs(newkey, fkey) != strlen(newkey) || putc('\n', fkey) != '\n')
+      {
+        fprintf(stderr, "Error writing to key file.\n");
+        fclose(fkey);
+        exit(1);
+      }
+      else
+      {
+        fclose(fkey);
+        fprintf(stderr, "Key update complete.\n");
+      }
+    }
+  }
+  else
+  {
+    if (mode == M_ZEPHYR_ENCRYPT || mode == M_ENCRYPT)
+      error = !do_encrypt((mode == M_ZEPHYR_ENCRYPT), class, instance,
+                          &zoptions, keyfile, cipher);
+    else
+      error = !do_decrypt(keyfile, cipher);
+  }
+
+  /* Always print the **END** message if -D is specified. */
+  if (mode == M_DECRYPT)
+    printf("**END**\n");
+
+  return error;
+}
+
+int ParseCryptSpec(const char *spec, const char **keyfile) {
+  int cipher = CIPHER_DES;
+  char *cipher_name = strdup(spec);
+  char *colon = strchr(cipher_name, ':');
+
+  *keyfile = spec;
+
+  if (colon) {
+    char *rest = strchr(spec, ':') + 1;
+    while(isspace(*rest)) rest++;
+
+    *colon-- = '\0';
+    while (colon >= cipher_name && isspace(*colon)) {
+      *colon = '\0';
+    }
+
+    if(strcmp(cipher_name, "AES") == 0) {
+      cipher = CIPHER_AES;
+      *keyfile = rest;
+    } else if(strcmp(cipher_name, "DES") == 0) {
+      cipher = CIPHER_DES;
+      *keyfile = rest;
+    }
+  }
+
+  free(cipher_name);
+
+  return cipher;
+}
+
+/* Build a space-separated string from argv from elements between start  *
+ * and end - 1.  malloc()'s the returned string. */
+char *BuildArgString(char **argv, int start, int end)
+{
+  int len = 1;
+  int i;
+  char *result;
+
+  /* Compute the length of the string.  (Plus 1 or 2) */
+  for (i = start; i < end; i++)
+    len += strlen(argv[i]) + 1;
+
+  /* Allocate memory */
+  result = (char *)malloc(len);
+  if (result)
+  {
+    /* Build the string */
+    char *ptr = result;
+    /* Start with an empty string, in case nothing is copied. */
+    *ptr = '\0';
+    /* Copy the arguments */
+    for (i = start; i < end; i++)
+    {
+      char *temp = argv[i];
+      /* Add a space, if not the first argument */
+      if (i != start)
+        *ptr++ = ' ';
+      /* Copy argv[i], leaving ptr pointing to the '\0' copied from temp */
+      while ((*ptr = *temp++))
+        ptr++;
+    }
+  }
+
+  return result;
+}
 
 #define MAX_BUFF 258
 #define MAX_SEARCH 3
 /* Find the class/instance in the .crypt-table */
-char *GetZephyrVarKeyFile(const char *whoami, const char *class, const char *instance) {
+char *GetZephyrVarKeyFile(const char *whoami, const char *class, const char *instance)
+{
   char *keyfile = NULL;
   char *varname[MAX_SEARCH];
@@ -223,14 +416,13 @@
 
   /* Determine names to look for in .crypt-table */
-  if (instance) {
-    varname[numsearch++] = owl_sprintf("crypt-%s-%s:", (class?class:"message"), instance);
-  }
-  if (class) {
-    varname[numsearch++] = owl_sprintf("crypt-%s:", class);
-  }
-  varname[numsearch++] = owl_strdup("crypt-default:");
+  if (instance)
+    varname[numsearch++] = g_strdup_printf("crypt-%s-%s:", (class?class:"message"), instance);
+  if (class)
+    varname[numsearch++] = g_strdup_printf("crypt-%s:", class);
+  varname[numsearch++] = g_strdup("crypt-default:");
 
   /* Setup the result array, and determine string lengths */
-  for (i = 0; i < numsearch; i++) {
+  for (i = 0; i < numsearch; i++)
+  {
     result[i][0] = '\0';
     length[i] = strlen(varname[i]);
@@ -238,54 +430,461 @@
 
   /* Open~/.crypt-table */
-  filename = owl_sprintf("%s/.crypt-table", getenv("HOME"));
+  filename = g_strdup_printf("%s/.crypt-table", getenv("HOME"));
   fsearch = fopen(filename, "r");
-  if (fsearch) {
+  if (fsearch)
+  {
     /* Scan file for a match */
-    while (!feof(fsearch)) {
+    while (!feof(fsearch))
+    {
       if (!fgets(buffer, MAX_BUFF - 3, fsearch)) break;
-      for (i = 0; i < numsearch; i++) {
-	if (strncasecmp(varname[i], buffer, length[i]) == 0) {
-	  int j;
-	  for (j = length[i]; buffer[j] == ' '; j++)
-	    ;
-	  strcpy(result[i], &buffer[j]);
-	  if (*result[i]) {
-	    if (result[i][strlen(result[i])-1] == '\n') {
-	      result[i][strlen(result[i])-1] = '\0';
-	    }
-	  }
-	}
-      }
+      for (i = 0; i < numsearch; i++)
+        if (strncasecmp(varname[i], buffer, length[i]) == 0)
+        {
+          int j;
+          for (j = length[i]; buffer[j] == ' '; j++)
+            ;
+          strcpy(result[i], &buffer[j]);
+          if (*result[i])
+            if (result[i][strlen(result[i])-1] == '\n')
+              result[i][strlen(result[i])-1] = '\0';
+        }
     }
 
     /* Pick the "best" match found */
     keyfile = NULL;
-    for (i = 0; i < numsearch; i++) {
-      if (*result[i]) {
-	keyfile = result[i];
-	break;
+    for (i = 0; i < numsearch; i++)
+      if (*result[i])
+      {
+        keyfile = result[i];
+        break;
       }
-    }
-
-    if (keyfile == NULL) {
-      /* printf("Could not find key table entry.\n"); */
+
+    if (keyfile != NULL)
+    {
+      /* Prepare result to be returned */
+      char *temp = keyfile;
+      keyfile = (char *)malloc(strlen(temp) + 1);
+      if (keyfile)
+        strcpy(keyfile, temp);
+      else
+        fprintf(stderr, "Memory allocation error.\n");
+    }
+    fclose(fsearch);
+  }
+  else
+    fprintf(stderr, "Could not open key table file: %s\n", filename);
+
+  for(i = 0; i < MAX_SEARCH; i++) {
+    if(varname[i] != NULL) {
+      g_free(varname[i]);
+    }
+  }
+
+  if(filename != NULL) {
+    g_free(filename);
+  }
+
+  return keyfile;
+}
+
+static pid_t zephyrpipe_pid = 0;
+
+/* Open a pipe to zwrite */
+FILE *GetZephyrPipe(const char *class, const char *instance, const ZWRITEOPTIONS *zoptions)
+{
+  int fildes[2];
+  pid_t pid;
+  FILE *result;
+  const char *argv[20];
+  int argc = 0;
+
+  if (pipe(fildes) < 0)
+    return NULL;
+  pid = fork();
+
+  if (pid < 0)
+  {
+    /* Error: clean up */
+    close(fildes[0]);
+    close(fildes[1]);
+    result = NULL;
+  }
+  else if (pid == 0)
+  {
+    /* Setup child process */
+    argv[argc++] = "zwrite";
+    argv[argc++] = "-n";     /* Always send without ping */
+    if (class)
+    {
+      argv[argc++] = "-c";
+      argv[argc++] = class;
+    }
+    if (instance)
+    {
+      argv[argc++] = "-i";
+      argv[argc++] = instance;
+    }
+    if (zoptions->flags & ZWRITE_OPT_NOAUTH)
+      argv[argc++] = "-d";
+    if (zoptions->flags & ZWRITE_OPT_QUIET)
+      argv[argc++] = "-q";
+    if (zoptions->flags & ZWRITE_OPT_VERBOSE)
+      argv[argc++] = "-v";
+    if (zoptions->flags & ZWRITE_OPT_SIGNATURE)
+    {
+      argv[argc++] = "-s";
+      argv[argc++] = zoptions->signature;
+    }
+    argv[argc++] = "-O";
+    argv[argc++] = "crypt";
+    argv[argc] = NULL;
+    close(fildes[1]);
+    if (fildes[0] != STDIN_FILENO)
+    {
+      if (dup2(fildes[0], STDIN_FILENO) != STDIN_FILENO)
+        exit(0);
+      close(fildes[0]);
+    }
+    close(fildes[0]);
+    execvp(argv[0], (char **)argv);
+    fprintf(stderr, "Exec error: could not run zwrite\n");
+    exit(0);
+  }
+  else
+  {
+    close(fildes[0]);
+    /* Create a FILE * for the zwrite pipe */
+    result = (FILE *)fdopen(fildes[1], "w");
+    zephyrpipe_pid = pid;
+  }
+
+  return result;
+}
+
+/* Close the pipe to zwrite */
+void CloseZephyrPipe(FILE *pipe)
+{
+  fclose(pipe);
+  waitpid(zephyrpipe_pid, NULL, 0);
+  zephyrpipe_pid = 0;
+}
+
+#define BASE_CODE 70
+#define LAST_CODE (BASE_CODE + 15)
+#define OUTPUT_BLOCK_SIZE 16
+
+void block_to_ascii(unsigned char *output, FILE *outfile)
+{
+  int i;
+  for (i = 0; i < 8; i++)
+  {
+    putc(((output[i] & 0xf0) >> 4) + BASE_CODE, outfile);
+    putc( (output[i] & 0x0f)       + BASE_CODE, outfile);
+  }
+}
+
+char *slurp_stdin(int ignoredot, int *length) {
+  char *buf;
+  char *inptr;
+
+  if ((inptr = buf = (char *)malloc(MAX_RESULT)) == NULL)
+  {
+    fprintf(stderr, "Memory allocation error\n");
+    return NULL;
+  }
+  while (inptr - buf < MAX_RESULT - MAX_LINE - 20)
+  {
+    if (fgets(inptr, MAX_LINE, stdin) == NULL)
+      break;
+
+    if (inptr[0])
+    {
+      if (inptr[0] == '.' && inptr[1] == '\n' && !ignoredot)
+      {
+        inptr[0] = '\0';
+        break;
+      }
+      else
+        inptr += strlen(inptr);
+    }
+    else
+      break;
+  }
+  *length = inptr - buf;
+
+  return buf;
+}
+
+char *GetInputBuffer(ZWRITEOPTIONS *zoptions, int *length) {
+  char *buf;
+
+  if (zoptions->flags & ZCRYPT_OPT_MESSAGE)
+  {
+    /* Use the -m message */
+    buf = strdup(zoptions->message);
+    *length = strlen(buf);
+  }
+  else
+  {
+    if (isatty(0)) {
+      /* tty input, so show the "Type your message now..." message */
+      if (zoptions->flags & ZCRYPT_OPT_IGNOREDOT)
+        printf("Type your message now.  End with the end-of-file character.\n");
+      else
+        printf("Type your message now.  End with control-D or a dot on a line by itself.\n");
     } else {
-      /* Prepare result to be returned */
-      keyfile = owl_strdup(keyfile);
-    }
-    
-    fclose(fsearch);
-  } else {
-    /* printf("Could not open key table file: %s\n", filename); */
-  }
-
-  for(i = 0; i < MAX_SEARCH; i++) {
-    owl_free(varname[i]);
-  }
-
-  owl_free(filename);
-
-  return(keyfile);
-}
-
-#endif
+      zoptions->flags |= ZCRYPT_OPT_IGNOREDOT;
+    }
+
+    buf = slurp_stdin(zoptions->flags & ZCRYPT_OPT_IGNOREDOT, length);
+  }
+  return buf;
+}
+
+char *read_keystring(const char *keyfile) {
+  char *keystring;
+  FILE *fkey = fopen(keyfile, "r");
+  if(!fkey) {
+    fprintf(stderr, "Unable to open keyfile %s\n", keyfile);
+    return NULL;
+  }
+  keystring = malloc(MAX_KEY);
+  if(!fgets(keystring, MAX_KEY-1, fkey)) {
+    fprintf(stderr, "Unable to read from keyfile: %s\n", keyfile);
+    free(keystring);
+    keystring = NULL;
+  }
+  fclose(fkey);
+  return keystring;
+}
+
+/* Encrypt stdin, with prompt if isatty, and send to stdout, or to zwrite
+   if zephyr is set. */
+int do_encrypt(int zephyr, const char *class, const char *instance,
+               ZWRITEOPTIONS *zoptions, const char *keyfile, int cipher)
+{
+  FILE *outfile = stdout;
+  char *inbuff = NULL;
+  int buflen;
+  int out = TRUE;
+
+  inbuff = GetInputBuffer(zoptions, &buflen);
+
+  if(!inbuff) {
+    fprintf(stderr, "Error reading zcrypt input!\n");
+    return FALSE;
+  }
+
+  if (zephyr) {
+    outfile = GetZephyrPipe(class, instance, zoptions);
+    if (!outfile)
+    {
+      fprintf(stderr, "Could not run zwrite\n");
+      if (inbuff)
+        free(inbuff);
+      return FALSE;
+    }
+  }
+
+  out = ciphers[cipher].encrypt(keyfile, inbuff, buflen, outfile);
+
+  if (zephyr)
+    CloseZephyrPipe(outfile);
+
+  free(inbuff);
+  return out;
+}
+
+int do_encrypt_des(const char *keyfile, const char *in, int length, FILE *outfile)
+{
+  des_key_schedule schedule;
+  unsigned char input[8], output[8];
+  const char *inptr;
+  int num_blocks, last_block_size;
+  char *keystring;
+  int size;
+
+  keystring = read_keystring(keyfile);
+  if(!keystring) {
+    return FALSE;
+  }
+
+  owl_zcrypt_string_to_schedule(keystring, &schedule);
+  free(keystring);
+
+  inptr = in;
+  num_blocks = (length + 7) / 8;
+  last_block_size = ((length + 7) % 8) + 1;
+
+  /* Encrypt the input (inbuff or stdin) and send it to outfile */
+  while (TRUE)
+  {
+    /* Get 8 bytes from buffer */
+    if (num_blocks > 1)
+    {
+      size = 8;
+      memcpy(input, inptr, size);
+      inptr += 8;
+      num_blocks--;
+    }
+    else if (num_blocks == 1)
+    {
+      size = last_block_size;
+      memcpy(input, inptr, size);
+      num_blocks--;
+    }
+    else
+      size = 0;
+
+    /* Check for EOF and pad the string to 8 chars, if needed */
+    if (size == 0)
+      break;
+    if (size < 8)
+      memset(input + size, 0, 8 - size);
+
+    /* Encrypt and output the block */
+    des_ecb_encrypt(&input, &output, schedule, TRUE);
+    block_to_ascii(output, outfile);
+
+    if (size < 8)
+      break;
+  }
+
+  putc('\n', outfile);
+
+  return TRUE;
+}
+
+int do_encrypt_aes(const char *keyfile, const char *in, int length, FILE *outfile)
+{
+  char *out;
+  int err, status;
+  const char *argv[] = {
+    "gpg",
+    "--symmetric",
+    "--batch",
+    "--quiet",
+    "--no-use-agent",
+    "--armor",
+    "--cipher-algo", "AES",
+    "--passphrase-file", keyfile,
+    NULL
+  };
+  err = call_filter("gpg", argv, in, &out, &status);
+  if(err || status) {
+    if(out) g_free(out);
+    return FALSE;
+  }
+  fwrite(out, strlen(out), 1, outfile);
+  g_free(out);
+  return TRUE;
+}
+
+/* Read a half-byte from stdin, skipping invalid characters.  Returns -1
+   if at EOF or file error */
+int read_ascii_nybble(void)
+{
+  char c;
+
+  while (TRUE)
+  {
+    if (fread(&c, 1, 1, stdin) == 0)
+      return -1;
+    else if (c >= BASE_CODE && c <= LAST_CODE)
+      return c - BASE_CODE;
+  }
+}
+
+/* Read both halves of the byte and return the single byte.  Returns -1
+   if at EOF or file error. */
+int read_ascii_byte(void)
+{
+  int c1, c2;
+  c1 = read_ascii_nybble();
+  if (c1 >= 0)
+  {
+    c2 = read_ascii_nybble();
+    if (c2 >= 0)
+    {
+      return c1 * 0x10 + c2;
+    }
+  }
+  return -1;
+}
+
+/* Read an 8-byte DES block from stdin */
+int read_ascii_block(unsigned char *input)
+{
+  int c;
+
+  int i;
+  for (i = 0; i < 8; i++)
+  {
+    c = read_ascii_byte();
+    if (c < 0)
+      return FALSE;
+
+    input[i] = c;
+  }
+
+  return TRUE;
+}
+
+/* Decrypt stdin */
+int do_decrypt(const char *keyfile, int cipher)
+{
+  return ciphers[cipher].decrypt(keyfile);
+}
+
+int do_decrypt_aes(const char *keyfile) {
+  char *in, *out;
+  int length;
+  const char *argv[] = {
+    "gpg",
+    "--decrypt",
+    "--batch",
+    "--no-use-agent",
+    "--quiet",
+    "--passphrase-file", keyfile,
+    NULL
+  };
+  int err, status;
+
+  in = slurp_stdin(TRUE, &length);
+  if(!in) return FALSE;
+
+  err = call_filter("gpg", argv, in, &out, &status);
+  if(err || status) {
+    if(out) g_free(out);
+    return FALSE;
+  }
+  fwrite(out, strlen(out), 1, stdout);
+  g_free(out);
+
+  return TRUE;
+}
+
+int do_decrypt_des(const char *keyfile) {
+  des_key_schedule schedule;
+  unsigned char input[8], output[8];
+  char *keystring;
+
+  output[0] = '\0';    /* In case no message at all                 */
+
+  keystring = read_keystring(keyfile);
+  if(!keystring) return FALSE;
+
+  owl_zcrypt_string_to_schedule(keystring, &schedule);
+
+  free(keystring);
+
+  while (read_ascii_block(input))
+  {
+    des_ecb_encrypt(&input, &output, schedule, FALSE);
+    printf("%s", output);
+  }
+
+  if (!output[0] || output[strlen((const char*)output) - 1] != '\n')
+      printf("\n");
+  return TRUE;
+}
