Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34070561
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
28 KB
Subscribers
None
View Options
diff --git a/tools/mime-parser.c b/tools/mime-parser.c
index 7ac3c69cb..0ca5452c3 100644
--- a/tools/mime-parser.c
+++ b/tools/mime-parser.c
@@ -1,772 +1,789 @@
/* mime-parser.c - Parse MIME structures (high level rfc822 parser).
* Copyright (C) 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.
*
* GnuPG 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/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "rfc822parse.h"
#include "mime-parser.h"
enum pgpmime_states
{
PGPMIME_NONE = 0,
PGPMIME_WAIT_ENCVERSION,
PGPMIME_IN_ENCVERSION,
PGPMIME_WAIT_ENCDATA,
PGPMIME_IN_ENCDATA,
PGPMIME_GOT_ENCDATA,
PGPMIME_WAIT_SIGNEDDATA,
PGPMIME_IN_SIGNEDDATA,
PGPMIME_WAIT_SIGNATURE,
PGPMIME_IN_SIGNATURE,
PGPMIME_GOT_SIGNATURE,
PGPMIME_INVALID
};
/* Definition of the mime parser object. */
struct mime_parser_context_s
{
void *cookie; /* Cookie passed to all callbacks. */
/* The callback to announce a new part. */
gpg_error_t (*new_part) (void *cookie,
const char *mediatype,
const char *mediasubtype);
/* The callback to return data of a part. */
gpg_error_t (*part_data) (void *cookie,
const void *data,
size_t datalen);
/* The callback to collect encrypted data. */
gpg_error_t (*collect_encrypted) (void *cookie, const char *data);
/* The callback to collect signed data. */
gpg_error_t (*collect_signeddata) (void *cookie, const char *data);
/* The callback to collect a signature. */
gpg_error_t (*collect_signature) (void *cookie, const char *data);
+ /* The RFC822 parser context is stored here during callbacks. */
+ rfc822parse_t msg;
+
/* Helper to convey error codes from user callbacks. */
gpg_error_t err;
int nesting_level; /* The current nesting level. */
int hashing_at_level; /* The nesting level at which we are hashing. */
enum pgpmime_states pgpmime; /* Current PGP/MIME state. */
unsigned int delay_hashing:1;/* Helper for PGPMIME_IN_SIGNEDDATA. */
unsigned int want_part:1; /* Return the current part. */
unsigned int decode_part:2; /* Decode the part. 1 = QP, 2 = Base64. */
unsigned int verbose:1; /* Enable verbose mode. */
unsigned int debug:1; /* Enable debug mode. */
/* Flags to help with debug output. */
struct {
unsigned int n_skip; /* Skip showing these number of lines. */
unsigned int header:1; /* Show the header lines. */
unsigned int data:1; /* Show the data lines. */
unsigned int as_note:1; /* Show the next data line as a note. */
unsigned int boundary : 1;
} show;
struct b64state *b64state; /* NULL or malloced Base64 decoder state. */
/* A buffer for reading a mail line, */
char line[5000];
};
/* Print the event received by the parser for debugging. */
static void
show_message_parser_event (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;
}
log_debug ("*** RFC822 event %s\n", s);
}
/* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
Returns the new length of the buffer and stores true at R_SLBRK if
the line ended with a soft line break; false is stored if not.
This function asssumes that a complete line is passed in
buffer. */
static size_t
qp_decode (char *buffer, size_t length, int *r_slbrk)
{
char *d, *s;
if (r_slbrk)
*r_slbrk = 0;
/* Fixme: We should remove trailing white space first. */
for (s=d=buffer; length; length--)
{
if (*s == '=')
{
if (length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
{
s++;
*(unsigned char*)d++ = xtoi_2 (s);
s += 2;
length -= 2;
}
else if (length > 2 && s[1] == '\r' && s[2] == '\n')
{
/* Soft line break. */
s += 3;
length -= 2;
if (r_slbrk && length == 1)
*r_slbrk = 1;
}
else if (length > 1 && s[1] == '\n')
{
/* Soft line break with only a Unix line terminator. */
s += 2;
length -= 1;
if (r_slbrk && length == 1)
*r_slbrk = 1;
}
else if (length == 1)
{
/* Soft line break at the end of the line. */
s += 1;
if (r_slbrk)
*r_slbrk = 1;
}
else
*d++ = *s++;
}
else
*d++ = *s++;
}
return d - buffer;
}
/* This function is called by parse_mail to communicate events. This
* callback communicates with the caller using a structure passed in
* OPAQUE. Should return 0 on success or set ERRNO and return -1. */
static int
parse_message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
{
mime_parser_t ctx = opaque;
const char *s;
int rc = 0;
+ /* Make the RFC822 parser context availabale for callbacks. */
+ ctx->msg = msg;
+
if (ctx->debug)
show_message_parser_event (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->pgpmime == PGPMIME_WAIT_SIGNEDDATA)
{
if (ctx->debug)
log_debug ("begin_hash\n");
ctx->hashing_at_level = ctx->nesting_level;
ctx->pgpmime = PGPMIME_IN_SIGNEDDATA;
ctx->delay_hashing = 0;
}
}
if (event == RFC822PARSE_OPEN)
{
/* Initialize for a new message. */
ctx->show.header = 1;
}
else if (event == RFC822PARSE_T2BODY)
{
rfc822parse_field_t field;
ctx->want_part = 0;
ctx->decode_part = 0;
field = rfc822parse_parse_field (msg, "Content-Type", -1);
if (field)
{
const char *s1, *s2;
s1 = rfc822parse_query_media_type (field, &s2);
if (s1)
{
if (ctx->verbose)
log_debug ("h media: %*s%s %s\n",
ctx->nesting_level*2, "", s1, s2);
if (ctx->pgpmime == PGPMIME_WAIT_ENCVERSION)
{
if (!strcmp (s1, "application")
&& !strcmp (s2, "pgp-encrypted"))
{
if (ctx->debug)
log_debug ("c begin_encversion\n");
ctx->pgpmime = PGPMIME_IN_ENCVERSION;
}
else
{
log_error ("invalid PGP/MIME structure;"
" expected '%s', got '%s/%s'\n",
"application/pgp-encrypted", s1, s2);
ctx->pgpmime = PGPMIME_INVALID;
}
}
else if (ctx->pgpmime == PGPMIME_WAIT_ENCDATA)
{
if (!strcmp (s1, "application")
&& !strcmp (s2, "octet-stream"))
{
if (ctx->debug)
log_debug ("c begin_encdata\n");
ctx->pgpmime = PGPMIME_IN_ENCDATA;
}
else
{
log_error ("invalid PGP/MIME structure;"
" expected '%s', got '%s/%s'\n",
"application/octet-stream", s1, s2);
ctx->pgpmime = PGPMIME_INVALID;
}
}
else if (ctx->pgpmime == PGPMIME_WAIT_SIGNATURE)
{
if (!strcmp (s1, "application")
&& !strcmp (s2, "pgp-signature"))
{
if (ctx->debug)
log_debug ("c begin_signature\n");
ctx->pgpmime = PGPMIME_IN_SIGNATURE;
}
else
{
log_error ("invalid PGP/MIME structure;"
" expected '%s', got '%s/%s'\n",
"application/pgp-signature", s1, s2);
ctx->pgpmime = PGPMIME_INVALID;
}
}
else if (!strcmp (s1, "multipart")
&& !strcmp (s2, "encrypted"))
{
s = rfc822parse_query_parameter (field, "protocol", 0);
if (s)
{
if (ctx->debug)
log_debug ("h encrypted.protocol: %s\n", s);
if (!strcmp (s, "application/pgp-encrypted"))
{
if (ctx->pgpmime)
log_error ("note: "
"ignoring nested PGP/MIME signature\n");
else
ctx->pgpmime = PGPMIME_WAIT_ENCVERSION;
}
else if (ctx->verbose)
log_debug ("# this protocol is not supported\n");
}
}
else if (!strcmp (s1, "multipart")
&& !strcmp (s2, "signed"))
{
s = rfc822parse_query_parameter (field, "protocol", 1);
if (s)
{
if (ctx->debug)
log_debug ("h signed.protocol: %s\n", s);
if (!strcmp (s, "application/pgp-signature"))
{
if (ctx->pgpmime)
log_error ("note: "
"ignoring nested PGP/MIME signature\n");
else
ctx->pgpmime = PGPMIME_WAIT_SIGNEDDATA;
}
else if (ctx->verbose)
log_debug ("# this protocol is not supported\n");
}
}
else if (ctx->new_part)
{
ctx->err = ctx->new_part (ctx->cookie, s1, s2);
if (!ctx->err)
ctx->want_part = 1;
else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
ctx->err = 0;
else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
{
ctx->want_part = ctx->decode_part = 1;
ctx->err = 0;
}
}
}
else
{
if (ctx->debug)
log_debug ("h media: %*s none\n", ctx->nesting_level*2, "");
if (ctx->new_part)
{
ctx->err = ctx->new_part (ctx->cookie, "", "");
if (!ctx->err)
ctx->want_part = 1;
else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
ctx->err = 0;
else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
{
ctx->want_part = ctx->decode_part = 1;
ctx->err = 0;
}
}
}
rfc822parse_release_field (field);
}
else
{
if (ctx->verbose)
log_debug ("h media: %*stext plain [assumed]\n",
ctx->nesting_level*2, "");
if (ctx->new_part)
{
ctx->err = ctx->new_part (ctx->cookie, "text", "plain");
if (!ctx->err)
ctx->want_part = 1;
else if (gpg_err_code (ctx->err) == GPG_ERR_FALSE)
ctx->err = 0;
else if (gpg_err_code (ctx->err) == GPG_ERR_TRUE)
{
ctx->want_part = ctx->decode_part = 1;
ctx->err = 0;
}
}
}
/* Figure out the encoding if needed. */
if (ctx->decode_part)
{
char *value;
size_t valueoff;
ctx->decode_part = 0; /* Fallback for unknown encoding. */
value = rfc822parse_get_field (msg, "Content-Transfer-Encoding", -1,
&valueoff);
if (value)
{
if (!stricmp (value+valueoff, "quoted-printable"))
ctx->decode_part = 1;
else if (!stricmp (value+valueoff, "base64"))
{
ctx->decode_part = 2;
if (ctx->b64state)
b64dec_finish (ctx->b64state); /* Reuse state. */
else
{
ctx->b64state = xtrymalloc (sizeof *ctx->b64state);
if (!ctx->b64state)
rc = gpg_error_from_syserror ();
}
if (!rc)
rc = b64dec_start (ctx->b64state, NULL);
}
free (value); /* Right, we need a plain free. */
}
}
ctx->show.header = 0;
ctx->show.data = 1;
ctx->show.n_skip = 1;
}
else if (event == RFC822PARSE_PREAMBLE)
ctx->show.as_note = 1;
else if (event == RFC822PARSE_LEVEL_DOWN)
{
if (ctx->debug)
log_debug ("b down\n");
ctx->nesting_level++;
}
else if (event == RFC822PARSE_LEVEL_UP)
{
if (ctx->debug)
log_debug ("b up\n");
if (ctx->nesting_level)
ctx->nesting_level--;
else
log_error ("invalid structure (bad nesting level)\n");
}
else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
{
ctx->show.data = 0;
ctx->show.boundary = 1;
if (event == RFC822PARSE_BOUNDARY)
{
ctx->show.header = 1;
ctx->show.n_skip = 1;
if (ctx->debug)
log_debug ("b part\n");
}
else if (ctx->debug)
log_debug ("b last\n");
if (ctx->pgpmime == PGPMIME_IN_ENCDATA)
{
if (ctx->debug)
log_debug ("c end_encdata\n");
ctx->pgpmime = PGPMIME_GOT_ENCDATA;
/* FIXME: We should assert (event == LAST_BOUNDARY). */
}
else if (ctx->pgpmime == PGPMIME_IN_SIGNEDDATA
&& ctx->nesting_level == ctx->hashing_at_level)
{
if (ctx->debug)
log_debug ("c end_hash\n");
ctx->pgpmime = PGPMIME_WAIT_SIGNATURE;
if (ctx->collect_signeddata)
ctx->err = ctx->collect_signeddata (ctx->cookie, NULL);
}
else if (ctx->pgpmime == PGPMIME_IN_SIGNATURE)
{
if (ctx->debug)
log_debug ("c end_signature\n");
ctx->pgpmime = PGPMIME_GOT_SIGNATURE;
/* FIXME: We should assert (event == LAST_BOUNDARY). */
}
else if (ctx->want_part)
{
if (ctx->part_data)
{
/* FIXME: We may need to flush things. */
ctx->err = ctx->part_data (ctx->cookie, NULL, 0);
}
ctx->want_part = 0;
}
}
+ ctx->msg = NULL;
+
return rc;
}
/* Create a new mime parser object. COOKIE is a values which will be
* used as first argument for all callbacks registered with this
* parser object. */
gpg_error_t
mime_parser_new (mime_parser_t *r_parser, void *cookie)
{
mime_parser_t ctx;
*r_parser = NULL;
ctx = xtrycalloc (1, sizeof *ctx);
if (!ctx)
return gpg_error_from_syserror ();
ctx->cookie = cookie;
*r_parser = ctx;
return 0;
}
/* Release a mime parser object. */
void
mime_parser_release (mime_parser_t ctx)
{
if (!ctx)
return;
if (ctx->b64state)
{
b64dec_finish (ctx->b64state);
xfree (ctx->b64state);
}
xfree (ctx);
}
/* Set verbose and debug mode. */
void
mime_parser_set_verbose (mime_parser_t ctx, int level)
{
if (!level)
{
ctx->verbose = 0;
ctx->debug = 0;
}
else
{
ctx->verbose = 1;
if (level > 10)
ctx->debug = 1;
}
}
/* Set the callback used to announce a new part. It will be called
* with the media type and media subtype of the part. If no
* Content-type header was given both values are the empty string.
* The callback should return 0 on success or an error code. The
* error code GPG_ERR_FALSE indicates that the caller is not
* interested in the part and data shall not be returned via a
* registered part_data callback. The error code GPG_ERR_TRUE
* indicates that the parts shall be redurned in decoded format
* (i.e. base64 or QP encoding is removed). */
void
mime_parser_set_new_part (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *mediatype,
const char *mediasubtype))
{
ctx->new_part = fnc;
}
/* Set the callback used to return the data of a part to the caller.
* The end of the part is indicated by passing NUL for DATA. */
void
mime_parser_set_part_data (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const void *data,
size_t datalen))
{
ctx->part_data = fnc;
}
/* Set the callback to collect encrypted data. A NULL passed to the
* callback indicates the end of the encrypted data; the callback may
* then decrypt the collected data. */
void
mime_parser_set_collect_encrypted (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *data))
{
ctx->collect_encrypted = fnc;
}
/* Set the callback to collect signed data. A NULL passed to the
* callback indicates the end of the signed data. */
void
mime_parser_set_collect_signeddata (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *data))
{
ctx->collect_signeddata = fnc;
}
/* Set the callback to collect the signature. A NULL passed to the
* callback indicates the end of the signature; the callback may the
* verify the signature. */
void
mime_parser_set_collect_signature (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *data))
{
ctx->collect_signature = fnc;
}
+/* Return the RFC888 parser context. This is only available inside a
+ * callback. */
+rfc822parse_t
+mime_parser_rfc822parser (mime_parser_t ctx)
+{
+ return ctx->msg;
+}
+
+
/* Read and parse a message from FP and call the appropriate
* callbacks. */
gpg_error_t
mime_parser_parse (mime_parser_t ctx, estream_t fp)
{
gpg_error_t err;
rfc822parse_t msg = NULL;
unsigned int lineno = 0;
size_t length, nbytes;
char *line;
line = ctx->line;
msg = rfc822parse_open (parse_message_cb, ctx);
if (!msg)
{
err = gpg_error_from_syserror ();
log_error ("can't open mail parser: %s", gpg_strerror (err));
goto leave;
}
/* Fixme: We should not use fgets because it can't cope with
embedded nul characters. */
while (es_fgets (ctx->line, sizeof (ctx->line), fp))
{
lineno++;
if (lineno == 1 && !strncmp (line, "From ", 5))
continue; /* We better ignore a leading From line. */
length = strlen (line);
if (length && line[length - 1] == '\n')
line[--length] = 0;
else
log_error ("mail parser detected too long or"
" non terminated last line (lnr=%u)\n", lineno);
if (length && line[length - 1] == '\r')
line[--length] = 0;
ctx->err = 0;
if (rfc822parse_insert (msg, line, length))
{
err = gpg_error_from_syserror ();
log_error ("mail parser failed: %s", gpg_strerror (err));
goto leave;
}
if (ctx->err)
{
/* Error from a callback detected. */
err = ctx->err;
goto leave;
}
/* Debug output. Note that the boundary is shown before n_skip
* is evaluated. */
if (ctx->show.boundary)
{
if (ctx->debug)
log_debug ("# Boundary: %s\n", line);
ctx->show.boundary = 0;
}
if (ctx->show.n_skip)
ctx->show.n_skip--;
else if (ctx->show.data)
{
if (ctx->show.as_note)
{
if (ctx->verbose)
log_debug ("# Note: %s\n", line);
ctx->show.as_note = 0;
}
else if (ctx->debug)
log_debug ("# Data: %s\n", line);
}
else if (ctx->show.header && ctx->verbose)
log_debug ("# Header: %s\n", line);
if (ctx->pgpmime == PGPMIME_IN_ENCVERSION)
{
trim_trailing_spaces (line);
if (!*line)
; /* Skip empty lines. */
else if (!strcmp (line, "Version: 1"))
ctx->pgpmime = PGPMIME_WAIT_ENCDATA;
else
{
log_error ("invalid PGP/MIME structure;"
" garbage in pgp-encrypted part ('%s')\n", line);
ctx->pgpmime = PGPMIME_INVALID;
}
}
else if (ctx->pgpmime == PGPMIME_IN_ENCDATA)
{
if (ctx->collect_encrypted)
{
err = ctx->collect_encrypted (ctx->cookie, line);
if (!err)
err = ctx->collect_encrypted (ctx->cookie, "\r\n");
if (err)
goto leave;
}
}
else if (ctx->pgpmime == PGPMIME_GOT_ENCDATA)
{
ctx->pgpmime = PGPMIME_NONE;
if (ctx->collect_encrypted)
ctx->collect_encrypted (ctx->cookie, NULL);
}
else if (ctx->pgpmime == PGPMIME_IN_SIGNEDDATA)
{
/* If we are processing signed data, store the signed data.
* We need to delay the hashing of the CR/LF because the
* last line ending belongs to the next boundary. This is
* the reason why we can't use the PGPMIME state as a
* condition. */
if (ctx->debug)
log_debug ("# hashing %s'%s'\n",
ctx->delay_hashing? "CR,LF+":"", line);
if (ctx->collect_signeddata)
{
if (ctx->delay_hashing)
ctx->collect_signeddata (ctx->cookie, "\r\n");
ctx->collect_signeddata (ctx->cookie, line);
}
ctx->delay_hashing = 1;
}
else if (ctx->pgpmime == PGPMIME_IN_SIGNATURE)
{
if (ctx->collect_signeddata)
{
ctx->collect_signature (ctx->cookie, line);
ctx->collect_signature (ctx->cookie, "\r\n");
}
}
else if (ctx->pgpmime == PGPMIME_GOT_SIGNATURE)
{
ctx->pgpmime = PGPMIME_NONE;
if (ctx->collect_signeddata)
ctx->collect_signature (ctx->cookie, NULL);
}
else if (ctx->want_part)
{
if (ctx->part_data)
{
if (ctx->decode_part == 1)
{
length = qp_decode (line, length, NULL);
}
else if (ctx->decode_part == 2)
{
log_assert (ctx->b64state);
err = b64dec_proc (ctx->b64state, line, length, &nbytes);
if (err)
goto leave;
length = nbytes;
}
err = ctx->part_data (ctx->cookie, line, length);
if (err)
goto leave;
}
}
}
rfc822parse_close (msg);
msg = NULL;
err = 0;
leave:
rfc822parse_cancel (msg);
return err;
}
diff --git a/tools/mime-parser.h b/tools/mime-parser.h
index ab0d79288..b217a2c8f 100644
--- a/tools/mime-parser.h
+++ b/tools/mime-parser.h
@@ -1,52 +1,59 @@
/* mime-parser.h - Parse MIME structures (high level rfc822 parser).
* Copyright (C) 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.
*
* GnuPG 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/>.
*/
#ifndef GNUPG_MIME_PARSER_H
#define GNUPG_MIME_PARSER_H
struct mime_parser_context_s;
typedef struct mime_parser_context_s *mime_parser_t;
gpg_error_t mime_parser_new (mime_parser_t *r_ctx, void *cookie);
void mime_parser_release (mime_parser_t ctx);
void mime_parser_set_verbose (mime_parser_t ctx, int level);
void mime_parser_set_new_part (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *mediatype,
const char *mediasubtype));
void mime_parser_set_part_data (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const void *data,
size_t datalen));
void mime_parser_set_collect_encrypted (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *data));
void mime_parser_set_collect_signeddata (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *data));
void mime_parser_set_collect_signature (mime_parser_t ctx,
gpg_error_t (*fnc) (void *cookie,
const char *data));
gpg_error_t mime_parser_parse (mime_parser_t ctx, estream_t fp);
+/* Duplicated declaration of the RFC822 parser context. */
+struct rfc822parse_context;
+typedef struct rfc822parse_context *rfc822parse_t;
+
+rfc822parse_t mime_parser_rfc822parser (mime_parser_t ctx);
+
+
#endif /*GNUPG_MIME_PARSER_H*/
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 1, 10:26 PM (1 d, 18 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
12/7f/98607d89c093268fc3d317654286
Attached To
rG GnuPG
Event Timeline
Log In to Comment