diff --git a/NEWS b/NEWS
index ae80642f..20a80e8d 100644
--- a/NEWS
+++ b/NEWS
@@ -7,13 +7,16 @@ Noteworthy changes in version 1.11.2 (unreleased)
* Added context flag "auto-key-locate" to control the
behavior of GPGME_KEYLIST_MODE_LOCATE.
+ * New data function to create a data object from an estream.
+
* Interface changes relative to the 1.11.1 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ gpgme_data_new_from_estream NEW.
gpgme_decrypt_result_t EXTENDED: New field legacy_cipher_nomdc.
gpgme_set_ctx_flag EXTENDED: New flag 'ignore-mdc-error'.
GPGME_AUDITLOG_DEFAULT NEW.
GPGME_AUDITLOG_DIAG NEW.
- gpgme_set_ctx_flag EXTENDED: New flag 'auto-key-locate'.
+ gpgme_set_ctx_flag EXTENDED: New flag 'auto-key-locate'.
cpp: DecryptionResult::sessionKey NEW.
cpp: DecryptionResult::symkeyAlgo NEW.
cpp: DecryptionResult::isLegacyCipherNoMDC New.
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index 38d34809..aff72405 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -1909,6 +1909,25 @@ data object was successfully created, and @code{GPG_ERR_ENOMEM} if not
enough memory is available.
@end deftypefun
+@deftypefun gpgme_error_t gpgme_data_new_from_estream (@w{gpgme_data_t *@var{dh}}, @w{gpgrt_stream_t @var{stream}})
+The function @code{gpgme_data_new_from_estream} creates a new
+@code{gpgme_data_t} object and uses the gpgrt stream @var{stream} to read
+from (if used as an input data object) and write to (if used as an
+output data object).
+
+When using the data object as an input buffer, the function might read
+a bit more from the stream than is actually needed by the crypto
+engine in the desired operation because of internal buffering.
+
+Note that GPGME assumes that the stream is in blocking mode. Errors
+during I/O operations, except for EINTR, are usually fatal for crypto
+operations.
+
+The function returns the error code @code{GPG_ERR_NO_ERROR} if the
+data object was successfully created, and @code{GPG_ERR_ENOMEM} if not
+enough memory is available.
+@end deftypefun
+
@node Callback Based Data Buffers
@subsection Callback Based Data Buffers
diff --git a/src/Makefile.am b/src/Makefile.am
index 0a196e0c..1394c028 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -70,6 +70,7 @@ main_sources = \
parsetlv.c parsetlv.h \
mbox-util.c mbox-util.h \
data.h data.c data-fd.c data-stream.c data-mem.c data-user.c \
+ data-estream.c \
data-compat.c data-identify.c \
signers.c sig-notation.c \
wait.c wait-global.c wait-private.c wait-user.c wait.h \
diff --git a/src/data-estream.c b/src/data-estream.c
new file mode 100644
index 00000000..34f88a7f
--- /dev/null
+++ b/src/data-estream.c
@@ -0,0 +1,99 @@
+/* data-stream.c - A stream based data object.
+ * Copyright (C) 2002, 2004, 2018 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME 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.
+ *
+ * GPGME 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, see .
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#if HAVE_CONFIG_H
+#include
+#endif
+
+#include
+#ifdef HAVE_SYS_TYPES_H
+# include
+#endif
+
+#include "debug.h"
+#include "data.h"
+
+
+static gpgme_ssize_t
+stream_es_read (gpgme_data_t dh, void *buffer, size_t size)
+{
+ size_t amt = gpgrt_fread (buffer, 1, size, dh->data.e_stream);
+ if (amt > 0)
+ return amt;
+ return gpgrt_ferror (dh->data.e_stream) ? -1 : 0;
+}
+
+
+static gpgme_ssize_t
+stream_es_write (gpgme_data_t dh, const void *buffer, size_t size)
+{
+ size_t amt = gpgrt_fwrite (buffer, 1, size, dh->data.e_stream);
+ if (amt > 0)
+ return amt;
+ return gpgrt_ferror (dh->data.e_stream) ? -1 : 0;
+}
+
+
+static gpgme_off_t
+stream_es_seek (gpgme_data_t dh, gpgme_off_t offset, int whence)
+{
+ int err;
+
+ err = gpgrt_fseeko (dh->data.e_stream, offset, whence);
+ if (err)
+ return -1;
+
+ return gpgrt_ftello (dh->data.e_stream);
+}
+
+
+static int
+stream_es_get_fd (gpgme_data_t dh)
+{
+ gpgrt_fflush (dh->data.e_stream);
+ return gpgrt_fileno (dh->data.e_stream);
+}
+
+
+static struct _gpgme_data_cbs stream_es_cbs =
+ {
+ stream_es_read,
+ stream_es_write,
+ stream_es_seek,
+ NULL,
+ stream_es_get_fd
+ };
+
+
+
+gpgme_error_t
+gpgme_data_new_from_estream (gpgme_data_t *r_dh, gpgrt_stream_t stream)
+{
+ gpgme_error_t err;
+ TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_estream", r_dh, "estream=%p",
+ stream);
+
+ err = _gpgme_data_new (r_dh, &stream_es_cbs);
+ if (err)
+ return TRACE_ERR (err);
+
+ (*r_dh)->data.e_stream = stream;
+ return TRACE_SUC1 ("dh=%p", *r_dh);
+}
diff --git a/src/data.h b/src/data.h
index 0a15b613..f12508b9 100644
--- a/src/data.h
+++ b/src/data.h
@@ -100,6 +100,9 @@ struct gpgme_data
/* For gpgme_data_new_from_stream. */
FILE *stream;
+ /* For gpgme_data_new_from_estream. */
+ gpgrt_stream_t e_stream;
+
/* For gpgme_data_new_from_cbs. */
struct
{
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index 421199a9..35968017 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -1180,6 +1180,8 @@ gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *dh,
gpgme_error_t gpgme_data_new_from_fd (gpgme_data_t *dh, int fd);
gpgme_error_t gpgme_data_new_from_stream (gpgme_data_t *dh, FILE *stream);
+gpgme_error_t gpgme_data_new_from_estream (gpgme_data_t *r_dh,
+ gpgrt_stream_t stream);
/* Return the encoding attribute of the data buffer DH */
gpgme_data_encoding_t gpgme_data_get_encoding (gpgme_data_t dh);