LCOV - code coverage report
Current view: top level - tools - mime-parser.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 355 0.0 %
Date: 2016-12-01 18:37:21 Functions: 0 14 0.0 %

          Line data    Source code
       1             : /* mime-parser.c - Parse MIME structures (high level rfc822 parser).
       2             :  * Copyright (C) 2016 g10 Code GmbH
       3             :  *
       4             :  * This file is part of GnuPG.
       5             :  *
       6             :  * GnuPG is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * GnuPG is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program; if not, see <https://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include <config.h>
      21             : #include <stdio.h>
      22             : #include <stdlib.h>
      23             : #include <string.h>
      24             : 
      25             : #include "util.h"
      26             : #include "rfc822parse.h"
      27             : #include "mime-parser.h"
      28             : 
      29             : 
      30             : enum pgpmime_states
      31             :   {
      32             :     PGPMIME_NONE = 0,
      33             :     PGPMIME_WAIT_ENCVERSION,
      34             :     PGPMIME_IN_ENCVERSION,
      35             :     PGPMIME_WAIT_ENCDATA,
      36             :     PGPMIME_IN_ENCDATA,
      37             :     PGPMIME_GOT_ENCDATA,
      38             :     PGPMIME_WAIT_SIGNEDDATA,
      39             :     PGPMIME_IN_SIGNEDDATA,
      40             :     PGPMIME_WAIT_SIGNATURE,
      41             :     PGPMIME_IN_SIGNATURE,
      42             :     PGPMIME_GOT_SIGNATURE,
      43             :     PGPMIME_INVALID
      44             :   };
      45             : 
      46             : 
      47             : /* Definition of the mime parser object.  */
      48             : struct mime_parser_context_s
      49             : {
      50             :   void *cookie;                /* Cookie passed to all callbacks.  */
      51             : 
      52             :   /* The callback to announce a new part.  */
      53             :   gpg_error_t (*new_part) (void *cookie,
      54             :                            const char *mediatype,
      55             :                            const char *mediasubtype);
      56             :   /* The callback to return data of a part.  */
      57             :   gpg_error_t (*part_data) (void *cookie,
      58             :                             const void *data,
      59             :                             size_t datalen);
      60             :   /* The callback to collect encrypted data.  */
      61             :   gpg_error_t (*collect_encrypted) (void *cookie, const char *data);
      62             :   /* The callback to collect signed data.  */
      63             :   gpg_error_t (*collect_signeddata) (void *cookie, const char *data);
      64             :   /* The callback to collect a signature.  */
      65             :   gpg_error_t (*collect_signature) (void *cookie, const char *data);
      66             : 
      67             :   /* The RFC822 parser context is stored here during callbacks.  */
      68             :   rfc822parse_t msg;
      69             : 
      70             :   /* Helper to convey error codes from user callbacks.  */
      71             :   gpg_error_t err;
      72             : 
      73             :   int nesting_level;           /* The current nesting level.  */
      74             :   int hashing_at_level;        /* The nesting level at which we are hashing. */
      75             :   enum pgpmime_states pgpmime; /* Current PGP/MIME state.  */
      76             :   unsigned int delay_hashing:1;/* Helper for PGPMIME_IN_SIGNEDDATA. */
      77             :   unsigned int want_part:1;    /* Return the current part.  */
      78             :   unsigned int decode_part:2;  /* Decode the part.  1 = QP, 2 = Base64. */
      79             : 
      80             :   unsigned int verbose:1;      /* Enable verbose mode.  */
      81             :   unsigned int debug:1;        /* Enable debug mode.  */
      82             : 
      83             :   /* Flags to help with debug output.  */
      84             :   struct {
      85             :     unsigned int n_skip;         /* Skip showing these number of lines.  */
      86             :     unsigned int header:1;       /* Show the header lines.  */
      87             :     unsigned int data:1;         /* Show the data lines.  */
      88             :     unsigned int as_note:1;      /* Show the next data line as a note.  */
      89             :     unsigned int boundary : 1;
      90             :   } show;
      91             : 
      92             :   struct b64state *b64state;     /* NULL or malloced Base64 decoder state.  */
      93             : 
      94             :   /* A buffer for reading a mail line,  */
      95             :   char line[5000];
      96             : };
      97             : 
      98             : 
      99             : /* Print the event received by the parser for debugging.  */
     100             : static void
     101           0 : show_message_parser_event (rfc822parse_event_t event)
     102             : {
     103             :   const char *s;
     104             : 
     105           0 :   switch (event)
     106             :     {
     107           0 :     case RFC822PARSE_OPEN: s= "Open"; break;
     108           0 :     case RFC822PARSE_CLOSE: s= "Close"; break;
     109           0 :     case RFC822PARSE_CANCEL: s= "Cancel"; break;
     110           0 :     case RFC822PARSE_T2BODY: s= "T2Body"; break;
     111           0 :     case RFC822PARSE_FINISH: s= "Finish"; break;
     112           0 :     case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
     113           0 :     case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
     114           0 :     case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
     115           0 :     case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
     116           0 :     case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
     117           0 :     case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
     118           0 :     case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
     119           0 :     case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
     120           0 :     default: s= "[unknown event]"; break;
     121             :     }
     122           0 :   log_debug ("*** RFC822 event %s\n", s);
     123           0 : }
     124             : 
     125             : 
     126             : /* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
     127             :    Returns the new length of the buffer and stores true at R_SLBRK if
     128             :    the line ended with a soft line break; false is stored if not.
     129             :    This function asssumes that a complete line is passed in
     130             :    buffer.  */
     131             : static size_t
     132           0 : qp_decode (char *buffer, size_t length, int *r_slbrk)
     133             : {
     134             :   char *d, *s;
     135             : 
     136           0 :   if (r_slbrk)
     137           0 :     *r_slbrk = 0;
     138             : 
     139             :   /* Fixme:  We should remove trailing white space first.  */
     140           0 :   for (s=d=buffer; length; length--)
     141             :     {
     142           0 :       if (*s == '=')
     143             :         {
     144           0 :           if (length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
     145             :             {
     146           0 :               s++;
     147           0 :               *(unsigned char*)d++ = xtoi_2 (s);
     148           0 :               s += 2;
     149           0 :               length -= 2;
     150             :             }
     151           0 :           else if (length > 2 && s[1] == '\r' && s[2] == '\n')
     152             :             {
     153             :               /* Soft line break.  */
     154           0 :               s += 3;
     155           0 :               length -= 2;
     156           0 :               if (r_slbrk && length == 1)
     157           0 :                 *r_slbrk = 1;
     158             :             }
     159           0 :           else if (length > 1 && s[1] == '\n')
     160             :             {
     161             :               /* Soft line break with only a Unix line terminator. */
     162           0 :               s += 2;
     163           0 :               length -= 1;
     164           0 :               if (r_slbrk && length == 1)
     165           0 :                 *r_slbrk = 1;
     166             :             }
     167           0 :           else if (length == 1)
     168             :             {
     169             :               /* Soft line break at the end of the line. */
     170           0 :               s += 1;
     171           0 :               if (r_slbrk)
     172           0 :                 *r_slbrk = 1;
     173             :             }
     174             :           else
     175           0 :             *d++ = *s++;
     176             :         }
     177             :       else
     178           0 :         *d++ = *s++;
     179             :     }
     180             : 
     181           0 :   return d - buffer;
     182             : }
     183             : 
     184             : 
     185             : /* This function is called by parse_mail to communicate events.  This
     186             :  * callback communicates with the caller using a structure passed in
     187             :  * OPAQUE.  Should return 0 on success or set ERRNO and return -1. */
     188             : static int
     189           0 : parse_message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
     190             : {
     191           0 :   mime_parser_t ctx = opaque;
     192             :   const char *s;
     193           0 :   int rc = 0;
     194             : 
     195             :   /* Make the RFC822 parser context availabale for callbacks.  */
     196           0 :   ctx->msg = msg;
     197             : 
     198           0 :   if (ctx->debug)
     199           0 :     show_message_parser_event (event);
     200             : 
     201           0 :   if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
     202             :     {
     203             :       /* We need to check here whether to start collecting signed data
     204             :        * because attachments might come without header lines and thus
     205             :        * we won't see the BEGIN_HEADER event.  */
     206           0 :       if (ctx->pgpmime == PGPMIME_WAIT_SIGNEDDATA)
     207             :         {
     208           0 :           if (ctx->debug)
     209           0 :             log_debug ("begin_hash\n");
     210           0 :           ctx->hashing_at_level = ctx->nesting_level;
     211           0 :           ctx->pgpmime = PGPMIME_IN_SIGNEDDATA;
     212           0 :           ctx->delay_hashing = 0;
     213             :         }
     214             :     }
     215             : 
     216           0 :   if (event == RFC822PARSE_OPEN)
     217             :     {
     218             :       /* Initialize for a new message. */
     219           0 :       ctx->show.header = 1;
     220             :     }
     221           0 :   else if (event == RFC822PARSE_T2BODY)
     222             :     {
     223             :       rfc822parse_field_t field;
     224             : 
     225           0 :       ctx->want_part = 0;
     226           0 :       ctx->decode_part = 0;
     227           0 :       field = rfc822parse_parse_field (msg, "Content-Type", -1);
     228           0 :       if (field)
     229             :         {
     230             :           const char *s1, *s2;
     231             : 
     232           0 :           s1 = rfc822parse_query_media_type (field, &s2);
     233           0 :           if (s1)
     234             :             {
     235           0 :               if (ctx->verbose)
     236           0 :                 log_debug ("h media: %*s%s %s\n",
     237           0 :                            ctx->nesting_level*2, "", s1, s2);
     238           0 :               if (ctx->pgpmime == PGPMIME_WAIT_ENCVERSION)
     239             :                 {
     240           0 :                   if (!strcmp (s1, "application")
     241           0 :                       && !strcmp (s2, "pgp-encrypted"))
     242             :                     {
     243           0 :                       if (ctx->debug)
     244           0 :                         log_debug ("c begin_encversion\n");
     245           0 :                       ctx->pgpmime = PGPMIME_IN_ENCVERSION;
     246             :                     }
     247             :                   else
     248             :                     {
     249           0 :                       log_error ("invalid PGP/MIME structure;"
     250             :                                  " expected '%s', got '%s/%s'\n",
     251             :                                  "application/pgp-encrypted", s1, s2);
     252           0 :                       ctx->pgpmime = PGPMIME_INVALID;
     253             :                     }
     254             :                 }
     255           0 :               else if (ctx->pgpmime == PGPMIME_WAIT_ENCDATA)
     256             :                 {
     257           0 :                   if (!strcmp (s1, "application")
     258           0 :                       && !strcmp (s2, "octet-stream"))
     259             :                     {
     260           0 :                       if (ctx->debug)
     261           0 :                         log_debug ("c begin_encdata\n");
     262           0 :                       ctx->pgpmime = PGPMIME_IN_ENCDATA;
     263             :                     }
     264             :                   else
     265             :                     {
     266           0 :                       log_error ("invalid PGP/MIME structure;"
     267             :                                  " expected '%s', got '%s/%s'\n",
     268             :                                  "application/octet-stream", s1, s2);
     269           0 :                       ctx->pgpmime = PGPMIME_INVALID;
     270             :                     }
     271             :                 }
     272           0 :               else if (ctx->pgpmime == PGPMIME_WAIT_SIGNATURE)
     273             :                 {
     274           0 :                   if (!strcmp (s1, "application")
     275           0 :                       && !strcmp (s2, "pgp-signature"))
     276             :                     {
     277           0 :                       if (ctx->debug)
     278           0 :                         log_debug ("c begin_signature\n");
     279           0 :                       ctx->pgpmime = PGPMIME_IN_SIGNATURE;
     280             :                     }
     281             :                   else
     282             :                     {
     283           0 :                       log_error ("invalid PGP/MIME structure;"
     284             :                                  " expected '%s', got '%s/%s'\n",
     285             :                                  "application/pgp-signature", s1, s2);
     286           0 :                       ctx->pgpmime = PGPMIME_INVALID;
     287             :                     }
     288             :                 }
     289           0 :               else if (!strcmp (s1, "multipart")
     290           0 :                        && !strcmp (s2, "encrypted"))
     291             :                 {
     292           0 :                   s = rfc822parse_query_parameter (field, "protocol", 0);
     293           0 :                   if (s)
     294             :                     {
     295           0 :                       if (ctx->debug)
     296           0 :                         log_debug ("h encrypted.protocol: %s\n", s);
     297           0 :                       if (!strcmp (s, "application/pgp-encrypted"))
     298             :                         {
     299           0 :                           if (ctx->pgpmime)
     300           0 :                             log_error ("note: "
     301             :                                        "ignoring nested PGP/MIME signature\n");
     302             :                           else
     303           0 :                             ctx->pgpmime = PGPMIME_WAIT_ENCVERSION;
     304             :                         }
     305           0 :                       else if (ctx->verbose)
     306           0 :                         log_debug ("# this protocol is not supported\n");
     307             :                     }
     308             :                 }
     309           0 :               else if (!strcmp (s1, "multipart")
     310           0 :                        && !strcmp (s2, "signed"))
     311             :                 {
     312           0 :                   s = rfc822parse_query_parameter (field, "protocol", 1);
     313           0 :                   if (s)
     314             :                     {
     315           0 :                       if (ctx->debug)
     316           0 :                         log_debug ("h signed.protocol: %s\n", s);
     317           0 :                       if (!strcmp (s, "application/pgp-signature"))
     318             :                         {
     319           0 :                           if (ctx->pgpmime)
     320           0 :                             log_error ("note: "
     321             :                                        "ignoring nested PGP/MIME signature\n");
     322             :                           else
     323           0 :                             ctx->pgpmime = PGPMIME_WAIT_SIGNEDDATA;
     324             :                         }
     325           0 :                       else if (ctx->verbose)
     326           0 :                         log_debug ("# this protocol is not supported\n");
     327             :                     }
     328             :                 }
     329           0 :               else if (ctx->new_part)
     330             :                 {
     331           0 :                   ctx->err = ctx->new_part (ctx->cookie, s1, s2);
     332           0 :                   if (!ctx->err)
     333           0 :                     ctx->want_part = 1;
     334           0 :                   else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
     335           0 :                     ctx->err = 0;
     336           0 :                   else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
     337             :                     {
     338           0 :                       ctx->want_part = ctx->decode_part = 1;
     339           0 :                       ctx->err = 0;
     340             :                     }
     341             :                 }
     342             :             }
     343             :           else
     344             :             {
     345           0 :               if (ctx->debug)
     346           0 :                 log_debug ("h media: %*s none\n", ctx->nesting_level*2, "");
     347           0 :               if (ctx->new_part)
     348             :                 {
     349           0 :                   ctx->err = ctx->new_part (ctx->cookie, "", "");
     350           0 :                   if (!ctx->err)
     351           0 :                     ctx->want_part = 1;
     352           0 :                   else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
     353           0 :                     ctx->err = 0;
     354           0 :                   else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
     355             :                     {
     356           0 :                       ctx->want_part = ctx->decode_part = 1;
     357           0 :                       ctx->err = 0;
     358             :                     }
     359             :                 }
     360             :             }
     361             : 
     362           0 :           rfc822parse_release_field (field);
     363             :         }
     364             :       else
     365             :         {
     366           0 :           if (ctx->verbose)
     367           0 :             log_debug ("h media: %*stext plain [assumed]\n",
     368           0 :                        ctx->nesting_level*2, "");
     369           0 :           if (ctx->new_part)
     370             :             {
     371           0 :               ctx->err = ctx->new_part (ctx->cookie, "text", "plain");
     372           0 :               if (!ctx->err)
     373           0 :                 ctx->want_part = 1;
     374           0 :               else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
     375           0 :                 ctx->err = 0;
     376           0 :               else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
     377             :                 {
     378           0 :                   ctx->want_part = ctx->decode_part = 1;
     379           0 :                   ctx->err = 0;
     380             :                 }
     381             :             }
     382             :         }
     383             : 
     384             :       /* Figure out the encoding if needed.  */
     385           0 :       if (ctx->decode_part)
     386             :         {
     387             :           char *value;
     388             :           size_t valueoff;
     389             : 
     390           0 :           ctx->decode_part = 0; /* Fallback for unknown encoding.  */
     391           0 :           value = rfc822parse_get_field (msg, "Content-Transfer-Encoding", -1,
     392             :                                          &valueoff);
     393           0 :           if (value)
     394             :             {
     395           0 :               if (!stricmp (value+valueoff, "quoted-printable"))
     396           0 :                 ctx->decode_part = 1;
     397           0 :               else if (!stricmp (value+valueoff, "base64"))
     398             :                 {
     399           0 :                   ctx->decode_part = 2;
     400           0 :                   if (ctx->b64state)
     401           0 :                     b64dec_finish (ctx->b64state); /* Reuse state.  */
     402             :                   else
     403             :                     {
     404           0 :                       ctx->b64state = xtrymalloc (sizeof *ctx->b64state);
     405           0 :                       if (!ctx->b64state)
     406           0 :                         rc = gpg_error_from_syserror ();
     407             :                     }
     408           0 :                   if (!rc)
     409           0 :                     rc = b64dec_start (ctx->b64state, NULL);
     410             :                 }
     411           0 :               free (value); /* Right, we need a plain free.  */
     412             :             }
     413             :         }
     414             : 
     415           0 :       ctx->show.header = 0;
     416           0 :       ctx->show.data = 1;
     417           0 :       ctx->show.n_skip = 1;
     418             :     }
     419           0 :   else if (event == RFC822PARSE_PREAMBLE)
     420           0 :     ctx->show.as_note = 1;
     421           0 :   else if (event == RFC822PARSE_LEVEL_DOWN)
     422             :     {
     423           0 :       if (ctx->debug)
     424           0 :         log_debug ("b down\n");
     425           0 :       ctx->nesting_level++;
     426             :     }
     427           0 :   else if (event == RFC822PARSE_LEVEL_UP)
     428             :     {
     429           0 :       if (ctx->debug)
     430           0 :         log_debug ("b up\n");
     431           0 :       if (ctx->nesting_level)
     432           0 :         ctx->nesting_level--;
     433             :       else
     434           0 :         log_error ("invalid structure (bad nesting level)\n");
     435             :     }
     436           0 :   else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
     437             :     {
     438           0 :       ctx->show.data = 0;
     439           0 :       ctx->show.boundary = 1;
     440           0 :       if (event == RFC822PARSE_BOUNDARY)
     441             :         {
     442           0 :           ctx->show.header = 1;
     443           0 :           ctx->show.n_skip = 1;
     444           0 :           if (ctx->debug)
     445           0 :             log_debug ("b part\n");
     446             :         }
     447           0 :       else if (ctx->debug)
     448           0 :         log_debug ("b last\n");
     449             : 
     450           0 :       if (ctx->pgpmime == PGPMIME_IN_ENCDATA)
     451             :         {
     452           0 :           if (ctx->debug)
     453           0 :             log_debug ("c end_encdata\n");
     454           0 :           ctx->pgpmime = PGPMIME_GOT_ENCDATA;
     455             :           /* FIXME: We should assert (event == LAST_BOUNDARY).  */
     456             :         }
     457           0 :       else if (ctx->pgpmime == PGPMIME_IN_SIGNEDDATA
     458           0 :                && ctx->nesting_level == ctx->hashing_at_level)
     459             :         {
     460           0 :           if (ctx->debug)
     461           0 :             log_debug ("c end_hash\n");
     462           0 :           ctx->pgpmime = PGPMIME_WAIT_SIGNATURE;
     463           0 :           if (ctx->collect_signeddata)
     464           0 :             ctx->err = ctx->collect_signeddata (ctx->cookie, NULL);
     465             :         }
     466           0 :       else if (ctx->pgpmime == PGPMIME_IN_SIGNATURE)
     467             :         {
     468           0 :           if (ctx->debug)
     469           0 :             log_debug ("c end_signature\n");
     470           0 :           ctx->pgpmime = PGPMIME_GOT_SIGNATURE;
     471             :           /* FIXME: We should assert (event == LAST_BOUNDARY).  */
     472             :         }
     473           0 :       else if (ctx->want_part)
     474             :         {
     475           0 :           if (ctx->part_data)
     476             :             {
     477             :               /* FIXME: We may need to flush things.  */
     478           0 :               ctx->err = ctx->part_data (ctx->cookie, NULL, 0);
     479             :             }
     480           0 :           ctx->want_part = 0;
     481             :         }
     482             :     }
     483             : 
     484           0 :   ctx->msg = NULL;
     485             : 
     486           0 :   return rc;
     487             : }
     488             : 
     489             : 
     490             : /* Create a new mime parser object.  COOKIE is a values which will be
     491             :  * used as first argument for all callbacks registered with this
     492             :  * parser object.  */
     493             : gpg_error_t
     494           0 : mime_parser_new (mime_parser_t *r_parser, void *cookie)
     495             : {
     496             :   mime_parser_t ctx;
     497             : 
     498           0 :   *r_parser = NULL;
     499             : 
     500           0 :   ctx = xtrycalloc (1, sizeof *ctx);
     501           0 :   if (!ctx)
     502           0 :     return gpg_error_from_syserror ();
     503           0 :   ctx->cookie = cookie;
     504             : 
     505           0 :   *r_parser = ctx;
     506           0 :   return 0;
     507             : }
     508             : 
     509             : 
     510             : /* Release a mime parser object.  */
     511             : void
     512           0 : mime_parser_release (mime_parser_t ctx)
     513             : {
     514           0 :   if (!ctx)
     515           0 :     return;
     516             : 
     517           0 :   if (ctx->b64state)
     518             :     {
     519           0 :       b64dec_finish (ctx->b64state);
     520           0 :       xfree (ctx->b64state);
     521             :     }
     522           0 :   xfree (ctx);
     523             : }
     524             : 
     525             : 
     526             : /* Set verbose and debug mode.  */
     527             : void
     528           0 : mime_parser_set_verbose (mime_parser_t ctx, int level)
     529             : {
     530           0 :   if (!level)
     531             :     {
     532           0 :       ctx->verbose = 0;
     533           0 :       ctx->debug = 0;
     534             :     }
     535             :   else
     536             :     {
     537           0 :       ctx->verbose = 1;
     538           0 :       if (level > 10)
     539           0 :         ctx->debug = 1;
     540             :     }
     541           0 : }
     542             : 
     543             : 
     544             : /* Set the callback used to announce a new part.  It will be called
     545             :  * with the media type and media subtype of the part.  If no
     546             :  * Content-type header was given both values are the empty string.
     547             :  * The callback should return 0 on success or an error code.  The
     548             :  * error code GPG_ERR_FALSE indicates that the caller is not
     549             :  * interested in the part and data shall not be returned via a
     550             :  * registered part_data callback.  The error code GPG_ERR_TRUE
     551             :  * indicates that the parts shall be redurned in decoded format
     552             :  * (i.e. base64 or QP encoding is removed).  */
     553             : void
     554           0 : mime_parser_set_new_part (mime_parser_t ctx,
     555             :                           gpg_error_t (*fnc) (void *cookie,
     556             :                                               const char *mediatype,
     557             :                                               const char *mediasubtype))
     558             : {
     559           0 :   ctx->new_part = fnc;
     560           0 : }
     561             : 
     562             : 
     563             : /* Set the callback used to return the data of a part to the caller.
     564             :  * The end of the part is indicated by passing NUL for DATA.  */
     565             : void
     566           0 : mime_parser_set_part_data (mime_parser_t ctx,
     567             :                            gpg_error_t (*fnc) (void *cookie,
     568             :                                                const void *data,
     569             :                                                size_t datalen))
     570             : {
     571           0 :   ctx->part_data = fnc;
     572           0 : }
     573             : 
     574             : 
     575             : /* Set the callback to collect encrypted data.  A NULL passed to the
     576             :  * callback indicates the end of the encrypted data; the callback may
     577             :  * then decrypt the collected data.  */
     578             : void
     579           0 : mime_parser_set_collect_encrypted (mime_parser_t ctx,
     580             :                                    gpg_error_t (*fnc) (void *cookie,
     581             :                                                        const char *data))
     582             : {
     583           0 :   ctx->collect_encrypted = fnc;
     584           0 : }
     585             : 
     586             : 
     587             : /* Set the callback to collect signed data.  A NULL passed to the
     588             :  * callback indicates the end of the signed data.  */
     589             : void
     590           0 : mime_parser_set_collect_signeddata (mime_parser_t ctx,
     591             :                                     gpg_error_t (*fnc) (void *cookie,
     592             :                                                         const char *data))
     593             : {
     594           0 :   ctx->collect_signeddata = fnc;
     595           0 : }
     596             : 
     597             : 
     598             : /* Set the callback to collect the signature.  A NULL passed to the
     599             :  * callback indicates the end of the signature; the callback may the
     600             :  * verify the signature.  */
     601             : void
     602           0 : mime_parser_set_collect_signature (mime_parser_t ctx,
     603             :                                    gpg_error_t (*fnc) (void *cookie,
     604             :                                                        const char *data))
     605             : {
     606           0 :   ctx->collect_signature = fnc;
     607           0 : }
     608             : 
     609             : 
     610             : /* Return the RFC888 parser context.  This is only available inside a
     611             :  * callback.  */
     612             : rfc822parse_t
     613           0 : mime_parser_rfc822parser (mime_parser_t ctx)
     614             : {
     615           0 :   return ctx->msg;
     616             : }
     617             : 
     618             : 
     619             : /* Helper for mime_parser_parse.  */
     620             : static gpg_error_t
     621           0 : process_part_data (mime_parser_t ctx, char *line, size_t *length)
     622             : {
     623             :   gpg_error_t err;
     624             :   size_t nbytes;
     625             : 
     626           0 :   if (!ctx->want_part)
     627           0 :     return 0;
     628           0 :   if (!ctx->part_data)
     629           0 :     return 0;
     630             : 
     631           0 :   if (ctx->decode_part == 1)
     632             :     {
     633           0 :       *length = qp_decode (line, *length, NULL);
     634             :     }
     635           0 :   else if (ctx->decode_part == 2)
     636             :     {
     637           0 :       log_assert (ctx->b64state);
     638           0 :       err = b64dec_proc (ctx->b64state, line, *length, &nbytes);
     639           0 :       if (err)
     640           0 :         return err;
     641           0 :       *length = nbytes;
     642             :     }
     643             : 
     644           0 :   return ctx->part_data (ctx->cookie, line, *length);
     645             : }
     646             : 
     647             : 
     648             : /* Read and parse a message from FP and call the appropriate
     649             :  * callbacks.  */
     650             : gpg_error_t
     651           0 : mime_parser_parse (mime_parser_t ctx, estream_t fp)
     652             : {
     653             :   gpg_error_t err;
     654           0 :   rfc822parse_t msg = NULL;
     655           0 :   unsigned int lineno = 0;
     656             :   size_t length;
     657             :   char *line;
     658             : 
     659           0 :   line = ctx->line;
     660             : 
     661           0 :   msg = rfc822parse_open (parse_message_cb, ctx);
     662           0 :   if (!msg)
     663             :     {
     664           0 :       err = gpg_error_from_syserror ();
     665           0 :       log_error ("can't open mail parser: %s", gpg_strerror (err));
     666           0 :       goto leave;
     667             :     }
     668             : 
     669             :   /* Fixme: We should not use fgets because it can't cope with
     670             :      embedded nul characters. */
     671           0 :   while (es_fgets (ctx->line, sizeof (ctx->line), fp))
     672             :     {
     673           0 :       lineno++;
     674           0 :       if (lineno == 1 && !strncmp (line, "From ", 5))
     675           0 :         continue;  /* We better ignore a leading From line. */
     676             : 
     677           0 :       length = strlen (line);
     678           0 :       if (length && line[length - 1] == '\n')
     679           0 :         line[--length] = 0;
     680             :       else
     681           0 :         log_error ("mail parser detected too long or"
     682             :                    " non terminated last line (lnr=%u)\n", lineno);
     683           0 :       if (length && line[length - 1] == '\r')
     684           0 :         line[--length] = 0;
     685             : 
     686           0 :       ctx->err = 0;
     687           0 :       if (rfc822parse_insert (msg, line, length))
     688             :         {
     689           0 :           err = gpg_error_from_syserror ();
     690           0 :           log_error ("mail parser failed: %s", gpg_strerror (err));
     691           0 :           goto leave;
     692             :         }
     693           0 :       if (ctx->err)
     694             :         {
     695             :           /* Error from a callback detected.  */
     696           0 :           err = ctx->err;
     697           0 :           goto leave;
     698             :         }
     699             : 
     700             : 
     701             :       /* Debug output.  Note that the boundary is shown before n_skip
     702             :        * is evaluated.  */
     703           0 :       if (ctx->show.boundary)
     704             :         {
     705           0 :           if (ctx->debug)
     706           0 :             log_debug ("# Boundary: %s\n", line);
     707           0 :           ctx->show.boundary = 0;
     708             :         }
     709           0 :       if (ctx->show.n_skip)
     710           0 :         ctx->show.n_skip--;
     711           0 :       else if (ctx->show.data)
     712             :         {
     713           0 :           if (ctx->show.as_note)
     714             :             {
     715           0 :               if (ctx->verbose)
     716           0 :                 log_debug ("# Note: %s\n", line);
     717           0 :               ctx->show.as_note = 0;
     718             :             }
     719           0 :           else if (ctx->debug)
     720           0 :             log_debug ("# Data: %s\n", line);
     721             :         }
     722           0 :       else if (ctx->show.header && ctx->verbose)
     723           0 :         log_debug ("# Header: %s\n", line);
     724             : 
     725           0 :       if (ctx->pgpmime == PGPMIME_IN_ENCVERSION)
     726             :         {
     727           0 :           trim_trailing_spaces (line);
     728           0 :           if (!*line)
     729             :             ;  /* Skip empty lines.  */
     730           0 :           else if (!strcmp (line, "Version: 1"))
     731           0 :             ctx->pgpmime = PGPMIME_WAIT_ENCDATA;
     732             :           else
     733             :             {
     734           0 :               log_error ("invalid PGP/MIME structure;"
     735             :                          " garbage in pgp-encrypted part ('%s')\n", line);
     736           0 :               ctx->pgpmime = PGPMIME_INVALID;
     737             :             }
     738             :         }
     739           0 :       else if (ctx->pgpmime == PGPMIME_IN_ENCDATA)
     740             :         {
     741           0 :           if (ctx->collect_encrypted)
     742             :             {
     743           0 :               err = ctx->collect_encrypted (ctx->cookie, line);
     744           0 :               if (!err)
     745           0 :                 err = ctx->collect_encrypted (ctx->cookie, "\r\n");
     746           0 :               if (err)
     747           0 :                 goto leave;
     748             :             }
     749             :         }
     750           0 :       else if (ctx->pgpmime == PGPMIME_GOT_ENCDATA)
     751             :         {
     752           0 :           ctx->pgpmime = PGPMIME_NONE;
     753           0 :           if (ctx->collect_encrypted)
     754           0 :             ctx->collect_encrypted (ctx->cookie, NULL);
     755             :         }
     756           0 :       else if (ctx->pgpmime == PGPMIME_IN_SIGNEDDATA)
     757             :         {
     758             :           /* If we are processing signed data, store the signed data.
     759             :            * We need to delay the hashing of the CR/LF because the
     760             :            * last line ending belongs to the next boundary.  This is
     761             :            * the reason why we can't use the PGPMIME state as a
     762             :            * condition.  */
     763           0 :           if (ctx->debug)
     764           0 :             log_debug ("# hashing %s'%s'\n",
     765           0 :                        ctx->delay_hashing? "CR,LF+":"", line);
     766           0 :           if (ctx->collect_signeddata)
     767             :             {
     768           0 :               if (ctx->delay_hashing)
     769           0 :                 ctx->collect_signeddata (ctx->cookie, "\r\n");
     770           0 :               ctx->collect_signeddata (ctx->cookie, line);
     771             :             }
     772           0 :           ctx->delay_hashing = 1;
     773             : 
     774           0 :           err = process_part_data (ctx, line, &length);
     775           0 :           if (err)
     776           0 :             goto leave;
     777             :         }
     778           0 :       else if (ctx->pgpmime == PGPMIME_IN_SIGNATURE)
     779             :         {
     780           0 :           if (ctx->collect_signeddata)
     781             :             {
     782           0 :               ctx->collect_signature (ctx->cookie, line);
     783           0 :               ctx->collect_signature (ctx->cookie, "\r\n");
     784             :             }
     785             :         }
     786           0 :       else if (ctx->pgpmime == PGPMIME_GOT_SIGNATURE)
     787             :         {
     788           0 :           ctx->pgpmime = PGPMIME_NONE;
     789           0 :           if (ctx->collect_signeddata)
     790           0 :             ctx->collect_signature (ctx->cookie, NULL);
     791             :         }
     792             :       else
     793             :         {
     794           0 :           err = process_part_data (ctx, line, &length);
     795           0 :           if (err)
     796           0 :             goto leave;
     797             :         }
     798             :     }
     799             : 
     800           0 :   rfc822parse_close (msg);
     801           0 :   msg = NULL;
     802           0 :   err = 0;
     803             : 
     804             :  leave:
     805           0 :   rfc822parse_cancel (msg);
     806           0 :   return err;
     807             : }

Generated by: LCOV version 1.11