diff --git a/src/reader.c b/src/reader.c
index c59978d..e117896 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -1,450 +1,485 @@
/* reader.c - provides the Reader object
* Copyright (C) 2001, 2010, 2012 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - 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.
*
* or both in parallel, as here.
*
* KSBA 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 copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
#include
#include
#include
#include
#include
#include
#include "util.h"
#include "ksba.h"
#include "reader.h"
/**
* ksba_reader_new:
*
* Create a new but uninitialized ksba_reader_t Object. Using this
* reader object in unitialized state does always yield eof.
*
* Return value: ksba_reader_t object or an error code.
**/
gpg_error_t
ksba_reader_new (ksba_reader_t *r_r)
{
*r_r = xtrycalloc (1, sizeof **r_r);
if (!*r_r)
return gpg_error_from_errno (errno);
return 0;
}
/**
* ksba_reader_release:
* @r: Reader Object (or NULL)
*
* Release this object
**/
void
ksba_reader_release (ksba_reader_t r)
{
if (!r)
return;
if (r->notify_cb)
{
void (*notify_fnc)(void*,ksba_reader_t) = r->notify_cb;
r->notify_cb = NULL;
notify_fnc (r->notify_cb_value, r);
}
if (r->type == READER_TYPE_MEM)
xfree (r->u.mem.buffer);
xfree (r->unread.buf);
xfree (r);
}
/* Set NOTIFY as function to be called by ksba_reader_release before
resources are actually deallocated. NOTIFY_VALUE is passed to the
called function as its first argument. Note that only the last
registered function will be called; passing NULL for NOTIFY removes
the notification. */
gpg_error_t
ksba_reader_set_release_notify (ksba_reader_t r,
void (*notify)(void*,ksba_reader_t),
void *notify_value)
{
if (!r)
return gpg_error (GPG_ERR_INV_VALUE);
r->notify_cb = notify;
r->notify_cb_value = notify_value;
return 0;
}
/* Clear the error and eof indicators for READER, so that it can be
continued to use. Also dicards any unread bytes. This is usually
required if the upper layer want to send to send an EOF to indicate
the logical end of one part of a file. If BUFFER and BUFLEN are
not NULL, possible unread data is copied to a newly allocated
buffer and this buffer is assigned to BUFFER, BUFLEN will be set to
the length of the unread bytes. */
gpg_error_t
ksba_reader_clear (ksba_reader_t r, unsigned char **buffer, size_t *buflen)
{
size_t n;
if (!r)
return gpg_error (GPG_ERR_INV_VALUE);
r->eof = 0;
r->error = 0;
r->nread = 0;
n = r->unread.length;
r->unread.length = 0;
if (buffer && buflen)
{
*buffer = NULL;
*buflen = 0;
if (n)
{
*buffer = xtrymalloc (n);
if (!*buffer)
return gpg_error_from_errno (errno);
memcpy (*buffer, r->unread.buf, n);
*buflen = n;
}
}
return 0;
}
gpg_error_t
ksba_reader_error (ksba_reader_t r)
{
return r? gpg_error_from_errno (r->error) : gpg_error (GPG_ERR_INV_VALUE);
}
unsigned long
ksba_reader_tell (ksba_reader_t r)
{
return r? r->nread : 0;
}
/**
* ksba_reader_set_mem:
* @r: Reader object
* @buffer: Data
* @length: Length of Data (bytes)
*
* Intialize the reader object with @length bytes from @buffer and set
* the read position to the beginning. It is possible to reuse this
* reader object with another buffer if the reader object has
* already been initialized using this function.
*
* Return value: 0 on success or an error code.
**/
gpg_error_t
ksba_reader_set_mem (ksba_reader_t r, const void *buffer, size_t length)
{
if (!r || !buffer)
return gpg_error (GPG_ERR_INV_VALUE);
if (r->type == READER_TYPE_MEM)
{ /* Reuse this reader */
xfree (r->u.mem.buffer);
r->type = 0;
}
if (r->type)
return gpg_error (GPG_ERR_CONFLICT);
r->u.mem.buffer = xtrymalloc (length);
if (!r->u.mem.buffer)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (r->u.mem.buffer, buffer, length);
r->u.mem.size = length;
r->u.mem.readpos = 0;
r->type = READER_TYPE_MEM;
r->eof = 0;
return 0;
}
/**
* ksba_reader_set_fd:
* @r: Reader object
* @fd: file descriptor
*
* Initialize the Reader object with a file descriptor, so that read
* operations on this object are excuted on this file descriptor.
*
* Return value:
**/
gpg_error_t
ksba_reader_set_fd (ksba_reader_t r, int fd)
{
if (!r || fd == -1)
return gpg_error (GPG_ERR_INV_VALUE);
if (r->type)
return gpg_error (GPG_ERR_CONFLICT);
r->eof = 0;
r->type = READER_TYPE_FD;
r->u.fd = fd;
return 0;
}
/**
* ksba_reader_set_file:
* @r: Reader object
* @fp: file pointer
*
* Initialize the Reader object with a stdio file pointer, so that read
* operations on this object are excuted on this stream
*
* Return value:
**/
gpg_error_t
ksba_reader_set_file (ksba_reader_t r, FILE *fp)
{
if (!r || !fp)
return gpg_error (GPG_ERR_INV_VALUE);
if (r->type)
return gpg_error (GPG_ERR_CONFLICT);
r->eof = 0;
r->type = READER_TYPE_FILE;
r->u.file = fp;
return 0;
}
/**
* ksba_reader_set_cb:
* @r: Reader object
* @cb: Callback function
* @cb_value: Value passed to the callback function
*
* Initialize the reader object with a callback function.
* This callback function is defined as:
*
* typedef int (*cb) (void *cb_value,
* char *buffer, size_t count,
* size_t *nread);
*
*
* The callback should return a maximium of @count bytes in @buffer
* and the number actually read in @nread. It may return 0 in @nread
* if there are no bytes currently available. To indicate EOF the
* callback should return with an error code of GPG_ERR_EOF and set @nread to
* 0. The callback may support passing %NULL for @buffer and @nread
* and %0 for count as an indication to reset its internal read
* pointer.
*
* Return value: 0 on success or an error code
**/
gpg_error_t
ksba_reader_set_cb (ksba_reader_t r,
int (*cb)(void*,char *,size_t,size_t*), void *cb_value )
{
if (!r || !cb)
return gpg_error (GPG_ERR_INV_VALUE);
if (r->type)
return gpg_error (GPG_ERR_CONFLICT);
r->eof = 0;
r->type = READER_TYPE_CB;
r->u.cb.fnc = cb;
r->u.cb.value = cb_value;
return 0;
}
/**
* ksba_reader_read:
* @r: Readder object
* @buffer: A buffer for returning the data
* @length: The length of this buffer
* @nread: Number of bytes actually read.
*
* Read data from the current read position to the supplied @buffer,
* max. @length bytes are read and the actual number of bytes read are
* returned in @nread. If there are no more bytes available %GPG_ERR_EOF is
* returned and @nread is set to 0.
*
* If a @buffer of NULL is specified, the function does only return
* the number of bytes available and does not move the read pointer.
* This does only work for objects initialized from memory; if the
* object is not capable of this it will return the error
* GPG_ERR_NOT_IMPLEMENTED
*
* Return value: 0 on success, GPG_ERR_EOF or another error code
**/
gpg_error_t
ksba_reader_read (ksba_reader_t r, char *buffer, size_t length, size_t *nread)
{
size_t nbytes;
if (!r || !nread)
return gpg_error (GPG_ERR_INV_VALUE);
if (!buffer)
{
if (r->type != READER_TYPE_MEM)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
*nread = r->u.mem.size - r->u.mem.readpos;
if (r->unread.buf)
*nread += r->unread.length - r->unread.readpos;
return *nread? 0 : gpg_error (GPG_ERR_EOF);
}
*nread = 0;
if (r->unread.buf && r->unread.length)
{
nbytes = r->unread.length - r->unread.readpos;
if (!nbytes)
return gpg_error (GPG_ERR_BUG);
if (nbytes > length)
nbytes = length;
memcpy (buffer, r->unread.buf + r->unread.readpos, nbytes);
r->unread.readpos += nbytes;
if (r->unread.readpos == r->unread.length)
r->unread.readpos = r->unread.length = 0;
*nread = nbytes;
r->nread += nbytes;
return 0;
}
if (!r->type)
{
r->eof = 1;
return gpg_error (GPG_ERR_EOF);
}
else if (r->type == READER_TYPE_MEM)
{
nbytes = r->u.mem.size - r->u.mem.readpos;
if (!nbytes)
{
r->eof = 1;
return gpg_error (GPG_ERR_EOF);
}
if (nbytes > length)
nbytes = length;
memcpy (buffer, r->u.mem.buffer + r->u.mem.readpos, nbytes);
*nread = nbytes;
r->nread += nbytes;
r->u.mem.readpos += nbytes;
}
else if (r->type == READER_TYPE_FILE)
{
size_t n;
if (r->eof)
return gpg_error (GPG_ERR_EOF);
if (!length)
{
*nread = 0;
return 0;
}
n = fread (buffer, 1, length, r->u.file);
if (n)
{
r->nread += n;
*nread = n;
}
else
*nread = 0;
if (n < length)
{
if (ferror(r->u.file))
r->error = errno;
r->eof = 1;
if (!n)
return gpg_error (GPG_ERR_EOF);
}
}
else if (r->type == READER_TYPE_CB)
{
if (r->eof)
return gpg_error (GPG_ERR_EOF);
if (r->u.cb.fnc (r->u.cb.value, buffer, length, nread))
{
*nread = 0;
r->eof = 1;
return gpg_error (GPG_ERR_EOF);
}
r->nread += *nread;
}
+ else if (r->type == READER_TYPE_FD)
+ {
+ ssize_t n;
+
+ if (r->eof)
+ return gpg_error (GPG_ERR_EOF);
+
+ if (!length)
+ {
+ *nread = 0;
+ return 0;
+ }
+
+ n = read (r->u.fd, buffer, length);
+ if (n > 0)
+ {
+ r->nread += n;
+ *nread = n;
+ }
+ else
+ {
+ *nread = 0;
+
+ if (n < 0)
+ {
+ r->error = errno;
+ return gpg_error_from_errno (errno);
+ }
+ else
+ {
+ r->eof = 1;
+ return gpg_error (GPG_ERR_EOF);
+ }
+ }
+ }
else
return gpg_error (GPG_ERR_BUG);
return 0;
}
gpg_error_t
ksba_reader_unread (ksba_reader_t r, const void *buffer, size_t count)
{
if (!r || !buffer)
return gpg_error (GPG_ERR_INV_VALUE);
if (!count)
return 0;
/* Make sure that we do not push more bytes back than we have read.
Otherwise r->nread won't have a clear semantic. */
if (r->nread < count)
return gpg_error (GPG_ERR_CONFLICT);
if (!r->unread.buf)
{
r->unread.size = count + 100;
r->unread.buf = xtrymalloc (r->unread.size);
if (!r->unread.buf)
return gpg_error (GPG_ERR_ENOMEM);
r->unread.length = count;
r->unread.readpos = 0;
memcpy (r->unread.buf, buffer, count);
r->nread -= count;
}
else if (r->unread.length + count < r->unread.size)
{
memcpy (r->unread.buf+r->unread.length, buffer, count);
r->unread.length += count;
r->nread -= count;
}
else
return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* fixme: easy to do */
return 0;
}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 759b626..949a812 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,64 +1,64 @@
# Makefile.am - for the KSBA regression tests
# Copyright (C) 2001, 2003 g10 Code GmbH
#
# This file is part of KSBA.
#
# KSBA 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 3 of the License, or
# (at your option) any later version.
#
# KSBA 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, see .
## Process this file with automake to produce Makefile.in
TESTS_ENVIRONMENT =
test_certs = cert_dfn_pca01.der cert_dfn_pca15.der \
cert_g10code_test1.der crl_testpki_testpca.der \
samples/authority.crt samples/betsy.crt samples/bull.crt \
samples/ov-ocsp-server.crt samples/ov-userrev.crt \
samples/ov-root-ca-cert.crt samples/ov-serverrev.crt \
samples/ov-user.crt samples/ov-server.crt \
samples/ov2-root-ca-cert.crt samples/ov2-ocsp-server.crt \
samples/ov2-user.crt samples/ov2-userrev.crt
test_crls = samples/ov-test-crl.crl
test_keys = samples/ov-server.p12 samples/ov-userrev.p12 \
samples/ov-serverrev.p12 samples/ov-user.p12
EXTRA_DIST = $(test_certs) samples/README mkoidtbl.awk
BUILT_SOURCES = oidtranstbl.h
CLEANFILES = oidtranstbl.h
-TESTS = cert-basic t-crl-parser t-dnparser t-oid
+TESTS = cert-basic t-crl-parser t-dnparser t-oid t-reader
AM_CFLAGS = $(GPG_ERROR_CFLAGS)
AM_LDFLAGS = -no-install
noinst_HEADERS = t-common.h
noinst_PROGRAMS = $(TESTS) t-cms-parser t-crl-parser t-dnparser t-ocsp
LDADD = ../src/libksba.la $(GPG_ERROR_LIBS)
t_ocsp_SOURCES = t-ocsp.c sha1.c
# Build the OID table: Note that the binary includes data from an
# another program and we may not be allowed to distribute this. This
# ain't no problem as the programs using this generated data are not
# installed and thus not distributed.
oidtranstbl.h: Makefile mkoidtbl.awk
set -e; f="/dev/null"; \
for i in /etc/dumpasn1 /usr/local/bin /usr/local/share /usr/bin \
/usr/share ; do \
if test -f $$i/dumpasn1.cfg; then f=$$i/dumpasn1.cfg; break; fi; \
done; tr -d '\r' <$$f | $(AWK) -f $(srcdir)/mkoidtbl.awk >$@
LOG_COMPILER = $(VALGRIND)
diff --git a/tests/t-reader.c b/tests/t-reader.c
new file mode 100644
index 0000000..53118b5
--- /dev/null
+++ b/tests/t-reader.c
@@ -0,0 +1,207 @@
+/* t-reader.c - basic tests for the reader object
+ * Copyright (C) 2017 g10 Code GmbH
+ *
+ * This file is part of KSBA.
+ *
+ * KSBA 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KSBA 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, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "../src/ksba.h"
+#include "t-common.h"
+
+void
+test_fd(const char* path)
+{
+ int fd = open (path, O_RDONLY);
+ gpg_error_t err = 0;
+ ksba_reader_t reader;
+ ksba_cert_t cert;
+
+ if (fd < 0)
+ {
+ perror ("open() failed");
+ exit (1);
+ }
+
+ if ((err = ksba_reader_new (&reader)))
+ {
+ fprintf (stderr, "ksba_reader_new() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_reader_set_fd (reader, fd)))
+ {
+ fprintf (stderr, "ksba_reader_set_fd() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_cert_new (&cert)))
+ {
+ fprintf (stderr, "ksba_cert_new() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_cert_read_der (cert, reader)))
+ {
+ fprintf(stderr, "ksba_cert_read_der() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ ksba_cert_release (cert);
+ ksba_reader_release (reader);
+ close (fd);
+}
+
+void
+test_file(const char* path)
+{
+ FILE* fp = fopen (path, "r");
+ gpg_error_t err = 0;
+ ksba_reader_t reader;
+ ksba_cert_t cert;
+
+ if (!fp)
+ {
+ perror ("fopen() failed");
+ exit (1);
+ }
+
+ if ((err = ksba_reader_new (&reader)))
+ {
+ fprintf (stderr, "ksba_reader_new() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_reader_set_file (reader, fp)))
+ {
+ fprintf (stderr, "ksba_reader_set_fd() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_cert_new (&cert)))
+ {
+ fprintf (stderr, "ksba_cert_new() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_cert_read_der (cert, reader)))
+ {
+ fprintf(stderr, "ksba_cert_read_der() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ ksba_cert_release (cert);
+ ksba_reader_release (reader);
+ fclose (fp);
+}
+
+void
+test_mem(const char* path)
+{
+ int fd = open (path, O_RDONLY);
+ gpg_error_t err = 0;
+ ksba_reader_t reader;
+ ksba_cert_t cert;
+ char *mem = NULL;
+ ssize_t ret = 0;
+ size_t p = 0;
+ struct stat st;
+
+ if (fd < 0)
+ {
+ perror ("fopen() failed");
+ exit (1);
+ }
+
+ if (fstat (fd, &st))
+ {
+ fprintf (stderr, "fstat() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ mem = xmalloc(st.st_size);
+
+ while (p < st.st_size && (ret = read(fd, mem + p, st.st_size - p)))
+ {
+ if (ret < 0)
+ {
+ fprintf (stderr, "read() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+ p += ret;
+ }
+
+ if ((err = ksba_reader_new (&reader)))
+ {
+ exit (1);
+ }
+
+ if ((err = ksba_reader_set_mem (reader, mem, st.st_size)))
+ {
+ fprintf (stderr, "ksba_reader_set_mem() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_cert_new (&cert)))
+ {
+ fprintf (stderr, "ksba_cert_new() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ if ((err = ksba_cert_read_der (cert, reader)))
+ {
+ fprintf(stderr, "ksba_cert_read_der() failed: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+
+ ksba_cert_release (cert);
+ ksba_reader_release (reader);
+ xfree (mem);
+ close (fd);
+}
+
+int
+main (int argc, char **argv)
+{
+ if (argc == 1)
+ {
+ test_fd (prepend_srcdir ("cert_g10code_test1.der"));
+ test_file (prepend_srcdir ("cert_g10code_test1.der"));
+ test_mem (prepend_srcdir ("cert_g10code_test1.der"));
+ }
+ else
+ {
+ for (int i = 1; i < argc; ++i)
+ {
+ test_fd (argv[i]);
+ test_file (argv[i]);
+ test_mem (argv[i]);
+ }
+ }
+
+ return 0;
+}