From 412ccb3b47985fc6a631df6ef2e252609a6d85c8 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Wed, 9 Oct 2002 12:26:00 +0000 Subject: [PATCH] 2002-10-09 Marcus Brinkmann * data.h, data-user.c, data-stream.c, data-mem.c, data-fd.c, data-compat.c: New file. Really check them in this time, completes 2002-10-08 change. --- gpgme/ChangeLog | 4 + gpgme/data-compat.c | 193 ++++++++++++++++++++++++++++++++ gpgme/data-fd.c | 70 ++++++++++++ gpgme/data-mem.c | 266 ++++++++++++++++++++++++++++++++++++++++++++ gpgme/data-stream.c | 76 +++++++++++++ gpgme/data-user.c | 78 +++++++++++++ gpgme/data.h | 118 ++++++++++++++++++++ 7 files changed, 805 insertions(+) create mode 100644 gpgme/data-compat.c create mode 100644 gpgme/data-fd.c create mode 100644 gpgme/data-mem.c create mode 100644 gpgme/data-stream.c create mode 100644 gpgme/data-user.c create mode 100644 gpgme/data.h diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index ce496693..5dba90e0 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,5 +1,9 @@ 2002-10-09 Marcus Brinkmann + * data.h, data-user.c, data-stream.c, data-mem.c, data-fd.c, + data-compat.c: New file. Really check them in this time, completes + 2002-10-08 change. + * rungpg.h (GpgStatusHandler): Rename type to GpgmeStatusHandler and move to ... * types.h (GpgmeStatusHandler): ... here. diff --git a/gpgme/data-compat.c b/gpgme/data-compat.c new file mode 100644 index 00000000..6eec787c --- /dev/null +++ b/gpgme/data-compat.c @@ -0,0 +1,193 @@ +/* data-mem.c - A memory based data object. + * Copyright (C) 2002 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "data.h" +#include "util.h" + + +/* Create a new data buffer filled with LENGTH bytes starting from + OFFSET within the file FNAME or stream STREAM (exactly one must be + non-zero). */ +GpgmeError +gpgme_data_new_from_filepart (GpgmeData *dh, const char *fname, FILE *stream, + off_t offset, size_t length) +{ + GpgmeError err; + char *buf = NULL; + + if (stream && fname) + return mk_error (Invalid_Value); + + if (fname) + stream = fopen (fname, "rb"); + if (!stream) + return mk_error (File_Error); + + if (fseek (stream, offset, SEEK_SET)) + goto ferr; + + buf = malloc (length); + if (!buf) + goto ferr; + + while (fread (buf, length, 1, stream) < 1 + && ferror (stream) && errno == EINTR); + if (ferror (stream)) + { + if (buf) + free (buf); + goto ferr; + } + + if (fname) + fclose (stream); + + err = gpgme_data_new (dh); + if (err) + { + if (buf) + free (buf); + return err; + } + + (*dh)->data.mem.buffer = buf; + (*dh)->data.mem.size = length; + (*dh)->data.mem.length = length; + return 0; + + ferr: + { + int saved_errno = errno; + if (fname) + fclose (stream); + errno = saved_errno; + return mk_error (File_Error); + } +} + + +/* Create a new data buffer filled with the content of file FNAME. + COPY must be non-zero (delayed reads are not supported yet). */ +GpgmeError +gpgme_data_new_from_file (GpgmeData *dh, const char *fname, int copy) +{ + struct stat statbuf; + + if (!fname || !copy) + return mk_error (Invalid_Value); + + if (stat (fname, &statbuf) < 0) + return mk_error (File_Error); + + return gpgme_data_new_from_filepart (dh, fname, NULL, 0, statbuf.st_size); +} + + +static int +gpgme_error_to_errno (GpgmeError err) +{ + switch (err) + { + case mk_error (EOF): + return 0; + case mk_error (Out_Of_Core): + errno = ENOMEM; + return -1; + case mk_error (Invalid_Value): + errno = EINVAL; + return -1; + case mk_error (Busy): + errno = EBUSY; + return -1; + case mk_error (Not_Implemented): + errno = EOPNOTSUPP; + return -1; + default: + /* XXX Yeah, well. */ + errno = EINVAL; + return -1; + } +} + +static int +old_user_read (GpgmeData dh, void *buffer, size_t size) +{ + size_t amt; + GpgmeError err = (*dh->data.old_user.cb) (dh->data.old_user.handle, + buffer, size, &amt); + if (err) + return gpgme_error_to_errno (err); + return amt; +} + + +static off_t +old_user_seek (GpgmeData dh, off_t offset, int whence) +{ + GpgmeError err; + if (whence != SEEK_SET || offset) + return EINVAL; + err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL); + if (err) + return gpgme_error_to_errno (err); + return 0; +} + + +static struct gpgme_data_cbs old_user_cbs = + { + old_user_read, + NULL, + old_user_seek, + NULL + }; + + +/* Create a new data buffer which retrieves the data from the callback + function READ_CB. */ +GpgmeError +gpgme_data_new_with_read_cb (GpgmeData *dh, + int (*read_cb) (void *, char *, size_t, size_t *), + void *read_cb_value) +{ + GpgmeError err = _gpgme_data_new (dh, &old_user_cbs); + if (err) + return err; + + (*dh)->data.old_user.cb = read_cb; + (*dh)->data.old_user.handle = read_cb_value; + return 0; +} + + +GpgmeError +gpgme_data_rewind (GpgmeData dh) +{ + return (gpgme_data_seek (dh, 0, SEEK_SET) == -1) + ? mk_error (File_Error) : 0; +} diff --git a/gpgme/data-fd.c b/gpgme/data-fd.c new file mode 100644 index 00000000..037183d3 --- /dev/null +++ b/gpgme/data-fd.c @@ -0,0 +1,70 @@ +/* data-fd.c - A file descripor based data object. + * Copyright (C) 2002 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "data.h" + + +static int +fd_read (GpgmeData dh, void *buffer, size_t size) +{ + return read (dh->data.fd, buffer, size); +} + + +static ssize_t +fd_write (GpgmeData dh, const void *buffer, size_t size) +{ + return write (dh->data.fd, buffer, size); +} + + +static off_t +fd_seek (GpgmeData dh, off_t offset, int whence) +{ + return lseek (dh->data.fd, offset, whence); +} + + +static struct gpgme_data_cbs fd_cbs = + { + fd_read, + fd_write, + fd_seek, + NULL + }; + + +GpgmeError +gpgme_data_new_from_fd (GpgmeData *dh, int fd) +{ + GpgmeError err = _gpgme_data_new (dh, &fd_cbs); + if (err) + return err; + + (*dh)->data.fd = fd; + return 0; +} diff --git a/gpgme/data-mem.c b/gpgme/data-mem.c new file mode 100644 index 00000000..0afd1168 --- /dev/null +++ b/gpgme/data-mem.c @@ -0,0 +1,266 @@ +/* data-mem.c - A memory based data object. + * Copyright (C) 2002 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "util.h" + + +static int +mem_read (GpgmeData dh, void *buffer, size_t size) +{ + size_t amt = dh->data.mem.length - dh->data.mem.offset; + const char *src; + + if (!amt) + return 0; + + if (size < amt) + amt = size; + + src = dh->data.mem.buffer ? dh->data.mem.buffer : dh->data.mem.orig_buffer; + memcpy (buffer, src + dh->data.mem.offset, amt); + dh->data.mem.offset += amt; + return amt; +} + + +static ssize_t +mem_write (GpgmeData dh, const void *buffer, size_t size) +{ + size_t unused; + + if (!dh->data.mem.buffer && dh->data.mem.orig_buffer) + { + size_t new_size = dh->data.mem.size; + char *new_buffer; + + if (new_size < dh->data.mem.offset + size) + new_size = dh->data.mem.offset + size; + + new_buffer = malloc (new_size); + if (!new_buffer) + return -1; + dh->data.mem.buffer = new_buffer; + dh->data.mem.size = new_size; + } + + unused = dh->data.mem.size - dh->data.mem.offset; + if (unused < size) + { + /* Allocate a large enough buffer with exponential backoff. */ +#define INITIAL_ALLOC 512 + size_t new_size = dh->data.mem.size + ? (2 * dh->data.mem.size) : INITIAL_ALLOC; + char *new_buffer; + + if (new_size < dh->data.mem.offset + size) + new_size = dh->data.mem.offset + size; + + new_buffer = realloc (dh->data.mem.buffer, new_size); + if (!new_buffer && new_size > dh->data.mem.offset + size) + { + /* Maybe we were too greedy, try again. */ + new_size = dh->data.mem.offset + size; + new_buffer = realloc (dh->data.mem.buffer, new_size); + } + if (!new_buffer) + return -1; + dh->data.mem.buffer = new_buffer; + dh->data.mem.size = new_size; + } + + memcpy (dh->data.mem.buffer + dh->data.mem.offset, buffer, size); + dh->data.mem.offset += size; + if (dh->data.mem.length < dh->data.mem.offset) + dh->data.mem.length = dh->data.mem.offset; + return size; +} + + +static off_t +mem_seek (GpgmeData dh, off_t offset, int whence) +{ + switch (whence) + { + case SEEK_SET: + if (offset < 0 || offset > dh->data.mem.length) + { + errno = EINVAL; + return -1; + } + dh->data.mem.offset = offset; + break; + case SEEK_CUR: + if ((offset > 0 && dh->data.mem.length - dh->data.mem.offset < offset) + || (offset < 0 && dh->data.mem.offset < -offset)) + { + errno = EINVAL; + return -1; + } + dh->data.mem.offset += offset; + break; + case SEEK_END: + if (offset > 0 || -offset > dh->data.mem.length) + { + errno = EINVAL; + return -1; + } + dh->data.mem.offset = dh->data.mem.length - offset; + break; + default: + errno = EINVAL; + return -1; + } + return dh->data.mem.offset; +} + + +static int +mem_release (GpgmeData dh) +{ + if (dh->data.mem.buffer) + free (dh->data.mem.buffer); + return 0; +} + + +static struct gpgme_data_cbs mem_cbs = + { + mem_read, + mem_write, + mem_seek, + mem_release + }; + + +GpgmeError +gpgme_data_new (GpgmeData *dh) +{ + GpgmeError err = _gpgme_data_new (dh, &mem_cbs); + if (err) + return err; + + return 0; +} + + +/* Create a new data buffer filled with SIZE bytes starting from + BUFFER. If COPY is zero, copying is delayed until necessary, and + the data is taken from the original location when needed. */ +GpgmeError +gpgme_data_new_from_mem (GpgmeData *dh, const char *buffer, + size_t size, int copy) +{ + GpgmeError err = _gpgme_data_new (dh, &mem_cbs); + if (err) + return err; + + if (copy) + { + char *bufcpy = malloc (size); + if (!bufcpy) + _gpgme_data_release (*dh); + memcpy (bufcpy, buffer, size); + (*dh)->data.mem.buffer = bufcpy; + } + else + (*dh)->data.mem.orig_buffer = buffer; + + (*dh)->data.mem.size = size; + (*dh)->data.mem.length = size; + return 0; +} + + +/* This function does make sense when we know that it contains no nil + chars and if the underlying data object is memory based. */ +char * +_gpgme_data_get_as_string (GpgmeData dh) +{ + char *dst = NULL; + const char *src = NULL; + + assert (dh->cbs == &mem_cbs); + + src = dh->data.mem.buffer; + if (!src) + src = dh->data.mem.orig_buffer; + dst = malloc (dh->data.mem.length + 1); + if (dst) + { + if (src) + memcpy (dst, src, dh->data.mem.length); + dst[dh->data.mem.length] = '\0'; + } + return dst; +} + + +char * +gpgme_data_release_and_get_mem (GpgmeData dh, size_t *r_len) +{ + char *str = NULL; + + if (!dh || dh->cbs != &mem_cbs) + return NULL; + + str = dh->data.mem.buffer; + if (!str && dh->data.mem.orig_buffer) + { + str = malloc (dh->data.mem.length); + if (!str) + return NULL; + memcpy (str, dh->data.mem.orig_buffer, dh->data.mem.length); + } + + if (r_len) + *r_len = dh->data.mem.length; + + return str; +} + + +/* This function does make sense when we know that it contains no nil + chars and if the underlying data object is memory based. */ +char * +_gpgme_data_release_and_return_string (GpgmeData dh) +{ + char *str = NULL; + + if (!dh) + return NULL; + + assert (dh->cbs == &mem_cbs); + if (gpgme_data_write (dh, "", 1) == 1) + str = gpgme_data_release_and_get_mem (dh, NULL); + else + gpgme_data_release (dh); + + return str; +} diff --git a/gpgme/data-stream.c b/gpgme/data-stream.c new file mode 100644 index 00000000..33d44986 --- /dev/null +++ b/gpgme/data-stream.c @@ -0,0 +1,76 @@ +/* data-stream.c - A memory based data object. + * Copyright (C) 2002 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "data.h" + + +static int +stream_read (GpgmeData dh, void *buffer, size_t size) +{ + size_t amt = fread (buffer, 1, size, dh->data.stream); + if (amt > 0) + return amt; + return ferror (dh->data.stream) ? -1 : 0; +} + + +static ssize_t +stream_write (GpgmeData dh, const void *buffer, size_t size) +{ + size_t amt = fwrite (buffer, 1, size, dh->data.stream); + if (amt > 0) + return amt; + return ferror (dh->data.stream) ? -1 : 0; +} + + +static off_t +stream_seek (GpgmeData dh, off_t offset, int whence) +{ + return fseek (dh->data.stream, offset, whence); +} + + +static struct gpgme_data_cbs stream_cbs = + { + stream_read, + stream_write, + stream_seek, + NULL + }; + + +GpgmeError +gpgme_data_new_from_stream (GpgmeData *dh, FILE *stream) +{ + GpgmeError err = _gpgme_data_new (dh, &stream_cbs); + if (err) + return err; + + (*dh)->data.stream = stream; + return 0; +} diff --git a/gpgme/data-user.c b/gpgme/data-user.c new file mode 100644 index 00000000..dd0e5700 --- /dev/null +++ b/gpgme/data-user.c @@ -0,0 +1,78 @@ +/* data-user.c - A user callback based data object. + * Copyright (C) 2002 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include + +#include "data.h" + + +static int +user_read (GpgmeData dh, void *buffer, size_t size) +{ + return (*dh->data.user.cbs->read) (dh->data.user.handle, buffer, size); +} + + +static ssize_t +user_write (GpgmeData dh, const void *buffer, size_t size) +{ + return (*dh->data.user.cbs->write) (dh->data.user.handle, buffer, size); +} + + +static off_t +user_seek (GpgmeData dh, off_t offset, int whence) +{ + return (*dh->data.user.cbs->seek) (dh->data.user.handle, offset, whence); +} + + +static int +user_release (GpgmeData dh) +{ + (*dh->data.user.cbs->release) (dh->data.user.handle); + return 0; +} + + +static struct gpgme_data_cbs user_cbs = + { + user_read, + user_write, + user_seek, + user_release + }; + + +GpgmeError +gpgme_data_new_from_cbs (GpgmeData *dh, struct GpgmeDataCbs *cbs, void *handle) +{ + GpgmeError err = _gpgme_data_new (dh, &user_cbs); + if (err) + return err; + + (*dh)->data.user.cbs = cbs; + (*dh)->data.user.handle = handle; + return 0; +} diff --git a/gpgme/data.h b/gpgme/data.h new file mode 100644 index 00000000..67910153 --- /dev/null +++ b/gpgme/data.h @@ -0,0 +1,118 @@ +/* data.h - Internal data object abstraction interface. + * Copyright (C) 2002 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 General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef DATA_H +#define DATA_H + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "gpgme.h" + + +/* Read up to SIZE bytes into buffer BUFFER from the data object with + the handle DH. Return the number of characters read, 0 on EOF and + -1 on error. If an error occurs, errno is set. */ +typedef int (*gpgme_data_read_cb) (GpgmeData dh, void *buffer, size_t size); + +/* Write up to SIZE bytes from buffer BUFFER to the data object with + the handle DH. Return the number of characters written, or -1 on + error. If an error occurs, errno is set. */ +typedef ssize_t (*gpgme_data_write_cb) (GpgmeData dh, const void *buffer, + size_t size); + +/* Set the current position from where the next read or write starts + in the data object with the handle DH to OFFSET, relativ to + WHENCE. */ +typedef off_t (*gpgme_data_seek_cb) (GpgmeData dh, off_t offset, int whence); + +/* Release the data object with the handle DH. */ +typedef int (*gpgme_data_release_cb) (GpgmeData dh); + +struct gpgme_data_cbs +{ + gpgme_data_read_cb read; + gpgme_data_write_cb write; + gpgme_data_seek_cb seek; + gpgme_data_release_cb release; +}; + +struct gpgme_data_s +{ + struct gpgme_data_cbs *cbs; + GpgmeDataEncoding encoding; + +#ifdef PIPE_BUF +#define BUFFER_SIZE PIPE_BUF +#else +#ifdef _POSIX_PIPE_BUF +#define BUFFER_SIZE _POSIX_PIPE_BUF +#else +#define BUFFER_SIZE 512 +#endif +#endif + char pending[BUFFER_SIZE]; + int pending_len; + + union + { + /* For gpgme_data_new_from_fd. */ + int fd; + + /* For gpgme_data_new_from_stream. */ + FILE *stream; + + /* For gpgme_data_new_from_cbs. */ + struct + { + struct GpgmeDataCbs *cbs; + void *handle; + } user; + + /* For gpgme_data_new_from_mem. */ + struct + { + char *buffer; + const char *orig_buffer; + /* Allocated size of BUFFER. */ + size_t size; + size_t length; + size_t offset; + } mem; + + /* For gpgme_data_new_from_read_cb. */ + struct + { + int (*cb) (void *, char *, size_t, size_t *); + void *handle; + } old_user; + } data; +}; + + +GpgmeError _gpgme_data_new (GpgmeData *r_dh, struct gpgme_data_cbs *cbs); + +void _gpgme_data_release (GpgmeData dh); + +#endif /* DATA_H */