Fix gpg_addkey() to work with GPGME_CREATE_NOPASSWD as well.
[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, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19    02111-1307, USA.  */
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   return TRACE_SUC1 ("dh=%p", *r_dh);
181 }
182
183
184 /* Create a new data buffer filled with SIZE bytes starting from
185    BUFFER.  If COPY is zero, copying is delayed until necessary, and
186    the data is taken from the original location when needed.  */
187 gpgme_error_t
188 gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer,
189                          size_t size, int copy)
190 {
191   gpgme_error_t err;
192   TRACE_BEG4 (DEBUG_DATA, "gpgme_data_new_from_mem", r_dh,
193               "buffer=%p, size=%u, copy=%i (%s)", buffer, size,
194               copy, copy ? "yes" : "no");
195
196   err = _gpgme_data_new (r_dh, &mem_cbs);
197   if (err)
198     return TRACE_ERR (err);
199
200   if (copy)
201     {
202       char *bufcpy = malloc (size);
203       if (!bufcpy)
204         {
205           int saved_err = gpg_error_from_syserror ();
206           _gpgme_data_release (*r_dh);
207           return TRACE_ERR (saved_err);
208         }
209       memcpy (bufcpy, buffer, size);
210       (*r_dh)->data.mem.buffer = bufcpy;
211     }
212   else
213     (*r_dh)->data.mem.orig_buffer = buffer;
214
215   (*r_dh)->data.mem.size = size;
216   (*r_dh)->data.mem.length = size;
217   return TRACE_SUC1 ("dh=%p", *r_dh);
218 }
219
220
221 /* Destroy the data buffer DH and return a pointer to its content.
222    The memory has be to released with gpgme_free() by the user.  It's
223    size is returned in R_LEN.  */
224 char *
225 gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len)
226 {
227   char *str = NULL;
228
229   TRACE_BEG1 (DEBUG_DATA, "gpgme_data_release_and_get_mem", dh,
230               "r_len=%p", r_len);
231
232   if (!dh || dh->cbs != &mem_cbs)
233     {
234       gpgme_data_release (dh);
235       TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
236       return NULL;
237     }
238
239   str = dh->data.mem.buffer;
240   if (!str && dh->data.mem.orig_buffer)
241     {
242       str = malloc (dh->data.mem.length);
243       if (!str)
244         {
245           int saved_err = gpg_error_from_syserror ();
246           gpgme_data_release (dh);
247           TRACE_ERR (saved_err);
248           return NULL;
249         }
250       memcpy (str, dh->data.mem.orig_buffer, dh->data.mem.length);
251     }
252   else
253     /* Prevent mem_release from releasing the buffer memory.  We must
254        not fail from this point.  */
255     dh->data.mem.buffer = NULL;
256
257   if (r_len)
258     *r_len = dh->data.mem.length;
259
260   gpgme_data_release (dh);
261
262   if (r_len)
263     {
264       TRACE_SUC2 ("buffer=%p, len=%u", str, *r_len);
265     }
266   else
267     {
268       TRACE_SUC1 ("buffer=%p", str);
269     }
270   return str;
271 }
272
273
274 /* Release the memory returned by gpgme_data_release_and_get_mem() and
275    some other functions.  */
276 void
277 gpgme_free (void *buffer)
278 {
279   TRACE (DEBUG_DATA, "gpgme_free", buffer);
280
281   if (buffer)
282     free (buffer);
283 }