bf615386e190943d3f8e795f86d54c6cd2f910e0
[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, 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 #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, off_t offset, size_t length)
45 {
46   gpgme_error_t err;
47   char *buf = NULL;
48   int res;
49
50   TRACE_BEG4 (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh,
51               "file_name=%s, stream=%p, offset=%lli, length=%u",
52               fname, stream, 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_errno (errno));
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_errno = errno;
72       if (fname)
73         fclose (stream);
74       return TRACE_ERR (gpg_error_from_errno (saved_errno));
75     }
76
77   buf = malloc (length);
78   if (!buf)
79     {
80       int saved_errno = errno;
81       if (fname)
82         fclose (stream);
83       return TRACE_ERR (gpg_error_from_errno (saved_errno));
84     }
85
86   while (fread (buf, length, 1, stream) < 1
87          && ferror (stream) && errno == EINTR);
88   if (ferror (stream))
89     {
90       int saved_errno = errno;
91       if (buf)
92         free (buf);
93       if (fname)
94         fclose (stream);
95       return TRACE_ERR (gpg_error_from_errno (saved_errno));
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   return TRACE_SUC1 ("r_dh=%p", *r_dh);
114 }
115
116 \f
117 /* Create a new data buffer filled with the content of file FNAME.
118    COPY must be non-zero (delayed reads are not supported yet).  */
119 gpgme_error_t
120 gpgme_data_new_from_file (gpgme_data_t *r_dh, const char *fname, int copy)
121 {
122   gpgme_error_t err;
123   struct stat statbuf;
124   TRACE_BEG3 (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh,
125               "file_name=%s, copy=%i (%s)", fname, copy, copy ? "yes" : "no");
126
127   if (!fname || !copy)
128     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
129
130   if (stat (fname, &statbuf) < 0)
131     return TRACE_ERR (gpg_error_from_errno (errno));
132
133   err = gpgme_data_new_from_filepart (r_dh, fname, NULL, 0, statbuf.st_size);
134   return TRACE_ERR (err);
135 }
136
137 \f
138 static int
139 gpgme_error_to_errno (gpgme_error_t err)
140 {
141   int res = gpg_err_code_to_errno (err);
142
143   if (!err)
144     {
145       switch (gpg_err_code (err))
146         {
147         case GPG_ERR_EOF:
148           res = 0;
149           break;
150         case GPG_ERR_INV_VALUE:
151           res = EINVAL;
152           break;
153         case GPG_ERR_NOT_SUPPORTED:
154           res = ENOSYS;
155           break;
156         default:
157           /* FIXME: Yeah, well.  */
158           res = EINVAL;
159           break;
160         }
161     }
162   TRACE3 (DEBUG_DATA, "gpgme:gpgme_error_to_errno", 0,
163           "mapping %s <%s> to: %s", gpgme_strerror (err),
164           gpgme_strsource (err), strerror (res));
165   gpg_err_set_errno (res);
166   return res ? -1 : 0;
167 }
168
169
170 static ssize_t
171 old_user_read (gpgme_data_t dh, void *buffer, size_t size)
172 {
173   gpgme_error_t err;
174   size_t amt;
175   TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_read", dh,
176               "buffer=%p, size=%u", buffer, size);
177
178   err = (*dh->data.old_user.cb) (dh->data.old_user.handle,
179                                  buffer, size, &amt);
180   if (err)
181     return TRACE_SYSRES (gpgme_error_to_errno (err));
182   return TRACE_SYSRES (amt);
183 }
184
185
186 static off_t
187 old_user_seek (gpgme_data_t dh, off_t offset, int whence)
188 {
189   gpgme_error_t err;
190   TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_seek", dh,
191               "offset=%llu, whence=%i", offset, whence);
192
193   if (whence != SEEK_SET || offset)
194     {
195       gpg_err_set_errno (EINVAL);
196       return TRACE_SYSRES (-1);
197     }
198   err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL);
199   if (err)
200     return TRACE_SYSRES (gpgme_error_to_errno (err));
201   return TRACE_SYSRES (0);
202 }
203
204
205 static struct _gpgme_data_cbs old_user_cbs =
206   {
207     old_user_read,
208     NULL,
209     old_user_seek,
210     NULL
211   };
212
213
214 /* Create a new data buffer which retrieves the data from the callback
215    function READ_CB.  */
216 gpgme_error_t
217 gpgme_data_new_with_read_cb (gpgme_data_t *r_dh,
218                              int (*read_cb) (void *, char *, size_t, size_t *),
219                              void *read_cb_value)
220 {
221   gpgme_error_t err;
222   TRACE_BEG2 (DEBUG_DATA, "gpgme_data_new_with_read_cb", r_dh,
223               "read_cb=%p/%p", read_cb, read_cb_value);
224
225   err = _gpgme_data_new (r_dh, &old_user_cbs);
226
227   if (err)
228     return TRACE_ERR (err);
229
230   (*r_dh)->data.old_user.cb = read_cb;
231   (*r_dh)->data.old_user.handle = read_cb_value;
232   return TRACE_ERR (0);
233 }
234
235 \f
236 gpgme_error_t
237 gpgme_data_rewind (gpgme_data_t dh)
238 {
239   gpgme_error_t err;
240   TRACE_BEG (DEBUG_DATA, "gpgme_data_rewind", dh);
241
242   err = (gpgme_data_seek (dh, 0, SEEK_SET) == -1)
243     ? gpg_error_from_errno (errno) : 0;
244
245   return TRACE_ERR (err);
246 }