json: Fix t-decrypt-verify.out for GnuPG >= 2.3.
[gpgme.git] / src / data-compat.c
1 /* data-compat.c - Compatibility interfaces for data objects.
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 #ifdef HAVE_SYS_TIME_H
27 # include <sys/time.h>
28 #endif
29 #ifdef HAVE_SYS_STAT_H
30 # include <sys/stat.h>
31 #endif
32 #include <stdlib.h>
33
34 #include "data.h"
35 #include "util.h"
36 #include "debug.h"
37
38 \f
39 /* Create a new data buffer filled with LENGTH bytes starting from
40    OFFSET within the file FNAME or stream STREAM (exactly one must be
41    non-zero).  */
42 gpgme_error_t
43 gpgme_data_new_from_filepart (gpgme_data_t *r_dh, const char *fname,
44                               FILE *stream, gpgme_off_t offset, size_t length)
45 {
46   gpgme_error_t err;
47   char *buf = NULL;
48   int res;
49
50   TRACE_BEG  (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh,
51               "file_name=%s, stream=%p, offset=%lli, length=%zu",
52               fname, stream, (long long int)offset, length);
53
54   if (stream && fname)
55     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
56
57   if (fname)
58     stream = fopen (fname, "rb");
59   if (!stream)
60     return TRACE_ERR (gpg_error_from_syserror ());
61
62 #ifdef HAVE_FSEEKO
63   res = fseeko (stream, offset, SEEK_SET);
64 #else
65   /* FIXME: Check for overflow, or at least bail at compilation.  */
66   res = fseek (stream, offset, SEEK_SET);
67 #endif
68
69   if (res)
70     {
71       int saved_err = gpg_error_from_syserror ();
72       if (fname)
73         fclose (stream);
74       return TRACE_ERR (saved_err);
75     }
76
77   buf = malloc (length);
78   if (!buf)
79     {
80       int saved_err = gpg_error_from_syserror ();
81       if (fname)
82         fclose (stream);
83       return TRACE_ERR (saved_err);
84     }
85
86   while (fread (buf, length, 1, stream) < 1
87          && ferror (stream) && errno == EINTR);
88   if (ferror (stream))
89     {
90       int saved_err = gpg_error_from_syserror ();
91       if (buf)
92         free (buf);
93       if (fname)
94         fclose (stream);
95       return TRACE_ERR (saved_err);
96     }
97
98   if (fname)
99     fclose (stream);
100
101   err = gpgme_data_new (r_dh);
102   if (err)
103     {
104       if (buf)
105         free (buf);
106       return err;
107     }
108
109   (*r_dh)->data.mem.buffer = buf;
110   (*r_dh)->data.mem.size = length;
111   (*r_dh)->data.mem.length = length;
112
113   TRACE_SUC ("r_dh=%p", *r_dh);
114   return 0;
115 }
116
117 \f
118 /* Create a new data buffer filled with the content of file FNAME.
119    COPY must be non-zero (delayed reads are not supported yet).  */
120 gpgme_error_t
121 gpgme_data_new_from_file (gpgme_data_t *r_dh, const char *fname, int copy)
122 {
123   gpgme_error_t err;
124   struct stat statbuf;
125   TRACE_BEG  (DEBUG_DATA, "gpgme_data_new_from_file", r_dh,
126               "file_name=%s, copy=%i (%s)", fname, copy, copy ? "yes" : "no");
127
128   if (!fname || !copy)
129     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
130
131   if (stat (fname, &statbuf) < 0)
132     return TRACE_ERR (gpg_error_from_syserror ());
133
134   err = gpgme_data_new_from_filepart (r_dh, fname, NULL, 0, statbuf.st_size);
135   return TRACE_ERR (err);
136 }
137
138 \f
139 static int
140 gpgme_error_to_errno (gpgme_error_t err)
141 {
142   int res = gpg_err_code_to_errno (gpg_err_code (err));
143
144   if (!err)
145     {
146       switch (gpg_err_code (err))
147         {
148         case GPG_ERR_EOF:
149           res = 0;
150           break;
151         case GPG_ERR_INV_VALUE:
152           res = EINVAL;
153           break;
154         case GPG_ERR_NOT_SUPPORTED:
155           res = ENOSYS;
156           break;
157         default:
158           /* FIXME: Yeah, well.  */
159           res = EINVAL;
160           break;
161         }
162     }
163   TRACE (DEBUG_DATA, "gpgme:gpgme_error_to_errno", NULL,
164           "mapping %s <%s> to: %s", gpgme_strerror (err),
165           gpgme_strsource (err), strerror (res));
166   gpg_err_set_errno (res);
167   return res ? -1 : 0;
168 }
169
170
171 static gpgme_ssize_t
172 old_user_read (gpgme_data_t dh, void *buffer, size_t size)
173 {
174   gpgme_error_t err;
175   size_t amt;
176   TRACE_BEG  (DEBUG_DATA, "gpgme:old_user_read", dh,
177               "buffer=%p, size=%zu", buffer, size);
178
179   err = (*dh->data.old_user.cb) (dh->data.old_user.handle,
180                                  buffer, size, &amt);
181   if (err)
182     return TRACE_SYSRES (gpgme_error_to_errno (err));
183   return TRACE_SYSRES ((int)amt);
184 }
185
186
187 static gpgme_off_t
188 old_user_seek (gpgme_data_t dh, gpgme_off_t offset, int whence)
189 {
190   gpgme_error_t err;
191   TRACE_BEG  (DEBUG_DATA, "gpgme:old_user_seek", dh,
192               "offset=%llu, whence=%i", (long long int)offset, whence);
193
194   if (whence != SEEK_SET || offset)
195     {
196       gpg_err_set_errno (EINVAL);
197       return TRACE_SYSRES (-1);
198     }
199   err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL);
200   if (err)
201     return TRACE_SYSRES (gpgme_error_to_errno (err));
202   return TRACE_SYSRES (0);
203 }
204
205
206 static struct _gpgme_data_cbs old_user_cbs =
207   {
208     old_user_read,
209     NULL,
210     old_user_seek,
211     NULL
212   };
213
214
215 /* Create a new data buffer which retrieves the data from the callback
216    function READ_CB.  */
217 gpgme_error_t
218 gpgme_data_new_with_read_cb (gpgme_data_t *r_dh,
219                              int (*read_cb) (void *, char *, size_t, size_t *),
220                              void *read_cb_value)
221 {
222   gpgme_error_t err;
223   TRACE_BEG  (DEBUG_DATA, "gpgme_data_new_with_read_cb", r_dh,
224               "read_cb=%p/%p", read_cb, read_cb_value);
225
226   err = _gpgme_data_new (r_dh, &old_user_cbs);
227
228   if (err)
229     return TRACE_ERR (err);
230
231   (*r_dh)->data.old_user.cb = read_cb;
232   (*r_dh)->data.old_user.handle = read_cb_value;
233   return TRACE_ERR (0);
234 }