dirmngr: Add a background task framework.
[gnupg.git] / common / b64dec.c
index edd72c6..6af494b 100644 (file)
@@ -1,20 +1,21 @@
 /* b64dec.c - Simple Base64 decoder.
  * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2008, 2011, 2016 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
+ * This file 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.
  *
- * GnuPG is distributed in the hope that it will be useful,
+ * This file 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 General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -51,7 +52,7 @@ static unsigned char const asctobin[128] =
 
 enum decoder_states
   {
-    s_init, s_idle, s_lfseen, s_begin,
+    s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin,
     s_b64_0, s_b64_1, s_b64_2, s_b64_3,
     s_waitendtitle, s_waitend
   };
@@ -61,26 +62,18 @@ enum decoder_states
 /* Initialize the context for the base64 decoder.  If TITLE is NULL a
    plain base64 decoding is done.  If it is the empty string the
    decoder will skip everything until a "-----BEGIN " line has been
-   seen, decoding ends at a "----END " line.
-
-   Not yet implemented: If TITLE is either "PGP" or begins with "PGP "
-   the PGP armor lines are skipped as well.  */
+   seen, decoding ends at a "----END " line.  */
 gpg_error_t
 b64dec_start (struct b64state *state, const char *title)
 {
   memset (state, 0, sizeof *state);
   if (title)
     {
-      if (!strncmp (title, "PGP", 3) && (!title[3] || title[3] == ' '))
-        state->lasterr = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+      state->title = xtrystrdup (title);
+      if (!state->title)
+        state->lasterr = gpg_error_from_syserror ();
       else
-        {
-          state->title = xtrystrdup (title);
-          if (!state->title)
-            state->lasterr = gpg_error_from_syserror ();
-          else
-            state->idx = s_init;
-        }
+        state->idx = s_init;
     }
   else
     state->idx = s_b64_0;
@@ -113,6 +106,7 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length,
 
   for (s=d=buffer; length && !state->stop_seen; length--, s++)
     {
+    again:
       switch (ds)
         {
         case s_idle:
@@ -124,14 +118,45 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length,
           break;
         case s_init:
           ds = s_lfseen;
+          /* fall through */
         case s_lfseen:
           if (*s != "-----BEGIN "[pos])
-            ds = s_idle;
+            {
+              ds = s_idle;
+              goto again;
+            }
           else if (pos == 10)
-            ds = s_begin;
+            {
+              pos = 0;
+              ds = s_beginseen;
+            }
+          else
+            pos++;
+          break;
+        case s_beginseen:
+          if (*s != "PGP "[pos])
+            ds = s_begin; /* Not a PGP armor.  */
+          else if (pos == 3)
+            ds = s_waitheader;
           else
             pos++;
           break;
+        case s_waitheader:
+          if (*s == '\n')
+            ds = s_waitblank;
+          break;
+        case s_waitblank:
+          if (*s == '\n')
+            ds = s_b64_0; /* blank line found.  */
+          else if (*s == ' ' || *s == '\r' || *s == '\t')
+            ; /* Ignore spaces. */
+          else
+            {
+              /* Armor header line.  Note that we don't care that our
+               * FSM accepts a header prefixed with spaces.  */
+              ds = s_waitheader; /* Wait for next header.  */
+            }
+          break;
         case s_begin:
           if (*s == '\n')
             ds = s_b64_0;
@@ -219,10 +244,11 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length,
 gpg_error_t
 b64dec_finish (struct b64state *state)
 {
+  xfree (state->title);
+  state->title = NULL;
+
   if (state->lasterr)
     return state->lasterr;
 
-  xfree (state->title);
-  state->title = NULL;
   return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
 }