Add missing files
authorWerner Koch <wk@gnupg.org>
Fri, 10 Aug 2007 07:17:28 +0000 (07:17 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 10 Aug 2007 07:17:28 +0000 (07:17 +0000)
src/item-events.cpp [new file with mode: 0644]
src/item-events.h [new file with mode: 0644]
src/message.cpp [new file with mode: 0644]
src/message.h [new file with mode: 0644]
src/mimeparser.c [new file with mode: 0644]
src/mimeparser.h [new file with mode: 0644]
src/serpent.c [new file with mode: 0644]
src/serpent.h [new file with mode: 0644]

diff --git a/src/item-events.cpp b/src/item-events.cpp
new file mode 100644 (file)
index 0000000..2746610
--- /dev/null
@@ -0,0 +1,131 @@
+/* item-events.cpp - GpgolItemEvents implementation
+ *     Copyright (C) 2007 g10 Code GmbH
+ * 
+ * This file is part of GpgOL.
+ * 
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <windows.h>
+
+#include "mymapi.h"
+#include "mymapitags.h"
+#include "myexchext.h"
+#include "common.h"
+#include "olflange-def.h"
+#include "olflange.h"
+#include "item-events.h"
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+                                     SRCNAME, __func__, __LINE__); \
+                        } while (0)
+
+
+
+/* Our constructor.  */
+GpgolItemEvents::GpgolItemEvents (GpgolExt *pParentInterface)
+{ 
+  m_pExchExt = pParentInterface;
+  m_ref = 0;
+}
+
+
+/* The QueryInterfac.  */
+STDMETHODIMP 
+GpgolItemEvents::QueryInterface (REFIID riid, LPVOID FAR *ppvObj)
+{
+  *ppvObj = NULL;
+  if (riid == IID_IOutlookExtItemEvents)
+    {
+      *ppvObj = (LPVOID)this;
+      AddRef ();
+      return S_OK;
+    }
+  if (riid == IID_IUnknown)
+    {
+      *ppvObj = (LPVOID)m_pExchExt;
+      m_pExchExt->AddRef ();
+      return S_OK;
+    }
+  return E_NOINTERFACE;
+}
+
+/* This method is called if an item is about to being displayed.
+   Possible return values are: 
+
+      S_FALSE - Let Outlook continue the operation.
+
+      E_ABORT - Abort the open operation and the item is not being
+                displayed.
+*/
+STDMETHODIMP 
+GpgolItemEvents::OnOpen (LPEXCHEXTCALLBACK peecb)
+{
+  log_debug ("%s:%s: received", SRCNAME, __func__);
+  return S_FALSE;
+}
+
+
+/* Like all the other Complete methods this one is called after OnOpen
+   has been called for all registred extensions.  FLAGS may have tehse
+   values:
+
+    0 - The open action has not been canceled.
+
+    EEME_FAILED or EEME_COMPLETE_FAILED - both indicate that the
+        open action has been canceled.  
+
+    Note that this method may be called more than once for each OnOpen
+    and may even occur without an OnOpen (i.e. while setting up a new
+    extension).
+
+    The same return values as for OnOpen may be used..
+*/ 
+STDMETHODIMP 
+GpgolItemEvents::OnOpenComplete (LPEXCHEXTCALLBACK peecb, ULONG flags)
+{
+  log_debug ("%s:%s: received, flags=%#lx", SRCNAME, __func__, flags);
+  return S_FALSE;
+}
+
+
+/* This method is called if an item's window received a close request.
+   Possible return values are: 
+
+      S_FALSE - Let Outlook continue the operation.
+
+      E_ABORT - Abort the close operation and don't dismiss the window.
+*/
+STDMETHODIMP 
+GpgolItemEvents::OnClose (LPEXCHEXTCALLBACK peecb, ULONG save_options)
+{
+  log_debug ("%s:%s: received, options=%#lx", SRCNAME, __func__, save_options);
+  return S_FALSE;
+}
+
+
+/* This is the corresponding Complete method for OnClose.  See
+   OnOpenComplete for a description.  */
+STDMETHODIMP 
+GpgolItemEvents::OnCloseComplete (LPEXCHEXTCALLBACK peecb, ULONG flags)
+{
+  log_debug ("%s:%s: received, flags=%#lx", SRCNAME, __func__, flags);
+  return S_FALSE;
+}
diff --git a/src/item-events.h b/src/item-events.h
new file mode 100644 (file)
index 0000000..60b7828
--- /dev/null
@@ -0,0 +1,64 @@
+/* item-events.h - GpgolItemEvents definitions.
+ *     Copyright (C) 2007 g10 Code GmbH
+ * 
+ * This file is part of GpgOL.
+ * 
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#ifndef ITEM_EVENTS_H
+#define ITEM_EVENTS_H
+
+/*
+   GpgolItemEvents
+
+   The GpgolItemEvents class implements the processing of events
+   related to certain items.  This is currently open and close of an
+   inspector.
+ */
+class GpgolItemEvents : public IOutlookExtItemEvents
+{
+ public:
+  GpgolItemEvents (GpgolExt *pParentInterface);
+
+ private:
+  GpgolExt *m_pExchExt;
+  ULONG     m_ref;
+  
+ public:
+  STDMETHODIMP QueryInterface (REFIID riid, LPVOID FAR *ppvObj);
+  inline STDMETHODIMP_(ULONG) AddRef (void)
+    {
+      ++m_ref;
+      return m_ref;
+    }
+  inline STDMETHODIMP_(ULONG) Release (void)
+    {
+      ULONG count = --m_ref;
+      if (!count)
+       delete this;
+      return count;
+    }
+  
+  
+  STDMETHODIMP OnOpen (LPEXCHEXTCALLBACK peecb);
+  STDMETHODIMP OnOpenComplete (LPEXCHEXTCALLBACK peecb, ULONG flags);
+  STDMETHODIMP OnClose (LPEXCHEXTCALLBACK peecb, ULONG save_options);
+  STDMETHODIMP OnCloseComplete (LPEXCHEXTCALLBACK peecb, ULONG flags);
+};
+
+#endif /*ATTACHED_FILE_EVENTS_H*/
diff --git a/src/message.cpp b/src/message.cpp
new file mode 100644 (file)
index 0000000..997dedf
--- /dev/null
@@ -0,0 +1,268 @@
+/* message.cpp - Functions for message handling
+ *     Copyright (C) 2006, 2007 g10 Code GmbH
+ * 
+ * This file is part of GpgOL.
+ * 
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <windows.h>
+
+#include "mymapi.h"
+#include "mymapitags.h"
+#include "myexchext.h"
+#include "common.h"
+#include "mapihelp.h"
+#include "mimeparser.h"
+#include "message.h"
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+                                     SRCNAME, __func__, __LINE__); \
+                        } while (0)
+
+
+
+/* Verify MESSAGE and update the attachments as required.  MSGTYPE
+   should be the type of the message so that the fucntion can decide
+   what to do.  With FORCE set the verification is done regardlessless
+   of a cached signature result. */
+int
+message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
+{
+  mapi_attach_item_t *table;
+  int moss_idx = -1;
+  int i;
+  char *inbuf;
+  size_t inbuflen;
+  int err;
+
+  switch (msgtype)
+    {
+    case MSGTYPE_GPGOL_MULTIPART_SIGNED:
+      break;
+    case MSGTYPE_GPGOL_OPAQUE_SIGNED:
+      log_debug ("Opaque signed message are not yet supported!");
+      return 0;
+    case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
+    case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
+      return -1; /* Should not be called for such a message.  */
+    case MSGTYPE_UNKNOWN:
+    case MSGTYPE_GPGOL:
+      return 0; /* Nothing to do.  */
+    }
+  
+  /* If a verification is forced, we set the cached signature status
+     first to "?" to mark that no verification has yet happened. */
+  if (force)
+    mapi_set_sig_status (message, "?");
+  else if (mapi_has_sig_status (message))
+    return 0; /* Already checked that message.  */
+
+  table = mapi_create_attach_table (message, 0);
+  if (!table)
+    return -1; /* No attachment - this should not happen.  */
+
+
+  for (i=0; !table[i].end_of_table; i++)
+    if (table[i].attach_type == ATTACHTYPE_MOSS)
+      {
+        moss_idx = i;
+        break;
+      }
+  if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
+    {
+      /* No MOSS flag found in the table but there is only one
+         attachment.  Due to the message type we know that this is
+         the original MOSS message.  We mark this attachment as
+         hidden, so that it won't get displayed.  We further mark it
+         as our original MOSS attachment so that after parsing we have
+         a mean to find it again (see above).  */ 
+      moss_idx = 0;
+      mapi_mark_moss_attach (message, table+0);
+    }
+
+  if (moss_idx == -1)
+    {
+      mapi_release_attach_table (table);
+      return -1; /* No original attachment - this should not happen.  */
+    }
+
+
+  inbuf = mapi_get_attach (message, table+0, &inbuflen);
+  if (!inbuf)
+    {
+      mapi_release_attach_table (table);
+      return -1; /* Problem getting the attachment.  */
+    }
+
+
+  err = mime_verify (inbuf, inbuflen, message, 0, 
+                     opt.passwd_ttl, NULL, NULL, 0);
+  log_debug ("mime_verify returned %d", err);
+  xfree (inbuf);
+                    
+  if (err)
+    mapi_set_sig_status (message, gpg_strerror (err));
+  else
+    mapi_set_sig_status (message, "Signature was good");
+
+  mapi_release_attach_table (table);
+  return 0;
+}
+
+/* Decrypt MESSAGE, check signature and update the attachments as
+   required.  MSGTYPE should be the type of the message so that the
+   function can decide what to do.  With FORCE set the verification is
+   done regardlessless of a cached signature result - hmmm, should we
+   such a thing for an encrypted message? */
+int
+message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
+{
+  mapi_attach_item_t *table;
+  int part2_idx;
+  int tblidx;
+  int retval = -1;
+  LPSTREAM cipherstream;
+  gpg_error_t err;
+  int is_opaque = 0;
+  int is_smime = 0;
+
+  switch (msgtype)
+    {
+    case MSGTYPE_UNKNOWN:
+    case MSGTYPE_GPGOL:
+    case MSGTYPE_GPGOL_OPAQUE_SIGNED:
+    case MSGTYPE_GPGOL_MULTIPART_SIGNED:
+      return -1; /* Should not have been called for this.  */
+    case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
+      break;
+    case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
+      is_opaque = 1;
+      break;
+    }
+  
+  table = mapi_create_attach_table (message, 0);
+  if (!table)
+    return -1; /* No attachment - this should not happen.  */
+
+  if (is_opaque)
+    {
+      /* S/MIME opaque encrypted message: We expect 1 attachment.  As
+         we don't know ether we are called the first time, we first
+         try to find this attachment by looking at all attachments.
+         Only if this fails we identify it by its order.  */
+      part2_idx = -1;
+      for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
+        if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
+          {
+            if (part2_idx == -1 && table[tblidx].content_type 
+                && (!strcmp (table[tblidx].content_type,
+                            "application/pkcs7-mime")
+                    || !strcmp (table[tblidx].content_type,
+                                "application/x-pkcs7-mime")))
+              part2_idx = tblidx;
+          }
+      if (part2_idx == -1 && tblidx >= 1)
+        {
+          /* We have attachments but none are marked.  Thus we assume
+             that this is the first time we see this message and we
+             will set the mark now if we see appropriate content
+             types. */
+          if (table[0].content_type               
+              && (!strcmp (table[0].content_type, "application/pkcs7-mime")
+                  || !strcmp (table[0].content_type,
+                              "application/x-pkcs7-mime")))
+            part2_idx = 0;
+          if (part2_idx != -1)
+            mapi_mark_moss_attach (message, table+part2_idx);
+        }
+      if (part2_idx == -1)
+        {
+          log_debug ("%s:%s: this is not an S/MIME encrypted message",
+                     SRCNAME, __func__);
+          goto leave;
+        }
+      is_smime = 1;
+    }
+  else 
+    {
+      /* Multipart/encrypted message: We expect 2 attachments.  The
+         first one with the version number and the second one with the
+         ciphertext.  As we don't know ether we are called the first
+         time, we first try to find these attachments by looking at
+         all attachments.  Only if this fails we identify them by
+         their order (i.e. the first 2 attachments) and mark them as
+         part1 and part2.  */
+      int part1_idx;
+
+      part1_idx = part2_idx = -1;
+      for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
+        if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
+          {
+            if (part1_idx == -1 && table[tblidx].content_type 
+                && !strcmp (table[tblidx].content_type,
+                            "application/pgp-encrypted"))
+              part1_idx = tblidx;
+            else if (part2_idx == -1 && table[tblidx].content_type 
+                     && !strcmp (table[tblidx].content_type,
+                                 "application/octet-stream"))
+              part2_idx = tblidx;
+          }
+      if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
+        {
+          /* At least 2 attachments but none are marked.  Thus we
+             assume that this is the first time we see this message
+             and we will set the mark now if we see appropriate
+             content types. */
+          if (table[0].content_type               
+              && !strcmp (table[0].content_type, "application/pgp-encrypted"))
+            part1_idx = 0;
+          if (table[1].content_type             
+              && !strcmp (table[1].content_type, "application/octet-stream"))
+            part2_idx = 1;
+          if (part1_idx != -1 && part2_idx != -1)
+            {
+              mapi_mark_moss_attach (message, table+part1_idx);
+              mapi_mark_moss_attach (message, table+part2_idx);
+            }
+        }
+      if (part1_idx == -1 || part2_idx == -1)
+        {
+          log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
+                 SRCNAME, __func__);
+          goto leave;
+        }
+    }
+
+  /* Get the attachment as an allocated buffer and let the mimeparser
+     work on it.  */
+  cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
+  if (!cipherstream)
+    goto leave; /* Problem getting the attachment.  */
+
+  err = mime_decrypt (cipherstream, message, is_smime, opt.passwd_ttl,
+                      NULL, NULL, 0);
+  log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
+  cipherstream->Release ();
+  retval = 0;
+
+ leave:
+  mapi_release_attach_table (table);
+  return retval;
+}
diff --git a/src/message.h b/src/message.h
new file mode 100644 (file)
index 0000000..5223ea3
--- /dev/null
@@ -0,0 +1,30 @@
+/* message.h - Declarations for message.c
+ *     Copyright (C) 2007 g10 Code GmbH
+ * 
+ * This file is part of GpgOL.
+ * 
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+
+int message_verify (LPMESSAGE message, msgtype_t msgtype, int force);
+int message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force);
+
+
+#endif /*MESSAGE_H*/
diff --git a/src/mimeparser.c b/src/mimeparser.c
new file mode 100644 (file)
index 0000000..4d35337
--- /dev/null
@@ -0,0 +1,1104 @@
+/* mimeparser.c - Parse multipart MIME message
+ *     Copyright (C) 2005, 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ * 
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/*
+   EXPLAIN what we are doing here.
+*/
+   
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#define COBJMACROS
+#include <windows.h>
+#include <objidl.h> /* For IStream. */
+
+#include <gpgme.h>
+
+#include "mymapi.h"
+#include "mymapitags.h"
+
+#include "rfc822parse.h"
+#include "common.h"
+#include "engine.h"
+#include "mapihelp.h"
+#include "serpent.h"
+#include "mimeparser.h"
+
+/* Define the next to get extra debug message for the MIME parser.  */
+#define DEBUG_PARSER 1
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+                                     SRCNAME, __func__, __LINE__); \
+                        } while (0)
+
+static const char oid_mimetag[] =
+    {0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
+
+
+
+/* The maximum length of a line we are able to process.  RFC822 allows
+   only for 1000 bytes; thus 2000 seems to be a reasonable value. */
+#define LINEBUFSIZE 2000
+
+
+typedef enum
+  {
+    PROTOCOL_UNKNOWN = 0,
+    PROTOCOL_OPENPGP,
+    PROTOCOL_SMIME
+  }
+protocol_t;
+
+
+/* To keep track of the MIME message structures we use a linked list
+   with each item corresponding to one part. */
+struct mimestruct_item_s;
+typedef struct mimestruct_item_s *mimestruct_item_t;
+struct mimestruct_item_s
+{
+  mimestruct_item_t next;
+  unsigned int level;   /* Level in the hierarchy of that part.  0
+                           indicates the outer body.  */
+  char *filename;       /* Malloced fileanme or NULL.  */
+  char *charset;        /* Malloced charset or NULL.  */
+  char content_type[1]; /* String with the content type. */
+};
+
+
+
+
+/* The context object we use to track information. */
+struct mime_context
+{
+  HWND hwnd;          /* A window handle to be used for message boxes etc. */
+  rfc822parse_t msg;  /* The handle of the RFC822 parser. */
+
+  int preview;        /* Do only decryption and pop up no  message bozes.  */
+  
+  int protect_mode;   /* Encrypt all attachments etc. (cf. SYMENC). */
+  int verify_mode;    /* True if we want to verify a signature. */
+
+  int nesting_level;  /* Current MIME nesting level. */
+  int in_data;        /* We are currently in data (body or attachment). */
+  int body_seen;      /* True if we have seen a part we consider the
+                         body of the message.  */
+
+  gpgme_data_t signed_data;/* NULL or the data object used to collect
+                              the signed data. It would be better to
+                              just hash it but there is no support in
+                              gpgme for this yet. */
+  gpgme_data_t sig_data;  /* NULL or data object to collect the
+                             signature attachment which should be a
+                             signature then.  */
+  
+  int collect_attachment; /* True if we are collecting an attachment
+                             or the body. */
+  int collect_signeddata; /* True if we are collecting the signed data. */
+  int collect_signature;  /* True if we are collecting a signature.  */
+  int start_hashing;      /* Flag used to start collecting signed data. */
+  int hashing_level;      /* MIME level where we started hashing. */
+  int is_qp_encoded;      /* Current part is QP encoded. */
+  int is_base64_encoded;  /* Current part is base 64 encoded. */
+  int is_utf8;            /* Current part has charset utf-8. */
+  protocol_t protocol;    /* The detected crypto protocol.  */
+
+  int part_counter;       /* Counts the number of processed parts. */
+  int any_boundary;       /* Indicates whether we have seen any
+                             boundary which means that we are actually
+                             working on a MIME message and not just on
+                             plain rfc822 message.  */
+  
+  /* A linked list describing the structure of the mime message.  This
+     list gets build up while parsing the message.  */
+  mimestruct_item_t mimestruct;
+  mimestruct_item_t *mimestruct_tail;
+  mimestruct_item_t mimestruct_cur;
+
+  LPMESSAGE mapi_message; /* The MAPI message object we are working on.  */
+  LPSTREAM outstream;     /* NULL or a stream to write a part to. */
+  LPATTACH mapi_attach;   /* The attachment object we are writing.  */
+  symenc_t symenc;        /* NULL or the context used to protect
+                             attachments. */
+  int any_attachments_created;  /* True if we created a new atatchment.  */
+
+  b64_state_t base64;     /* The state of the Base-64 decoder.  */
+
+  int line_too_long;  /* Indicates that a received line was too long. */
+  int parser_error;   /* Indicates that we encountered a error from
+                         the parser. */
+
+  /* Buffer used to constructed complete files. */
+  size_t linebufsize;   /* The allocated size of the buffer. */
+  size_t linebufpos;    /* The actual write posituion. */  
+  char linebuf[1];      /* The buffer. */
+};
+typedef struct mime_context *mime_context_t;
+
+
+/* This function is a wrapper around gpgme_data_write to convert the
+   data to utf-8 first.  We assume Latin-1 here. */
+/* static int */
+/* latin1_data_write (gpgme_data_t data, const char *line, size_t len) */
+/* { */
+/*   const char *s; */
+/*   char *buffer, *p; */
+/*   size_t i, n; */
+/*   int rc; */
+
+/*   for (s=line, i=0, n=0 ; i < len; s++, i++ )  */
+/*     { */
+/*       n++; */
+/*       if (*s & 0x80) */
+/*         n++; */
+/*     } */
+/*   buffer = xmalloc (n + 1); */
+/*   for (s=line, i=0, p=buffer; i < len; s++, i++ ) */
+/*     { */
+/*       if (*s & 0x80) */
+/*         { */
+/*           *p++ = 0xc0 | ((*s >> 6) & 3); */
+/*           *p++ = 0x80 | (*s & 0x3f); */
+/*         } */
+/*       else */
+/*         *p++ = *s; */
+/*     } */
+/*   assert (p-buffer == n); */
+/*   rc = gpgme_data_write (data, buffer, n); */
+/*   xfree (buffer); */
+/*   return rc; */
+/* } */
+
+
+/* Print the message event EVENT. */
+static void
+debug_message_event (mime_context_t ctx, rfc822parse_event_t event)
+{
+  const char *s;
+
+  switch (event)
+    {
+    case RFC822PARSE_OPEN: s= "Open"; break;
+    case RFC822PARSE_CLOSE: s= "Close"; break;
+    case RFC822PARSE_CANCEL: s= "Cancel"; break;
+    case RFC822PARSE_T2BODY: s= "T2Body"; break;
+    case RFC822PARSE_FINISH: s= "Finish"; break;
+    case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
+    case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
+    case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
+    case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
+    case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
+    case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
+    case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
+    case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
+    default: s= "[unknown event]"; break;
+    }
+#ifdef DEBUG_PARSER
+  log_debug ("%s: ctx=%p, rfc822 event %s\n", SRCNAME, ctx, s);
+#endif
+}
+
+
+
+/* Start a new atatchment.  With IS_BODY set, the attachment is
+   actually the body part of the message which is treated in a special
+   way. */
+static int
+start_attachment (mime_context_t ctx, int is_body)
+{
+  int retval = -1;
+  HRESULT hr;
+  ULONG newpos;
+  SPropValue prop;
+  LPATTACH newatt = NULL;
+  LPSTREAM to = NULL;
+  LPUNKNOWN punk;
+
+#ifdef DEBUG_PARSER
+  log_debug ("%s:%s: for ctx=%p is_body=%d", SRCNAME, __func__, ctx, is_body);
+#endif
+
+  /* Just in case something has not been finished, do it here. */
+  if (ctx->outstream)
+    {
+      IStream_Release (ctx->outstream);
+      ctx->outstream = NULL;
+    }
+  if (ctx->mapi_attach)
+    {
+      IAttach_Release (ctx->mapi_attach);
+      ctx->mapi_attach = NULL;
+    }
+  if (ctx->symenc)
+    {
+      symenc_close (ctx->symenc);
+      ctx->symenc = NULL;
+    }
+  
+  /* Before we start with the first attachment we need to delete all
+     attachments which might have been created already by a past
+     parser run.  */
+  if (!ctx->any_attachments_created)
+    {
+      mapi_attach_item_t *table;
+      int i;
+      
+      table = mapi_create_attach_table (ctx->mapi_message, 1);
+      if (table)
+        {
+          for (i=0; !table[i].end_of_table; i++)
+            if (table[i].attach_type == ATTACHTYPE_FROMMOSS)
+              {
+                hr = IMessage_DeleteAttach (ctx->mapi_message, 
+                                            table[i].mapipos,
+                                            0, NULL, 0);
+                if (hr)
+                  log_error ("%s:%s: DeleteAttach failed: hr=%#lx\n",
+                             SRCNAME, __func__, hr); 
+              }
+          mapi_release_attach_table (table);
+        }
+      ctx->any_attachments_created = 1;
+    }
+  
+  /* Now create a new attachment.  */
+  hr = IMessage_CreateAttach (ctx->mapi_message, NULL, 0, &newpos, &newatt);
+  if (hr)
+    {
+      log_error ("%s:%s: can't create attachment: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  prop.ulPropTag = PR_ATTACH_METHOD;
+  prop.Value.ul = ATTACH_BY_VALUE;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+  if (hr != S_OK)
+    {
+      log_error ("%s:%s: can't set attach method: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  /* Mark that attachment so that we know why it has been created.  */
+  if (get_gpgolattachtype_tag (ctx->mapi_message, &prop.ulPropTag) )
+    goto leave;
+  prop.Value.l = ATTACHTYPE_FROMMOSS;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);       
+  if (hr)
+    {
+      log_error ("%s:%s: can't set %s property: hr=%#lx\n",
+                 SRCNAME, __func__, "GpgOL Attach Type", hr); 
+      goto leave;
+    }
+
+
+  /* The body attachment is special and should not be show in the list
+     of atatchments.  */
+  if (is_body)
+    {
+      prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
+      prop.Value.b = TRUE;
+      hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+      if (hr)
+        {
+          log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
+                     SRCNAME, __func__, hr); 
+          goto leave;
+        }
+    }
+  
+
+  /* We need to insert a short filename .  Without it, the _displayed_
+     list of attachments won't get updated although the attachment has
+     been created. */
+  prop.ulPropTag = PR_ATTACH_FILENAME_A;
+  {
+    char buf[100];
+
+    if (is_body)
+      prop.Value.lpszA = is_body == 2? "gpgol000.htm":"gpgol000.txt";
+    else
+      {
+        snprintf (buf, 100, "gpgol%03d.dat", ctx->part_counter);
+        prop.Value.lpszA = buf;
+      }
+    hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+  }
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  /* And now for the real name.  */
+  if (ctx->mimestruct_cur && ctx->mimestruct_cur->filename)
+    {
+      prop.ulPropTag = PR_ATTACH_LONG_FILENAME_A;
+      prop.Value.lpszA = ctx->mimestruct_cur->filename;
+      hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+      if (hr)
+        {
+          log_error ("%s:%s: can't set attach long filename: hr=%#lx\n",
+                     SRCNAME, __func__, hr); 
+          goto leave;
+        }
+    }
+
+  prop.ulPropTag = PR_ATTACH_TAG;
+  prop.Value.bin.cb  = sizeof oid_mimetag;
+  prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach tag: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  assert (ctx->mimestruct_cur && ctx->mimestruct_cur->content_type);
+  prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
+  prop.Value.lpszA = ctx->mimestruct_cur->content_type;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+
+  /* If we are in protect mode (i.e. working on a decrypted message,
+     we need to setup the symkey context to protect (encrypt) the
+     attachment in the MAPI.  */
+  if (ctx->protect_mode)
+    {
+      char *iv;
+
+      if (get_gpgolprotectiv_tag (ctx->mapi_message, &prop.ulPropTag) )
+        goto leave;
+
+      iv = create_initialization_vector (16);
+      if (!iv)
+        {
+          log_error ("%s:%s: error creating initialization vector",
+                     SRCNAME, __func__);
+          goto leave;
+        }
+      prop.Value.bin.cb = 16;
+      prop.Value.bin.lpb = iv;
+      hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);   
+      if (hr)
+        {
+          log_error ("%s:%s: can't set %s property: hr=%#lx\n",
+                     SRCNAME, __func__, "GpgOL Protect IV", hr); 
+          goto leave;
+        }
+
+      ctx->symenc = symenc_open (get_128bit_session_key (), 16, iv, 16);
+      xfree (iv);
+      if (!ctx->symenc)
+        {
+          log_error ("%s:%s: error creating cipher context",
+                     SRCNAME, __func__);
+          goto leave;
+        }
+    }
+
+  punk = (LPUNKNOWN)to;
+  hr = IAttach_OpenProperty (newatt, PR_ATTACH_DATA_BIN, &IID_IStream, 0,
+                             MAPI_CREATE|MAPI_MODIFY, &punk);
+  if (FAILED (hr)) 
+    {
+      log_error ("%s:%s: can't create output stream: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+  to = (LPSTREAM)punk;
+  
+  ctx->outstream = to;
+  to = NULL;
+  ctx->mapi_attach = newatt;
+  newatt = NULL;
+
+  if (ctx->symenc)
+    {
+      char tmpbuf[16];
+      /* Write an encrypted fixed 16 byte string which we need to
+         check at decryption time to see whether we have actually
+         encrypted it using this session key.  */
+      symenc_cfb_encrypt (ctx->symenc, tmpbuf, "GpgOL attachment", 16);
+      IStream_Write (ctx->outstream, tmpbuf, 16, NULL);
+    }
+  retval = 0; /* Success.  */
+
+ leave:
+  if (to)
+    {
+      IStream_Revert (to);
+      IStream_Release (to);
+    }
+  if (newatt)
+    IAttach_Release (newatt);
+  return retval;
+}
+
+
+static int
+finish_attachment (mime_context_t ctx, int cancel)
+{
+  HRESULT hr;
+  int retval = -1;
+
+#ifdef DEBUG_PARSER
+  log_debug ("%s:%s: for ctx=%p cancel=%d", SRCNAME, __func__, ctx, cancel);
+#endif
+
+  if (ctx->outstream)
+    {
+      IStream_Commit (ctx->outstream, 0);
+      IStream_Release (ctx->outstream);
+      ctx->outstream = NULL;
+
+      if (cancel)
+        retval = 0;
+      else if (ctx->mapi_attach)
+        {
+          hr = IAttach_SaveChanges (ctx->mapi_attach, KEEP_OPEN_READWRITE);
+          if (hr)
+            {
+              log_error ("%s:%s: SaveChanges(attachment) failed: hr=%#lx\n",
+                         SRCNAME, __func__, hr); 
+            }
+          else
+            retval = 0; /* Success.  */
+        }
+    }
+  if (ctx->mapi_attach)
+    {
+      IAttach_Release (ctx->mapi_attach);
+      ctx->mapi_attach = NULL;
+    }
+  if (ctx->symenc)
+    {
+      symenc_close (ctx->symenc);
+      ctx->symenc = NULL;
+    }
+  return retval;
+}
+
+
+/* Process the transition to body event. 
+
+   This means we have received the empty line indicating the body and
+   should now check the headers to see what to do about this part.  */
+static int
+t2body (mime_context_t ctx, rfc822parse_t msg)
+{
+  rfc822parse_field_t field;
+  const char *ctmain, *ctsub;
+  const char *s;
+  size_t off;
+  char *p;
+  int is_text = 0;
+  int is_body = 0;
+  char *filename = NULL; 
+  char *charset = NULL;
+        
+
+  /* Figure out the encoding.  */
+  ctx->is_qp_encoded = 0;
+  ctx->is_base64_encoded = 0;
+  p = rfc822parse_get_field (msg, "Content-Transfer-Encoding", -1, &off);
+  if (p)
+    {
+      if (!stricmp (p+off, "quoted-printable"))
+        ctx->is_qp_encoded = 1;
+      else if (!stricmp (p+off, "base64"))
+        {
+          ctx->is_base64_encoded = 1;
+          b64_init (&ctx->base64);
+        }
+      free (p);
+    }
+
+  /* Get the filename from the header.  */
+  field = rfc822parse_parse_field (msg, "Content-Disposition", -1);
+  if (field)
+    {
+      s = rfc822parse_query_parameter (field, "filename", 0);
+      if (s)
+        filename = xstrdup (s);
+      rfc822parse_release_field (field);
+    }
+
+  /* Process the Content-type and all its parameters.  */
+  ctmain = ctsub = NULL;
+  field = rfc822parse_parse_field (msg, "Content-Type", -1);
+  if (field)
+    ctmain = rfc822parse_query_media_type (field, &ctsub);
+  if (!ctmain)
+    {
+      /* Either there is no content type field or it is faulty; in
+         both cases we fall back to text/plain.  */
+      ctmain = "text";
+      ctsub  = "plain";
+    }
+
+#ifdef DEBUG_PARSER  
+  log_debug ("%s:%s: ctx=%p, ct=`%s/%s'\n",
+             SRCNAME, __func__, ctx, ctmain, ctsub);
+#endif
+
+  /* We only support UTF-8 for now.  Check here.  */
+  s = rfc822parse_query_parameter (field, "charset", 0);
+  if (s)
+    charset = xstrdup (s);
+  ctx->is_utf8 = (s && !strcmp (s, "utf-8"));
+
+  /* Update our idea of the entire MIME structure.  */
+  {
+    mimestruct_item_t ms;
+
+    ms = xmalloc (sizeof *ms + strlen (ctmain) + 1 + strlen (ctsub));
+    ctx->mimestruct_cur = ms;
+    *ctx->mimestruct_tail = ms;
+    ctx->mimestruct_tail = &ms->next;
+    ms->next = NULL;
+    strcpy (stpcpy (stpcpy (ms->content_type, ctmain), "/"), ctsub);
+    ms->level = ctx->nesting_level;
+    ms->filename = filename;
+    filename = NULL;
+    ms->charset = charset;
+    charset = NULL;
+
+  }
+
+      
+  if (!strcmp (ctmain, "multipart"))
+    {
+      /* We don't care about the top level multipart layer but wait
+         until it comes to the actual parts which then will get stored
+         as attachments.
+
+         For now encapsulated signed or encrypted containers are not
+         processed in a special way as they should.  Except for the
+         simple verify mode. */
+      if (ctx->verify_mode && !ctx->signed_data
+          && !strcmp (ctsub, "signed")
+          && (s = rfc822parse_query_parameter (field, "protocol", 0)))
+        {
+          if (!strcmp (s, "application/pgp-signature"))
+            ctx->protocol = PROTOCOL_OPENPGP;
+          else if (!strcmp (s, "application/pkcs7-signature")
+                   || !strcmp (s, "application/x-pkcs7-signature"))
+            ctx->protocol = PROTOCOL_SMIME;
+          else
+            ctx->protocol = PROTOCOL_UNKNOWN;
+
+          /* Need to start the hashing after the next boundary. */
+          ctx->start_hashing = 1;
+        }
+    }
+  else if (!strcmp (ctmain, "text"))
+    {
+      is_text = !strcmp (ctsub, "html")? 2:1;
+    }
+  else if (ctx->verify_mode && ctx->nesting_level == 1 && !ctx->sig_data
+           && !strcmp (ctmain, "application")
+           && ((ctx->protocol == PROTOCOL_OPENPGP   
+                && !strcmp (ctsub, "pgp-signature"))
+               || (ctx->protocol == PROTOCOL_SMIME   
+                   && (!strcmp (ctsub, "pkcs7-signature")
+                       || !strcmp (ctsub, "x-pkcs7-signature")))))
+    {
+      /* This is the second part of a MOSS signature.  We only support
+         here full messages thus checking the nesting level is
+         sufficient.  We do this only for the first signature (i.e. if
+         sig_data has not been set yet).  We also do this only while
+         in verify mode because we don't want to write a full MUA.  */
+      if (!ctx->preview && !gpgme_data_new (&ctx->sig_data))
+        ctx->collect_signature = 1;
+    }
+  else /* Other type. */
+    {
+      if (!ctx->preview)
+        ctx->collect_attachment = 1;
+    }
+  rfc822parse_release_field (field); /* (Content-type) */
+  ctx->in_data = 1;
+
+#ifdef DEBUG_PARSER
+  log_debug ("%s: this body: nesting=%d part_counter=%d is_text=%d\n",
+             SRCNAME, ctx->nesting_level, ctx->part_counter, is_text);
+#endif
+
+  /* If this is a text part, decide whether we treat it as our body. */
+  if (is_text)
+    {
+      /* If this is the first text part at all we will start to
+         collect it and use it later as the regular body.  */
+      if (!ctx->body_seen)
+        {
+          ctx->body_seen = 1;
+          ctx->collect_attachment = 1;
+          is_body = 1;
+        }
+      else if (!ctx->preview)
+        ctx->collect_attachment = 1;
+    }
+
+
+  if (ctx->collect_attachment)
+    {
+      /* Now that if we have an attachment prepare a new MAPI
+         attachment.  */
+      if (start_attachment (ctx, is_body))
+        return -1;
+      assert (ctx->outstream);
+    }
+
+  return 0;
+}
+
+
+/* This routine gets called by the RFC822 parser for all kind of
+   events.  OPAQUE carries in our case an smime context.  Should
+   return 0 on success or -1 as well as setting errno on
+   failure.  */
+static int
+message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
+{
+  int retval = 0;
+  mime_context_t ctx = opaque;
+
+  debug_message_event (ctx, event);
+
+  if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
+    {
+      /* We need to check here whether to start collecting signed data
+         because attachments might come without header lines and thus
+         we won't see the BEGIN_HEADER event. */
+      if (ctx->start_hashing == 1)
+        {
+          ctx->start_hashing = 2;
+          ctx->hashing_level = ctx->nesting_level;
+          ctx->collect_signeddata = 1;
+          gpgme_data_new (&ctx->signed_data);
+        }
+    }
+
+
+  switch (event)
+    {
+    case RFC822PARSE_T2BODY:
+      retval = t2body (ctx, msg);
+      break;
+
+    case RFC822PARSE_LEVEL_DOWN:
+      ctx->nesting_level++;
+      break;
+
+    case RFC822PARSE_LEVEL_UP:
+      if (ctx->nesting_level)
+        ctx->nesting_level--;
+      else 
+        {
+          log_error ("%s: ctx=%p, invalid structure: bad nesting level\n",
+                     SRCNAME, ctx);
+          ctx->parser_error = 1;
+        }
+      break;
+    
+    case RFC822PARSE_BOUNDARY:
+    case RFC822PARSE_LAST_BOUNDARY:
+      ctx->any_boundary = 1;
+      ctx->in_data = 0;
+      ctx->collect_attachment = 0;
+      
+      finish_attachment (ctx, 0);
+      assert (!ctx->outstream);
+
+      if (ctx->start_hashing == 2 && ctx->hashing_level == ctx->nesting_level)
+        {
+          ctx->start_hashing = 3; /* Avoid triggering it again. */
+          ctx->collect_signeddata = 0;
+        }
+      break;
+    
+    case RFC822PARSE_BEGIN_HEADER:
+      ctx->part_counter++;
+      break;
+
+    default:  /* Ignore all other events. */
+      break;
+    }
+
+  return retval;
+}
+
+
+
+/* This handler is called by GPGME with the decrypted plaintext. */
+static ssize_t
+plaintext_handler (void *handle, const void *buffer, size_t size)
+{
+  mime_context_t ctx = handle;
+  const char *s;
+  size_t nleft, pos, len;
+
+  s = buffer;
+  pos = ctx->linebufpos;
+  nleft = size;
+  for (; nleft ; nleft--, s++)
+    {
+      if (pos >= ctx->linebufsize)
+        {
+          log_error ("%s: ctx=%p, rfc822 parser failed: line too long\n",
+                     SRCNAME, ctx);
+          ctx->line_too_long = 1;
+          return 0; /* Error. */
+        }
+      if (*s != '\n')
+        ctx->linebuf[pos++] = *s;
+      else
+        { /* Got a complete line. Remove the last CR */
+          if (pos && ctx->linebuf[pos-1] == '\r')
+            pos--;
+
+          if (rfc822parse_insert (ctx->msg, ctx->linebuf, pos))
+            {
+              log_error ("%s: ctx=%p, rfc822 parser failed: %s\n",
+                         SRCNAME, ctx, strerror (errno));
+              ctx->parser_error = 1;
+              return 0; /* Error. */
+            }
+
+
+          if (ctx->collect_signeddata && ctx->signed_data)
+            {
+              /* Save the signed data.  Note that we need to delay
+                 the CR/LF because the last line ending belongs to the
+                 next boundary. */
+              if (ctx->collect_signeddata == 2)
+                gpgme_data_write (ctx->signed_data, "\r\n", 2);
+              gpgme_data_write (ctx->signed_data, ctx->linebuf, pos);
+              ctx->collect_signeddata = 2;
+            }
+
+          if (ctx->in_data && ctx->collect_attachment)
+            {
+              /* We are inside of an attachment part.  Write it out. */
+              if (ctx->collect_attachment == 1)  /* Skip the first line. */
+                ctx->collect_attachment = 2;
+              else if (ctx->outstream)
+                {
+                  HRESULT hr = 0;
+
+                  if (ctx->is_qp_encoded)
+                    len = qp_decode (ctx->linebuf, pos);
+                  else if (ctx->is_base64_encoded)
+                    len = b64_decode (&ctx->base64, ctx->linebuf, pos);
+                  else
+                    len = pos;
+                  if (len)
+                    {
+                      if (ctx->symenc)
+                        symenc_cfb_encrypt (ctx->symenc, ctx->linebuf,
+                                            ctx->linebuf, len);
+                      hr = IStream_Write (ctx->outstream, ctx->linebuf,
+                                          len, NULL);
+                    }
+                  if (!hr && !ctx->is_base64_encoded)
+                    {
+                      char tmp[3] = "\r\n";
+
+                      if (ctx->symenc)
+                        symenc_cfb_encrypt (ctx->symenc, tmp, tmp, 2);
+                      hr = IStream_Write (ctx->outstream, tmp, 2, NULL);
+                    }
+                  if (hr)
+                    {
+                      log_debug ("%s:%s: Write failed: hr=%#lx",
+                                 SRCNAME, __func__, hr);
+                      if (!ctx->preview)
+                        MessageBox (ctx->hwnd, _("Error writing to stream"),
+                                    _("I/O-Error"), MB_ICONERROR|MB_OK);
+                      ctx->parser_error = 1;
+                      return 0; /* Error. */
+                    }
+                }
+            }
+          else if (ctx->in_data && ctx->collect_signature)
+            {
+              /* We are inside of a signature attachment part.  */
+              if (ctx->collect_signature == 1)  /* Skip the first line. */
+                ctx->collect_signature = 2;
+              else if (ctx->sig_data)
+                {
+                  if (ctx->is_qp_encoded)
+                    len = qp_decode (ctx->linebuf, pos);
+                  else if (ctx->is_base64_encoded)
+                    len = b64_decode (&ctx->base64, ctx->linebuf, pos);
+                  else
+                    len = pos;
+                  if (len)
+                    gpgme_data_write (ctx->sig_data, ctx->linebuf, len);
+                  if (!ctx->is_base64_encoded)
+                    gpgme_data_write (ctx->sig_data, "\r\n", 2);
+                }
+            }
+          
+          /* Continue with next line. */
+          pos = 0;
+        }
+    }
+  ctx->linebufpos = pos;
+
+  return size;
+}
+
+
+
+static void 
+show_mimestruct (mimestruct_item_t mimestruct)
+{
+  mimestruct_item_t ms;
+
+  for (ms = mimestruct; ms; ms = ms->next)
+    log_debug ("MIMESTRUCT: %*s%s  cs=%s  fn=%s\n",
+               ms->level*2, "", ms->content_type,
+               ms->charset? ms->charset : "[none]",
+               ms->filename? ms->filename : "[none]");
+}
+
+
+
+int
+mime_verify (const char *message, size_t messagelen, 
+             LPMESSAGE mapi_message, int is_smime,
+             int ttl, gpgme_data_t attestation, HWND hwnd, int preview_mode)
+{
+  gpg_error_t err = 0;
+  mime_context_t ctx;
+  const char *s;
+  size_t len;
+
+  (void)is_smime;  /* Not yet used.  */
+
+  log_debug ("%s:%s: enter", SRCNAME, __func__);
+
+  ctx = xcalloc (1, sizeof *ctx + LINEBUFSIZE);
+  ctx->linebufsize = LINEBUFSIZE;
+  ctx->hwnd = hwnd;
+  ctx->preview = preview_mode;
+  ctx->verify_mode = 1;
+  ctx->mapi_message = mapi_message;
+  ctx->mimestruct_tail = &ctx->mimestruct;
+
+  ctx->msg = rfc822parse_open (message_cb, ctx);
+  if (!ctx->msg)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("%s:%s: failed to open the RFC822 parser: %s", 
+                 SRCNAME, __func__, gpg_strerror (err));
+      goto leave;
+    }
+
+  /* Need to pass the data line by line to the handler. */
+  while ( (s = memchr (message, '\n', messagelen)) )
+    {
+      len = s - message + 1;
+      plaintext_handler (ctx, message, len);
+      if (ctx->parser_error || ctx->line_too_long)
+        {
+          err = gpg_error (GPG_ERR_GENERAL);
+          break;
+        }
+      message += len;
+      assert (messagelen >= len);
+      messagelen -= len;
+    }
+  /* Note: the last character should be a LF, if not we ignore such an
+     incomplete last line.  */
+
+
+  /* Now actually verify the signature. */
+  if (!err && ctx->signed_data && ctx->sig_data)
+    {
+      char *tmp;
+      gpgme_protocol_t xprot;
+      int inv_prot = 0;
+
+      gpgme_data_seek (ctx->signed_data, 0, SEEK_SET);
+      gpgme_data_seek (ctx->sig_data, 0, SEEK_SET);
+      if (ctx->protocol == PROTOCOL_OPENPGP)
+        {
+          tmp = native_to_utf8 (_("[OpenPGP signature]"));
+          xprot = GPGME_PROTOCOL_OpenPGP;
+        }
+      else if (ctx->protocol == PROTOCOL_SMIME)
+        {
+          tmp = native_to_utf8 (_("[S/MIME signature]"));
+          xprot = GPGME_PROTOCOL_CMS;
+        }
+      else
+        {
+          tmp = native_to_utf8 (_("[Unknown signature protocol]"));
+          inv_prot = 1;
+        }
+
+      err = (inv_prot
+             ? gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL)
+             : op_verify_detached_sig_gpgme (xprot,
+                                             ctx->signed_data, ctx->sig_data,
+                                             tmp, attestation));
+      log_debug ("%s:%s: checked signature: %s <%s>", 
+                 SRCNAME, __func__, gpg_strerror (err), op_strsource (err));
+      xfree (tmp);
+    }
+
+
+ leave:
+  if (ctx)
+    {
+      /* Cancel any left open attachment.  */
+      finish_attachment (ctx, 1); 
+      rfc822parse_close (ctx->msg);
+      if (ctx->signed_data)
+        gpgme_data_release (ctx->signed_data);
+      if (ctx->sig_data)
+        gpgme_data_release (ctx->sig_data);
+      show_mimestruct (ctx->mimestruct);
+      while (ctx->mimestruct)
+        {
+          mimestruct_item_t tmp = ctx->mimestruct->next;
+          xfree (ctx->mimestruct->filename);
+          xfree (ctx->mimestruct->charset);
+          xfree (ctx->mimestruct);
+          ctx->mimestruct = tmp;
+        }
+      symenc_close (ctx->symenc);
+      xfree (ctx);
+    }
+  return err;
+}
+
+
+
+/* Decrypt the PGP or S/MIME message taken from INSTREAM.  If
+   ATTESTATION is not NULL a text with the result of the signature
+   verification will get printed to it.  HWND is the window to be used
+   for message box and such.  In PREVIEW_MODE no verification will be
+   done, no messages saved and no messages boxes will pop up. */
+int
+mime_decrypt (LPSTREAM instream, LPMESSAGE mapi_message, int is_smime,
+              int ttl, gpgme_data_t attestation, HWND hwnd, int preview_mode)
+{
+  gpg_error_t err;
+  struct gpgme_data_cbs cbs;
+  gpgme_data_t plaintext;
+  mime_context_t ctx;
+  char *title;
+  gpgme_protocol_t proto;
+
+  log_debug ("%s:%s: enter", SRCNAME, __func__);
+
+  memset (&cbs, 0, sizeof cbs);
+  cbs.write = plaintext_handler;
+
+  ctx = xcalloc (1, sizeof *ctx + LINEBUFSIZE);
+  ctx->linebufsize = LINEBUFSIZE;
+  ctx->protect_mode = 1; 
+  ctx->hwnd = hwnd;
+  ctx->preview = preview_mode;
+  ctx->mapi_message = mapi_message;
+  ctx->mimestruct_tail = &ctx->mimestruct;
+
+  ctx->msg = rfc822parse_open (message_cb, ctx);
+  if (!ctx->msg)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("%s:%s: failed to open the RFC822 parser: %s", 
+                 SRCNAME, __func__, gpg_strerror (err));
+      goto leave;
+    }
+
+  err = gpgme_data_new_from_cbs (&plaintext, &cbs, ctx);
+  if (err)
+    goto leave;
+
+  if (is_smime)
+    {
+      proto = GPGME_PROTOCOL_CMS;
+      title = native_to_utf8 (_("[Encrypted S/MIME message]"));
+    }
+  else
+    {
+      proto = GPGME_PROTOCOL_OpenPGP;
+      title = native_to_utf8 (_("[Encrypted PGP/MIME message]"));
+    }
+  err = op_decrypt_stream_to_gpgme (proto, instream, plaintext, ttl, title,
+                                    attestation, preview_mode);
+  xfree (title);
+  if (!err && (ctx->parser_error || ctx->line_too_long))
+    err = gpg_error (GPG_ERR_GENERAL);
+
+
+ leave:
+  if (plaintext)
+    gpgme_data_release (plaintext);
+  if (ctx)
+    {
+      /* Cancel any left over attachment which means that the MIME
+         structure was not complete.  However if we have not seen any
+         boundary the message is a non-MIME one but we way have
+         started the body attachment (gpgol000.txt) - this one needs
+         to be finished properly.  */
+      finish_attachment (ctx, ctx->any_boundary? 1: 0);
+      rfc822parse_close (ctx->msg);
+      if (ctx->signed_data)
+        gpgme_data_release (ctx->signed_data);
+      if (ctx->sig_data)
+        gpgme_data_release (ctx->sig_data);
+      show_mimestruct (ctx->mimestruct);
+      while (ctx->mimestruct)
+        {
+          mimestruct_item_t tmp = ctx->mimestruct->next;
+          xfree (ctx->mimestruct->filename);
+          xfree (ctx->mimestruct->charset);
+          xfree (ctx->mimestruct);
+          ctx->mimestruct = tmp;
+        }
+      symenc_close (ctx->symenc);
+      xfree (ctx);
+    }
+  return err;
+}
+
diff --git a/src/mimeparser.h b/src/mimeparser.h
new file mode 100644 (file)
index 0000000..44e13a0
--- /dev/null
@@ -0,0 +1,44 @@
+/* mimeparse.h - Multipart MIME parser.
+ *     Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ * 
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef SMIME_H
+#define SMIME_H
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+
+int mime_verify (const char *message, size_t messagelen, 
+                 LPMESSAGE mapi_message, int is_smime,
+                 int ttl, 
+                 gpgme_data_t attestation, HWND hwnd, int preview_mode);
+int mime_decrypt (LPSTREAM instream, LPMESSAGE mapi_message, int is_smime,
+                  int ttl,
+                  gpgme_data_t attestation, HWND hwnd, int preview_mode);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*SMIME_H*/
diff --git a/src/serpent.c b/src/serpent.c
new file mode 100644 (file)
index 0000000..6ebc09e
--- /dev/null
@@ -0,0 +1,1116 @@
+/* serpent.c - Implementation of the Serpent encryption algorithm.
+ *     Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* 
+   Requirements:
+
+   Big endian machines need to define WORDS_BIGENDIAN
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif 
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "serpent.h"
+
+/* Serpent is a 128 bit block cipher.  */
+#define BLOCKSIZE 16
+
+/* Number of rounds per Serpent encrypt/decrypt operation.  */
+#define ROUNDS 32
+
+/* Magic number, used during generating of the subkeys.  */
+#define PHI 0x9E3779B9
+
+/* Serpent works on 128 bit blocks.  */
+typedef uint32_t serpent_block_t[4];
+
+/* Serpent key, provided by the user.  If the original key is shorter
+   than 256 bits, it is padded.  */
+typedef uint32_t serpent_key_t[8];
+
+/* The key schedule consists of 33 128 bit subkeys.  */
+typedef uint32_t serpent_subkeys_t[ROUNDS + 1][4];
+
+/* A Serpent context.  Note that the public API uses symenc_t
+   instead.  */
+struct symenc_context_s
+{
+  serpent_subkeys_t keys;      /* Generated subkeys. */
+
+  union {
+    uint32_t dummy;             /* Force alignment.  */
+    struct {
+      unsigned char iv[BLOCKSIZE];     /* The IV. */
+      unsigned int unused;              /* Unused bytes in the IV. */
+    } cfb;
+  } u;
+};
+typedef struct symenc_context_s serpent_context_t;
+
+
+/* A prototype.  */
+static const char *serpent_test (void);
+      
+#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
+#define ror(x,n) ( ((x) >> (n)) | ((x) << (32-(n))) )
+
+#define byte_swap_32(x) \
+  (0 \
+   | (((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) \
+   | (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
+
+/* These are the S-Boxes of Serpent.  They are copied from Serpents
+   reference implementation (the optimized one, contained in
+   `floppy2') and are therefore:
+
+     Copyright (C) 1998 Ross Anderson, Eli Biham, Lars Knudsen.
+
+  To quote the Serpent homepage
+  (http://www.cl.cam.ac.uk/~rja14/serpent.html):
+
+  "Serpent is now completely in the public domain, and we impose no
+   restrictions on its use.  This was announced on the 21st August at
+   the First AES Candidate Conference. The optimised implementations
+   in the submission package are now under the GNU PUBLIC LICENSE
+   (GPL), although some comments in the code still say otherwise. You
+   are welcome to use Serpent for any application."  */
+
+#define SBOX0(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t05, t06, t07, t08, t09; \
+    uint32_t t11, t12, t13, t14, t15, t17, t01; \
+    t01 = b   ^ c  ; \
+    t02 = a   | d  ; \
+    t03 = a   ^ b  ; \
+    z   = t02 ^ t01; \
+    t05 = c   | z  ; \
+    t06 = a   ^ d  ; \
+    t07 = b   | c  ; \
+    t08 = d   & t05; \
+    t09 = t03 & t07; \
+    y   = t09 ^ t08; \
+    t11 = t09 & y  ; \
+    t12 = c   ^ d  ; \
+    t13 = t07 ^ t11; \
+    t14 = b   & t06; \
+    t15 = t06 ^ t13; \
+    w   =     ~ t15; \
+    t17 = w   ^ t14; \
+    x   = t12 ^ t17; \
+  }
+
+#define SBOX0_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t08, t09, t10; \
+    uint32_t t12, t13, t14, t15, t17, t18, t01; \
+    t01 = c   ^ d  ; \
+    t02 = a   | b  ; \
+    t03 = b   | c  ; \
+    t04 = c   & t01; \
+    t05 = t02 ^ t01; \
+    t06 = a   | t04; \
+    y   =     ~ t05; \
+    t08 = b   ^ d  ; \
+    t09 = t03 & t08; \
+    t10 = d   | y  ; \
+    x   = t09 ^ t06; \
+    t12 = a   | t05; \
+    t13 = x   ^ t12; \
+    t14 = t03 ^ t10; \
+    t15 = a   ^ c  ; \
+    z   = t14 ^ t13; \
+    t17 = t05 & t13; \
+    t18 = t14 | t17; \
+    w   = t15 ^ t18; \
+  }
+
+#define SBOX1(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t07, t08; \
+    uint32_t t10, t11, t12, t13, t16, t17, t01; \
+    t01 = a   | d  ; \
+    t02 = c   ^ d  ; \
+    t03 =     ~ b  ; \
+    t04 = a   ^ c  ; \
+    t05 = a   | t03; \
+    t06 = d   & t04; \
+    t07 = t01 & t02; \
+    t08 = b   | t06; \
+    y   = t02 ^ t05; \
+    t10 = t07 ^ t08; \
+    t11 = t01 ^ t10; \
+    t12 = y   ^ t11; \
+    t13 = b   & d  ; \
+    z   =     ~ t10; \
+    x   = t13 ^ t12; \
+    t16 = t10 | x  ; \
+    t17 = t05 & t16; \
+    w   = c   ^ t17; \
+  }
+
+#define SBOX1_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t07, t08; \
+    uint32_t t09, t10, t11, t14, t15, t17, t01; \
+    t01 = a   ^ b  ; \
+    t02 = b   | d  ; \
+    t03 = a   & c  ; \
+    t04 = c   ^ t02; \
+    t05 = a   | t04; \
+    t06 = t01 & t05; \
+    t07 = d   | t03; \
+    t08 = b   ^ t06; \
+    t09 = t07 ^ t06; \
+    t10 = t04 | t03; \
+    t11 = d   & t08; \
+    y   =     ~ t09; \
+    x   = t10 ^ t11; \
+    t14 = a   | y  ; \
+    t15 = t06 ^ x  ; \
+    z   = t01 ^ t04; \
+    t17 = c   ^ t15; \
+    w   = t14 ^ t17; \
+  }
+
+#define SBOX2(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t05, t06, t07, t08; \
+    uint32_t t09, t10, t12, t13, t14, t01; \
+    t01 = a   | c  ; \
+    t02 = a   ^ b  ; \
+    t03 = d   ^ t01; \
+    w   = t02 ^ t03; \
+    t05 = c   ^ w  ; \
+    t06 = b   ^ t05; \
+    t07 = b   | t05; \
+    t08 = t01 & t06; \
+    t09 = t03 ^ t07; \
+    t10 = t02 | t09; \
+    x   = t10 ^ t08; \
+    t12 = a   | d  ; \
+    t13 = t09 ^ x  ; \
+    t14 = b   ^ t13; \
+    z   =     ~ t09; \
+    y   = t12 ^ t14; \
+  }
+
+#define SBOX2_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t06, t07, t08, t09; \
+    uint32_t t10, t11, t12, t15, t16, t17, t01; \
+    t01 = a   ^ d  ; \
+    t02 = c   ^ d  ; \
+    t03 = a   & c  ; \
+    t04 = b   | t02; \
+    w   = t01 ^ t04; \
+    t06 = a   | c  ; \
+    t07 = d   | w  ; \
+    t08 =     ~ d  ; \
+    t09 = b   & t06; \
+    t10 = t08 | t03; \
+    t11 = b   & t07; \
+    t12 = t06 & t02; \
+    z   = t09 ^ t10; \
+    x   = t12 ^ t11; \
+    t15 = c   & z  ; \
+    t16 = w   ^ x  ; \
+    t17 = t10 ^ t15; \
+    y   = t16 ^ t17; \
+  }
+
+#define SBOX3(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t07, t08; \
+    uint32_t t09, t10, t11, t13, t14, t15, t01; \
+    t01 = a   ^ c  ; \
+    t02 = a   | d  ; \
+    t03 = a   & d  ; \
+    t04 = t01 & t02; \
+    t05 = b   | t03; \
+    t06 = a   & b  ; \
+    t07 = d   ^ t04; \
+    t08 = c   | t06; \
+    t09 = b   ^ t07; \
+    t10 = d   & t05; \
+    t11 = t02 ^ t10; \
+    z   = t08 ^ t09; \
+    t13 = d   | z  ; \
+    t14 = a   | t07; \
+    t15 = b   & t13; \
+    y   = t08 ^ t11; \
+    w   = t14 ^ t15; \
+    x   = t05 ^ t04; \
+  }
+
+#define SBOX3_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t07, t09; \
+    uint32_t t11, t12, t13, t14, t16, t01; \
+    t01 = c   | d  ; \
+    t02 = a   | d  ; \
+    t03 = c   ^ t02; \
+    t04 = b   ^ t02; \
+    t05 = a   ^ d  ; \
+    t06 = t04 & t03; \
+    t07 = b   & t01; \
+    y   = t05 ^ t06; \
+    t09 = a   ^ t03; \
+    w   = t07 ^ t03; \
+    t11 = w   | t05; \
+    t12 = t09 & t11; \
+    t13 = a   & y  ; \
+    t14 = t01 ^ t05; \
+    x   = b   ^ t12; \
+    t16 = b   | t13; \
+    z   = t14 ^ t16; \
+  }
+
+#define SBOX4(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t08, t09; \
+    uint32_t t10, t11, t12, t13, t14, t15, t16, t01; \
+    t01 = a   | b  ; \
+    t02 = b   | c  ; \
+    t03 = a   ^ t02; \
+    t04 = b   ^ d  ; \
+    t05 = d   | t03; \
+    t06 = d   & t01; \
+    z   = t03 ^ t06; \
+    t08 = z   & t04; \
+    t09 = t04 & t05; \
+    t10 = c   ^ t06; \
+    t11 = b   & c  ; \
+    t12 = t04 ^ t08; \
+    t13 = t11 | t03; \
+    t14 = t10 ^ t09; \
+    t15 = a   & t05; \
+    t16 = t11 | t12; \
+    y   = t13 ^ t08; \
+    x   = t15 ^ t16; \
+    w   =     ~ t14; \
+  }
+
+#define SBOX4_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t07, t09; \
+    uint32_t t10, t11, t12, t13, t15, t01; \
+    t01 = b   | d  ; \
+    t02 = c   | d  ; \
+    t03 = a   & t01; \
+    t04 = b   ^ t02; \
+    t05 = c   ^ d  ; \
+    t06 =     ~ t03; \
+    t07 = a   & t04; \
+    x   = t05 ^ t07; \
+    t09 = x   | t06; \
+    t10 = a   ^ t07; \
+    t11 = t01 ^ t09; \
+    t12 = d   ^ t04; \
+    t13 = c   | t10; \
+    z   = t03 ^ t12; \
+    t15 = a   ^ t04; \
+    y   = t11 ^ t13; \
+    w   = t15 ^ t09; \
+  }
+
+#define SBOX5(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t07, t08, t09; \
+    uint32_t t10, t11, t12, t13, t14, t01; \
+    t01 = b   ^ d  ; \
+    t02 = b   | d  ; \
+    t03 = a   & t01; \
+    t04 = c   ^ t02; \
+    t05 = t03 ^ t04; \
+    w   =     ~ t05; \
+    t07 = a   ^ t01; \
+    t08 = d   | w  ; \
+    t09 = b   | t05; \
+    t10 = d   ^ t08; \
+    t11 = b   | t07; \
+    t12 = t03 | w  ; \
+    t13 = t07 | t10; \
+    t14 = t01 ^ t11; \
+    y   = t09 ^ t13; \
+    x   = t07 ^ t08; \
+    z   = t12 ^ t14; \
+  }
+
+#define SBOX5_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t07, t08, t09; \
+    uint32_t t10, t12, t13, t15, t16, t01; \
+    t01 = a   & d  ; \
+    t02 = c   ^ t01; \
+    t03 = a   ^ d  ; \
+    t04 = b   & t02; \
+    t05 = a   & c  ; \
+    w   = t03 ^ t04; \
+    t07 = a   & w  ; \
+    t08 = t01 ^ w  ; \
+    t09 = b   | t05; \
+    t10 =     ~ b  ; \
+    x   = t08 ^ t09; \
+    t12 = t10 | t07; \
+    t13 = w   | x  ; \
+    z   = t02 ^ t12; \
+    t15 = t02 ^ t13; \
+    t16 = b   ^ d  ; \
+    y   = t16 ^ t15; \
+  }
+
+#define SBOX6(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t07, t08, t09, t10; \
+    uint32_t t11, t12, t13, t15, t17, t18, t01; \
+    t01 = a   & d  ; \
+    t02 = b   ^ c  ; \
+    t03 = a   ^ d  ; \
+    t04 = t01 ^ t02; \
+    t05 = b   | c  ; \
+    x   =     ~ t04; \
+    t07 = t03 & t05; \
+    t08 = b   & x  ; \
+    t09 = a   | c  ; \
+    t10 = t07 ^ t08; \
+    t11 = b   | d  ; \
+    t12 = c   ^ t11; \
+    t13 = t09 ^ t10; \
+    y   =     ~ t13; \
+    t15 = x   & t03; \
+    z   = t12 ^ t07; \
+    t17 = a   ^ b  ; \
+    t18 = y   ^ t15; \
+    w   = t17 ^ t18; \
+  }
+
+#define SBOX6_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t07, t08, t09; \
+    uint32_t t12, t13, t14, t15, t16, t17, t01; \
+    t01 = a   ^ c  ; \
+    t02 =     ~ c  ; \
+    t03 = b   & t01; \
+    t04 = b   | t02; \
+    t05 = d   | t03; \
+    t06 = b   ^ d  ; \
+    t07 = a   & t04; \
+    t08 = a   | t02; \
+    t09 = t07 ^ t05; \
+    x   = t06 ^ t08; \
+    w   =     ~ t09; \
+    t12 = b   & w  ; \
+    t13 = t01 & t05; \
+    t14 = t01 ^ t12; \
+    t15 = t07 ^ t13; \
+    t16 = d   | t02; \
+    t17 = a   ^ x  ; \
+    z   = t17 ^ t15; \
+    y   = t16 ^ t14; \
+  }
+
+#define SBOX7(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t05, t06, t08, t09, t10; \
+    uint32_t t11, t13, t14, t15, t16, t17, t01; \
+    t01 = a   & c  ; \
+    t02 =     ~ d  ; \
+    t03 = a   & t02; \
+    t04 = b   | t01; \
+    t05 = a   & b  ; \
+    t06 = c   ^ t04; \
+    z   = t03 ^ t06; \
+    t08 = c   | z  ; \
+    t09 = d   | t05; \
+    t10 = a   ^ t08; \
+    t11 = t04 & z  ; \
+    x   = t09 ^ t10; \
+    t13 = b   ^ x  ; \
+    t14 = t01 ^ x  ; \
+    t15 = c   ^ t05; \
+    t16 = t11 | t13; \
+    t17 = t02 | t14; \
+    w   = t15 ^ t17; \
+    y   = a   ^ t16; \
+  }
+
+#define SBOX7_INVERSE(a, b, c, d, w, x, y, z) \
+  { \
+    uint32_t t02, t03, t04, t06, t07, t08, t09; \
+    uint32_t t10, t11, t13, t14, t15, t16, t01; \
+    t01 = a   & b  ; \
+    t02 = a   | b  ; \
+    t03 = c   | t01; \
+    t04 = d   & t02; \
+    z   = t03 ^ t04; \
+    t06 = b   ^ t04; \
+    t07 = d   ^ z  ; \
+    t08 =     ~ t07; \
+    t09 = t06 | t08; \
+    t10 = b   ^ d  ; \
+    t11 = a   | d  ; \
+    x   = a   ^ t09; \
+    t13 = c   ^ t06; \
+    t14 = c   & t11; \
+    t15 = d   | x  ; \
+    t16 = t01 | t10; \
+    w   = t13 ^ t15; \
+    y   = t14 ^ t16; \
+  }
+
+/* XOR BLOCK1 into BLOCK0.  */
+#define BLOCK_XOR(block0, block1) \
+  {                               \
+    block0[0] ^= block1[0];       \
+    block0[1] ^= block1[1];       \
+    block0[2] ^= block1[2];       \
+    block0[3] ^= block1[3];       \
+  }
+
+/* Copy BLOCK_SRC to BLOCK_DST.  */
+#define BLOCK_COPY(block_dst, block_src) \
+  {                                      \
+    block_dst[0] = block_src[0];         \
+    block_dst[1] = block_src[1];         \
+    block_dst[2] = block_src[2];         \
+    block_dst[3] = block_src[3];         \
+  }
+
+/* Apply SBOX number WHICH to to the block found in ARRAY0 at index
+   INDEX, writing the output to the block found in ARRAY1 at index
+   INDEX.  */
+#define SBOX(which, array0, array1, index)            \
+  SBOX##which (array0[index + 0], array0[index + 1],  \
+               array0[index + 2], array0[index + 3],  \
+               array1[index + 0], array1[index + 1],  \
+               array1[index + 2], array1[index + 3]);
+
+/* Apply inverse SBOX number WHICH to to the block found in ARRAY0 at
+   index INDEX, writing the output to the block found in ARRAY1 at
+   index INDEX.  */
+#define SBOX_INVERSE(which, array0, array1, index)              \
+  SBOX##which##_INVERSE (array0[index + 0], array0[index + 1],  \
+                         array0[index + 2], array0[index + 3],  \
+                         array1[index + 0], array1[index + 1],  \
+                         array1[index + 2], array1[index + 3]);
+
+/* Apply the linear transformation to BLOCK.  */
+#define LINEAR_TRANSFORMATION(block)                  \
+  {                                                   \
+    block[0] = rol (block[0], 13);                    \
+    block[2] = rol (block[2], 3);                     \
+    block[1] = block[1] ^ block[0] ^ block[2];        \
+    block[3] = block[3] ^ block[2] ^ (block[0] << 3); \
+    block[1] = rol (block[1], 1);                     \
+    block[3] = rol (block[3], 7);                     \
+    block[0] = block[0] ^ block[1] ^ block[3];        \
+    block[2] = block[2] ^ block[3] ^ (block[1] << 7); \
+    block[0] = rol (block[0], 5);                     \
+    block[2] = rol (block[2], 22);                    \
+  }
+
+/* Apply the inverse linear transformation to BLOCK.  */
+#define LINEAR_TRANSFORMATION_INVERSE(block)          \
+  {                                                   \
+    block[2] = ror (block[2], 22);                    \
+    block[0] = ror (block[0] , 5);                    \
+    block[2] = block[2] ^ block[3] ^ (block[1] << 7); \
+    block[0] = block[0] ^ block[1] ^ block[3];        \
+    block[3] = ror (block[3], 7);                     \
+    block[1] = ror (block[1], 1);                     \
+    block[3] = block[3] ^ block[2] ^ (block[0] << 3); \
+    block[1] = block[1] ^ block[0] ^ block[2];        \
+    block[2] = ror (block[2], 3);                     \
+    block[0] = ror (block[0], 13);                    \
+  }
+
+/* Apply a Serpent round to BLOCK, using the SBOX number WHICH and the
+   subkeys contained in SUBKEYS.  Use BLOCK_TMP as temporary storage.
+   This macro increments `round'.  */
+#define ROUND(which, subkeys, block, block_tmp) \
+  {                                             \
+    BLOCK_XOR (block, subkeys[round]);          \
+    round++;                                    \
+    SBOX (which, block, block_tmp, 0);          \
+    LINEAR_TRANSFORMATION (block_tmp);          \
+    BLOCK_COPY (block, block_tmp);              \
+  }
+
+/* Apply the last Serpent round to BLOCK, using the SBOX number WHICH
+   and the subkeys contained in SUBKEYS.  Use BLOCK_TMP as temporary
+   storage.  The result will be stored in BLOCK_TMP.  This macro
+   increments `round'.  */
+#define ROUND_LAST(which, subkeys, block, block_tmp) \
+  {                                                  \
+    BLOCK_XOR (block, subkeys[round]);               \
+    round++;                                         \
+    SBOX (which, block, block_tmp, 0);               \
+    BLOCK_XOR (block_tmp, subkeys[round]);           \
+    round++;                                         \
+  }
+
+/* Apply an inverse Serpent round to BLOCK, using the SBOX number
+   WHICH and the subkeys contained in SUBKEYS.  Use BLOCK_TMP as
+   temporary storage.  This macro increments `round'.  */
+#define ROUND_INVERSE(which, subkey, block, block_tmp) \
+  {                                                    \
+    LINEAR_TRANSFORMATION_INVERSE (block);             \
+    SBOX_INVERSE (which, block, block_tmp, 0);         \
+    BLOCK_XOR (block_tmp, subkey[round]);              \
+    round--;                                           \
+    BLOCK_COPY (block, block_tmp);                     \
+  }
+
+/* Apply the first Serpent round to BLOCK, using the SBOX number WHICH
+   and the subkeys contained in SUBKEYS.  Use BLOCK_TMP as temporary
+   storage.  The result will be stored in BLOCK_TMP.  This macro
+   increments `round'.  */
+#define ROUND_FIRST_INVERSE(which, subkeys, block, block_tmp) \
+  {                                                           \
+    BLOCK_XOR (block, subkeys[round]);                        \
+    round--;                                                  \
+    SBOX_INVERSE (which, block, block_tmp, 0);                \
+    BLOCK_XOR (block_tmp, subkeys[round]);                    \
+    round--;                                                  \
+  }
+
+/* Convert the user provided key KEY of KEY_LENGTH bytes into the
+   internally used format.  */
+static void
+serpent_key_prepare (const unsigned char *key, unsigned int key_length,
+                    serpent_key_t key_prepared)
+{
+  unsigned int i;
+
+  /* Copy key.  */
+  for (i = 0; i < key_length / 4; i++)
+    {
+#ifdef WORDS_BIGENDIAN
+      key_prepared[i] = byte_swap_32 (((uint32_t *) key)[i]);
+#else
+      key_prepared[i] = ((uint32_t *) key)[i];
+#endif
+    }
+
+  if (i < 8)
+    {
+      /* Key must be padded according to the Serpent
+        specification.  */
+      key_prepared[i] = 0x00000001;
+
+      for (i++; i < 8; i++)
+       key_prepared[i] = 0;
+    }
+}
+
+/* Derive the 33 subkeys from KEY and store them in SUBKEYS.  */
+static void
+serpent_subkeys_generate (serpent_key_t key, serpent_subkeys_t subkeys)
+{
+  uint32_t w_real[140];                /* The `prekey'.  */
+  uint32_t k[132];
+  uint32_t *w = &w_real[8];
+  int i, j;
+
+  /* Initialize with key values.  */
+  for (i = 0; i < 8; i++)
+    w[i - 8] = key[i];
+
+  /* Expand to intermediate key using the affine recurrence.  */
+  for (i = 0; i < 132; i++)
+    w[i] = rol (w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11);
+
+  /* Calculate subkeys via S-Boxes, in bitslice mode.  */
+  SBOX (3, w, k,   0);
+  SBOX (2, w, k,   4);
+  SBOX (1, w, k,   8);
+  SBOX (0, w, k,  12);
+  SBOX (7, w, k,  16);
+  SBOX (6, w, k,  20);
+  SBOX (5, w, k,  24);
+  SBOX (4, w, k,  28);
+  SBOX (3, w, k,  32);
+  SBOX (2, w, k,  36);
+  SBOX (1, w, k,  40);
+  SBOX (0, w, k,  44);
+  SBOX (7, w, k,  48);
+  SBOX (6, w, k,  52);
+  SBOX (5, w, k,  56);
+  SBOX (4, w, k,  60);
+  SBOX (3, w, k,  64);
+  SBOX (2, w, k,  68);
+  SBOX (1, w, k,  72);
+  SBOX (0, w, k,  76);
+  SBOX (7, w, k,  80);
+  SBOX (6, w, k,  84);
+  SBOX (5, w, k,  88);
+  SBOX (4, w, k,  92);
+  SBOX (3, w, k,  96);
+  SBOX (2, w, k, 100);
+  SBOX (1, w, k, 104);
+  SBOX (0, w, k, 108);
+  SBOX (7, w, k, 112);
+  SBOX (6, w, k, 116);
+  SBOX (5, w, k, 120);
+  SBOX (4, w, k, 124);
+  SBOX (3, w, k, 128);
+
+  /* Renumber subkeys.  */
+  for (i = 0; i < ROUNDS + 1; i++)
+    for (j = 0; j < 4; j++)
+      subkeys[i][j] = k[4 * i + j];
+}
+
+/* Initialize CONTEXT with the key KEY of KEY_LENGTH bits.  */
+static void
+serpent_setkey_internal (serpent_context_t *context,
+                        const unsigned char *key, unsigned int key_length)
+{
+  serpent_key_t key_prepared;
+
+  serpent_key_prepare (key, key_length, key_prepared);
+  serpent_subkeys_generate (key_prepared, context->keys);
+}
+
+
+/* Initialize CTX with the key KEY of KEY_LENGTH bytes.  */
+static int
+serpent_setkey (serpent_context_t *context,
+               const void *key, unsigned int key_length)
+{
+  static const char *serpent_test_ret;
+  static int serpent_init_done;
+  int ret = 0;
+  
+  if (!serpent_init_done)
+    {
+      /* Execute a self-test the first time, Serpent is used.  */
+      serpent_test_ret = serpent_test ();
+#ifdef TEST
+      if (serpent_test_ret)
+       fprintf (stderr, "Serpent test failure: %s\n", serpent_test_ret);
+#endif /*TEST*/
+      serpent_init_done = 1;
+    }
+
+  if (serpent_test_ret)
+    ret = -1;
+  else
+    serpent_setkey_internal (context, key, key_length);
+
+  return ret;
+}
+
+
+static void
+serpent_encrypt_internal (serpent_context_t *context,
+                         const serpent_block_t input, serpent_block_t output)
+{
+  serpent_block_t b, b_next;
+  int round = 0;
+
+#ifdef WORDS_BIGENDIAN
+  b[0] = byte_swap_32 (input[0]);
+  b[1] = byte_swap_32 (input[1]);
+  b[2] = byte_swap_32 (input[2]);
+  b[3] = byte_swap_32 (input[3]);
+#else
+  b[0] = input[0];
+  b[1] = input[1];
+  b[2] = input[2];
+  b[3] = input[3];
+#endif
+
+  ROUND (0, context->keys, b, b_next);
+  ROUND (1, context->keys, b, b_next);
+  ROUND (2, context->keys, b, b_next);
+  ROUND (3, context->keys, b, b_next);
+  ROUND (4, context->keys, b, b_next);
+  ROUND (5, context->keys, b, b_next);
+  ROUND (6, context->keys, b, b_next);
+  ROUND (7, context->keys, b, b_next);
+  ROUND (0, context->keys, b, b_next);
+  ROUND (1, context->keys, b, b_next);
+  ROUND (2, context->keys, b, b_next);
+  ROUND (3, context->keys, b, b_next);
+  ROUND (4, context->keys, b, b_next);
+  ROUND (5, context->keys, b, b_next);
+  ROUND (6, context->keys, b, b_next);
+  ROUND (7, context->keys, b, b_next);
+  ROUND (0, context->keys, b, b_next);
+  ROUND (1, context->keys, b, b_next);
+  ROUND (2, context->keys, b, b_next);
+  ROUND (3, context->keys, b, b_next);
+  ROUND (4, context->keys, b, b_next);
+  ROUND (5, context->keys, b, b_next);
+  ROUND (6, context->keys, b, b_next);
+  ROUND (7, context->keys, b, b_next);
+  ROUND (0, context->keys, b, b_next);
+  ROUND (1, context->keys, b, b_next);
+  ROUND (2, context->keys, b, b_next);
+  ROUND (3, context->keys, b, b_next);
+  ROUND (4, context->keys, b, b_next);
+  ROUND (5, context->keys, b, b_next);
+  ROUND (6, context->keys, b, b_next);
+
+  ROUND_LAST (7, context->keys, b, b_next);
+
+#ifdef WORDS_BIGENDIAN
+  output[0] = byte_swap_32 (b_next[0]);
+  output[1] = byte_swap_32 (b_next[1]);
+  output[2] = byte_swap_32 (b_next[2]);
+  output[3] = byte_swap_32 (b_next[3]);
+#else
+  output[0] = b_next[0];
+  output[1] = b_next[1];
+  output[2] = b_next[2];
+  output[3] = b_next[3];
+#endif
+}
+
+
+#ifdef TEST /* We only use CFB mode, thus tehre is no actual need for
+               the decryption fucntion. */
+static void
+serpent_decrypt_internal (serpent_context_t *context,
+                         const serpent_block_t input, serpent_block_t output)
+{
+  serpent_block_t b, b_next;
+  int round = ROUNDS;
+
+#ifdef WORDS_BIGENDIAN
+  b_next[0] = byte_swap_32 (input[0]);
+  b_next[1] = byte_swap_32 (input[1]);
+  b_next[2] = byte_swap_32 (input[2]);
+  b_next[3] = byte_swap_32 (input[3]);
+#else
+  b_next[0] = input[0];
+  b_next[1] = input[1];
+  b_next[2] = input[2];
+  b_next[3] = input[3];
+#endif
+
+  ROUND_FIRST_INVERSE (7, context->keys, b_next, b);
+
+  ROUND_INVERSE (6, context->keys, b, b_next);
+  ROUND_INVERSE (5, context->keys, b, b_next);
+  ROUND_INVERSE (4, context->keys, b, b_next);
+  ROUND_INVERSE (3, context->keys, b, b_next);
+  ROUND_INVERSE (2, context->keys, b, b_next);
+  ROUND_INVERSE (1, context->keys, b, b_next);
+  ROUND_INVERSE (0, context->keys, b, b_next);
+  ROUND_INVERSE (7, context->keys, b, b_next);
+  ROUND_INVERSE (6, context->keys, b, b_next);
+  ROUND_INVERSE (5, context->keys, b, b_next);
+  ROUND_INVERSE (4, context->keys, b, b_next);
+  ROUND_INVERSE (3, context->keys, b, b_next);
+  ROUND_INVERSE (2, context->keys, b, b_next);
+  ROUND_INVERSE (1, context->keys, b, b_next);
+  ROUND_INVERSE (0, context->keys, b, b_next);
+  ROUND_INVERSE (7, context->keys, b, b_next);
+  ROUND_INVERSE (6, context->keys, b, b_next);
+  ROUND_INVERSE (5, context->keys, b, b_next);
+  ROUND_INVERSE (4, context->keys, b, b_next);
+  ROUND_INVERSE (3, context->keys, b, b_next);
+  ROUND_INVERSE (2, context->keys, b, b_next);
+  ROUND_INVERSE (1, context->keys, b, b_next);
+  ROUND_INVERSE (0, context->keys, b, b_next);
+  ROUND_INVERSE (7, context->keys, b, b_next);
+  ROUND_INVERSE (6, context->keys, b, b_next);
+  ROUND_INVERSE (5, context->keys, b, b_next);
+  ROUND_INVERSE (4, context->keys, b, b_next);
+  ROUND_INVERSE (3, context->keys, b, b_next);
+  ROUND_INVERSE (2, context->keys, b, b_next);
+  ROUND_INVERSE (1, context->keys, b, b_next);
+  ROUND_INVERSE (0, context->keys, b, b_next);
+
+
+#ifdef WORDS_BIGENDIAN
+  output[0] = byte_swap_32 (b_next[0]);
+  output[1] = byte_swap_32 (b_next[1]);
+  output[2] = byte_swap_32 (b_next[2]);
+  output[3] = byte_swap_32 (b_next[3]);
+#else
+  output[0] = b_next[0];
+  output[1] = b_next[1];
+  output[2] = b_next[2];
+  output[3] = b_next[3];
+#endif
+}
+#endif /*TEST*/
+
+
+static const char *
+serpent_test (void)
+{
+  serpent_context_t context;
+  unsigned char scratch[16];
+  unsigned int i;
+
+  static struct test
+  {
+    int key_length;
+    unsigned char key[32];
+    unsigned char text_plain[16];
+    unsigned char text_cipher[16];
+  } test_data[] =
+    {
+      {
+       16,
+       "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+       "\xD2\x9D\x57\x6F\xCE\xA3\xA3\xA7\xED\x90\x99\xF2\x92\x73\xD7\x8E",
+       "\xB2\x28\x8B\x96\x8A\xE8\xB0\x86\x48\xD1\xCE\x96\x06\xFD\x99\x2D"
+      },
+      {
+       0
+      }
+    };
+
+  for (i = 0; test_data[i].key_length; i++)
+    {
+      serpent_setkey_internal (&context, test_data[i].key,
+                               test_data[i].key_length);
+      serpent_encrypt_internal (&context,
+                               (const uint32_t *) test_data[i].text_plain,
+                               (uint32_t *) scratch);
+
+      if (memcmp (scratch, test_data[i].text_cipher, sizeof (serpent_block_t)))
+        return "Serpent test encryption failed";
+
+#ifdef TEST
+      serpent_decrypt_internal (&context,
+                                (const uint32_t *) test_data[i].text_cipher,
+                                (uint32_t *) scratch);
+      if (memcmp (scratch, test_data[i].text_plain, sizeof (serpent_block_t)))
+        return "Serpent test decryption failed.";
+#endif /*TEST*/
+    }
+
+  return NULL;
+}
+
+/* Open a new cipher instance with KEY and IV.  On error the fucntion
+   returns NULL, otherwise a handle which needs to be provided to the
+   other functions. */
+symenc_t
+symenc_open (const void *key, size_t keylen, const void *iv, size_t ivlen)
+{
+  symenc_t ctx;
+
+  if (!key || keylen != 16 || !iv || ivlen != BLOCKSIZE)
+    return NULL;
+  ctx = calloc (1, sizeof *ctx);
+  if (!ctx)
+    return NULL;
+  if (serpent_setkey (ctx, key, keylen))
+    {
+      free (ctx);
+      return NULL;
+    }
+  memcpy (ctx->u.cfb.iv, iv, BLOCKSIZE);
+  return ctx;
+}
+
+/* Close a Serpent encryption context.  */
+void
+symenc_close (symenc_t ctx)
+{
+  if (ctx)
+    {
+      free (ctx);
+    }
+}
+
+
+void
+symenc_cfb_encrypt (symenc_t ctx,
+                    void *buffer_out, const void *buffer_in, size_t nbytes)
+{
+  const unsigned char *inbuf  = buffer_in;
+  unsigned char *outbuf = buffer_out;
+  unsigned char *ivp;
+  int i;
+
+  assert (ctx->u.cfb.unused <= BLOCKSIZE);
+
+  /* If the input is short enough to be entirely encoded by the
+     remaining XOR mask, XOR the input with the IV and store input
+     into IV.  */
+  if (nbytes <= ctx->u.cfb.unused )
+    {
+      for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+           nbytes; nbytes--, ctx->u.cfb.unused--)
+        *outbuf++ = (*ivp++ ^= *inbuf++);
+      return; /* Ready.  */
+    }
+
+  /* If there are still some bytes left in the XOR mask, XOR the input
+     with the IV and store input into IV.  */
+  if (ctx->u.cfb.unused)
+    {
+      nbytes -= ctx->u.cfb.unused;
+      for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+           ctx->u.cfb.unused; ctx->u.cfb.unused--)
+        *outbuf++ = (*ivp++ ^= *inbuf++);
+    }
+
+  /* Now process complete blocks. */
+  while (nbytes >= BLOCKSIZE) 
+    {
+      serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv, 
+                                (uint32_t*)ctx->u.cfb.iv);
+      for (ivp = ctx->u.cfb.iv, i=0; i < BLOCKSIZE; i++ )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
+      nbytes -= BLOCKSIZE;
+    }
+
+  /* Process any remaining bytes from the last block.  */
+  if (nbytes) 
+    { 
+      serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv, 
+                                (uint32_t*)ctx->u.cfb.iv);
+      ctx->u.cfb.unused = BLOCKSIZE - nbytes;
+      for (ivp = ctx->u.cfb.iv; nbytes; nbytes--)
+        *outbuf++ = (*ivp++ ^= *inbuf++);
+    }
+}
+
+
+void
+symenc_cfb_decrypt (symenc_t ctx,
+                    void *buffer_out, const void *buffer_in, size_t nbytes)
+{
+  const unsigned char *inbuf  = buffer_in;
+  unsigned char *outbuf = buffer_out;
+  unsigned char *ivp;
+  uint32_t temp;
+  int i;
+
+  assert (ctx->u.cfb.unused <= BLOCKSIZE);
+
+  /* If the input is short enough to be entirely decoded by the
+     remaining XOR mask, XOR the input with the IV and store input
+     into IV.  */
+  if (nbytes <= ctx->u.cfb.unused) 
+    {
+      for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+           nbytes; nbytes--, ctx->u.cfb.unused--) 
+        {
+          temp = *inbuf++;
+          *outbuf++ = (*ivp ^ temp);
+          *ivp++ = temp;
+       }
+      return; /* Ready.  */
+    }
+
+  /* If there are still some bytes left in the XOR mask, XOR the input
+     with the IV and store input into IV.  */
+  if (ctx->u.cfb.unused)
+    {
+      nbytes -= ctx->u.cfb.unused;
+      for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+           ctx->u.cfb.unused; ctx->u.cfb.unused--)
+        {
+          temp = *inbuf++;
+          *outbuf++ = (*ivp ^ temp);
+          *ivp++ = temp;
+       }
+    }
+
+  /* Now process complete blocks.  */
+  while (nbytes >= BLOCKSIZE)
+    {
+      serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv, 
+                                (uint32_t*)ctx->u.cfb.iv);
+      for (ivp = ctx->u.cfb.iv, i=0; i < BLOCKSIZE; i++)
+        {
+          temp = *inbuf++;
+          *outbuf++ = (*ivp ^ temp);
+          *ivp++ = temp;
+       }
+      nbytes -= BLOCKSIZE;
+    }
+
+  /* Process any remaining bytes from the last block.  */
+  if (nbytes)
+    {
+      serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv, 
+                                (uint32_t*)ctx->u.cfb.iv);
+      ctx->u.cfb.unused = BLOCKSIZE - nbytes;
+      for (ivp = ctx->u.cfb.iv; nbytes; nbytes--)
+        {
+          temp = *inbuf++;
+          *outbuf++ = (*ivp ^ temp);
+          *ivp++ = temp;
+       }
+    }
+}
+
+
+
+#ifdef TEST
+int 
+main (int argc, char** argv)
+{
+  int decmode;
+  symenc_t ctx;
+  char buf[255];
+  size_t n;
+
+  if (argc) { argc--, argv++; }
+
+  decmode = (argc && !strcmp (*argv, "--decrypt"));
+
+  ctx = symenc_open ("1234567890123456", 16, "abcdefghijklmnop", 16);
+  if (!ctx)
+    abort ();
+  
+  while ( (n=fread (buf, 1, 255, stdin)) )
+    {
+      if (decmode)
+        symenc_cfb_decrypt (ctx, buf, buf, n);
+      else
+        symenc_cfb_encrypt (ctx, buf, buf, n);
+      if (fwrite (buf, 1, n, stdout) != n)
+        abort ();
+    }
+
+  symenc_close (ctx);
+  return 0;
+}
+
+#endif /*TEST*/
+
+
+
+/*
+Local Variables:
+compile-command: "gcc -Wall -g -O2 -DTEST -o serpent serpent.c"
+End:
+*/
diff --git a/src/serpent.h b/src/serpent.h
new file mode 100644 (file)
index 0000000..0ab6407
--- /dev/null
@@ -0,0 +1,55 @@
+/* serpent.h - Definitions of the Serpent encryption algorithm.
+ *     Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ * 
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef SERPENT_H
+#define SERPENT_H
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+
+/* Note that there is no special rason that we use Serpent and not AES
+   or even CAST5, blowfish or whatever.  Any decent block cipher will
+   do; even the blocksize does not matter for our purpose.  The
+   Serpent implementation was just the most convenient one top put
+   into this project. */
+
+struct symenc_context_s;
+typedef struct symenc_context_s *symenc_t;
+
+
+symenc_t symenc_open (const void *key, size_t keylen, 
+                      const void *iv, size_t ivlen);
+void symenc_close (symenc_t ctx);
+void symenc_cfb_encrypt (symenc_t ctx, void *buffer_out, 
+                         const void *buffer_in, size_t nbytes);
+void symenc_cfb_decrypt (symenc_t ctx, void *buffer_out, 
+                         const void *buffer_in, size_t nbytes);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*SERPENT_H*/