cpp: Add ostream operators for import result
[gpgme.git] / src / data-mem.c
1 /* data-mem.c - A memory based data object.
2  * Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <https://gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1-or-later
19  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <errno.h>
26 #include <stdlib.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <assert.h>
31 #include <string.h>
32
33 #include "data.h"
34 #include "util.h"
35 #include "debug.h"
36
37 \f
38 static gpgme_ssize_t
39 mem_read (gpgme_data_t dh, void *buffer, size_t size)
40 {
41   size_t amt = dh->data.mem.length - dh->data.mem.offset;
42   const char *src;
43
44   if (!amt)
45     return 0;
46
47   if (size < amt)
48     amt = size;
49
50   src = dh->data.mem.buffer ? dh->data.mem.buffer : dh->data.mem.orig_buffer;
51   memcpy (buffer, src + dh->data.mem.offset, amt);
52   dh->data.mem.offset += amt;
53   return amt;
54 }
55
56
57 static gpgme_ssize_t
58 mem_write (gpgme_data_t dh, const void *buffer, size_t size)
59 {
60   size_t unused;
61
62   if (!dh->data.mem.buffer && dh->data.mem.orig_buffer)
63     {
64       size_t new_size = dh->data.mem.size;
65       char *new_buffer;
66
67       if (new_size < dh->data.mem.offset + size)
68         new_size = dh->data.mem.offset + size;
69
70       new_buffer = malloc (new_size);
71       if (!new_buffer)
72         return -1;
73       memcpy (new_buffer, dh->data.mem.orig_buffer, dh->data.mem.length);
74
75       dh->data.mem.buffer = new_buffer;
76       dh->data.mem.size = new_size;
77     }
78
79   unused = dh->data.mem.size - dh->data.mem.offset;
80   if (unused < size)
81     {
82       /* Allocate a large enough buffer with exponential backoff.  */
83 #define INITIAL_ALLOC 512
84       size_t new_size = dh->data.mem.size
85         ? (2 * dh->data.mem.size) : INITIAL_ALLOC;
86       char *new_buffer;
87
88       if (new_size < dh->data.mem.offset + size)
89         new_size = dh->data.mem.offset + size;
90
91       new_buffer = realloc (dh->data.mem.buffer, new_size);
92       if (!new_buffer && new_size > dh->data.mem.offset + size)
93         {
94           /* Maybe we were too greedy, try again.  */
95           new_size = dh->data.mem.offset + size;
96           new_buffer = realloc (dh->data.mem.buffer, new_size);
97         }
98       if (!new_buffer)
99         return -1;
100       dh->data.mem.buffer = new_buffer;
101       dh->data.mem.size = new_size;
102     }
103
104   memcpy (dh->data.mem.buffer + dh->data.mem.offset, buffer, size);
105   dh->data.mem.offset += size;
106   if (dh->data.mem.length < dh->data.mem.offset)
107     dh->data.mem.length = dh->data.mem.offset;
108   return size;
109 }
110
111
112 static gpgme_off_t
113 mem_seek (gpgme_data_t dh, gpgme_off_t offset, int whence)
114 {
115   switch (whence)
116     {
117     case SEEK_SET:
118       if (offset < 0 || offset > dh->data.mem.length)
119         {
120           gpg_err_set_errno (EINVAL);
121           return -1;
122         }
123       dh->data.mem.offset = offset;
124       break;
125     case SEEK_CUR:
126       if ((offset > 0 && dh->data.mem.length - dh->data.mem.offset < offset)
127           || (offset < 0 && dh->data.mem.offset < -offset))
128         {
129           gpg_err_set_errno (EINVAL);
130           return -1;
131         }
132       dh->data.mem.offset += offset;
133       break;
134     case SEEK_END:
135       if (offset > 0 || -offset > dh->data.mem.length)
136         {
137           gpg_err_set_errno (EINVAL);
138           return -1;
139         }
140       dh->data.mem.offset = dh->data.mem.length + offset;
141       break;
142     default:
143       gpg_err_set_errno (EINVAL);
144       return -1;
145     }
146   return dh->data.mem.offset;
147 }
148
149
150 static void
151 mem_release (gpgme_data_t dh)
152 {
153   if (dh->data.mem.buffer)
154     free (dh->data.mem.buffer);
155 }
156
157
158 static struct _gpgme_data_cbs mem_cbs =
159   {
160     mem_read,
161     mem_write,
162     mem_seek,
163     mem_release,
164     NULL
165   };
166
167 \f
168 /* Create a new data buffer and return it in R_DH.  */
169 gpgme_error_t
170 gpgme_data_new (gpgme_data_t *r_dh)
171 {
172   gpgme_error_t err;
173   TRACE_BEG  (DEBUG_DATA, "gpgme_data_new", r_dh, "");
174
175   err = _gpgme_data_new (r_dh, &mem_cbs);
176
177   if (err)
178     return TRACE_ERR (err);
179
180   TRACE_SUC ("dh=%p", *r_dh);
181   return 0;
182 }
183
184
185 /* Create a new data buffer filled with SIZE bytes starting from
186    BUFFER.  If COPY is zero, copying is delayed until necessary, and
187    the data is taken from the original location when needed.  */
188 gpgme_error_t
189 gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer,
190                          size_t size, int copy)
191 {
192   gpgme_error_t err;
193   TRACE_BEG  (DEBUG_DATA, "gpgme_data_new_from_mem", r_dh,
194               "buffer=%p, size=%zu, copy=%i (%s)", buffer, size,
195               copy, copy ? "yes" : "no");
196
197   err = _gpgme_data_new (r_dh, &mem_cbs);
198   if (err)
199     return TRACE_ERR (err);
200
201   if (copy)
202     {
203       char *bufcpy = malloc (size);
204       if (!bufcpy)
205         {
206           int saved_err = gpg_error_from_syserror ();
207           _gpgme_data_release (*r_dh);
208           return TRACE_ERR (saved_err);
209         }
210       memcpy (bufcpy, buffer, size);
211       (*r_dh)->data.mem.buffer = bufcpy;
212     }
213   else
214     (*r_dh)->data.mem.orig_buffer = buffer;
215
216   (*r_dh)->data.mem.size = size;
217   (*r_dh)->data.mem.length = size;
218   TRACE_SUC ("dh=%p", *r_dh);
219   return 0;
220 }
221
222
223 /* Destroy the data buffer DH and return a pointer to its content.
224    The memory has be to released with gpgme_free() by the user.  It's
225    size is returned in R_LEN.  */
226 char *
227 gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len)
228 {
229   gpg_error_t err;
230   char *str = NULL;
231   size_t len;
232   int blankout;
233
234   TRACE_BEG  (DEBUG_DATA, "gpgme_data_release_and_get_mem", dh,
235               "r_len=%p", r_len);
236
237   if (!dh || dh->cbs != &mem_cbs)
238     {
239       gpgme_data_release (dh);
240       TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
241       return NULL;
242     }
243
244   err = _gpgme_data_get_prop (dh, 0, DATA_PROP_BLANKOUT, &blankout);
245   if (err)
246     {
247       gpgme_data_release (dh);
248       TRACE_ERR (err);
249       return NULL;
250     }
251
252   str = dh->data.mem.buffer;
253   len = dh->data.mem.length;
254   if (blankout && len)
255     len = 1;
256
257   if (!str && dh->data.mem.orig_buffer)
258     {
259       str = malloc (len);
260       if (!str)
261         {
262           int saved_err = gpg_error_from_syserror ();
263           gpgme_data_release (dh);
264           TRACE_ERR (saved_err);
265           return NULL;
266         }
267       if (blankout)
268         memset (str, 0, len);
269       else
270         memcpy (str, dh->data.mem.orig_buffer, len);
271     }
272   else
273     {
274       if (blankout && len)
275         *str = 0;
276       /* Prevent mem_release from releasing the buffer memory.  We
277        * must not fail from this point.  */
278       dh->data.mem.buffer = NULL;
279     }
280
281   if (r_len)
282     *r_len = len;
283
284   gpgme_data_release (dh);
285
286   if (r_len)
287     TRACE_SUC ("buffer=%p, len=%zu", str, *r_len);
288   else
289     TRACE_SUC ("buffer=%p", str);
290   return str;
291 }
292
293
294 /* Release the memory returned by gpgme_data_release_and_get_mem() and
295    some other functions.  */
296 void
297 gpgme_free (void *buffer)
298 {
299   TRACE (DEBUG_DATA, "gpgme_free", buffer, "");
300
301   if (buffer)
302     free (buffer);
303 }