diff --git a/ChangeLog b/ChangeLog
index 7e39ba8..24d5f8d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -50,6 +50,9 @@
on the caller if it works on files or on buffers.
Pcpstreams also automatically encode/decode Z85.
+ Due to the new pcpstream class pcp now supports
+ armored encrypted files, which it didn't previously.
+
Lots of refactoring have been done to clear things
out and make the system work with the changes
above.
@@ -73,6 +76,11 @@
in man/html/. Latest API docs can be found on
http://www.daemon.de/libpcp/.
+ At this point I'd like to thank Liquid Soul. Only
+ thanks to their music I was able to do those heavy
+ changes. It's like a drug boosting the brain. Love
+ U, man!
+
0.2.0 ED25519 and Curve25519 keys are now generated
separately (previously they were generated from
one random seed, the curve had been derived from
diff --git a/TODO b/TODO
index ce4759c..da6b7de 100644
--- a/TODO
+++ b/TODO
@@ -46,6 +46,7 @@ Check is_utf8 license.
Vault checksum with global vault
+Symmetric crypt mode tries to open vault
Python binding, e.g.:
py % cdll.LoadLibrary("libsodium.so.8")
diff --git a/include/pcp.h b/include/pcp.h
index fb0d6c6..8b16eae 100644
--- a/include/pcp.h
+++ b/include/pcp.h
@@ -8,7 +8,6 @@ extern "C" {
#include "pcp/config.h"
#include "pcp/base85.h"
#include "pcp/buffer.h"
-#include "pcp/config.h"
#include "pcp/crypto.h"
#include "pcp/defines.h"
#include "pcp/digital_crc32.h"
diff --git a/include/pcp/defines.h b/include/pcp/defines.h
index 1e336bd..c2f8e7f 100644
--- a/include/pcp/defines.h
+++ b/include/pcp/defines.h
@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
- Copyright (C) 2013 T.Linden.
+ Copyright (C) 2013-2014 T.v.Dein.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
- You can contact me by mail: .
+ You can contact me by mail: .
*/
@@ -58,8 +58,8 @@ typedef unsigned short dbyte; /* Double byte = 16 bits */
typedef unsigned int qbyte; /* Quad byte = 32 bits */
/* key stuff, deprecated. */
-#define PCP_ENFILE_HEADER "~~~~~ BEGIN PCP ENCRYPTED FILE ~~~~~"
-#define PCP_ENFILE_FOOTER "~~~~~ END PCP ENCRYPTED FILE ~~~~~"
+#define PCP_ENFILE_HEADER "~~~~~ BEGIN PCP ENCRYPTED FILE ~~~~~\r\n"
+#define PCP_ENFILE_FOOTER "\r\n~~~~~ END PCP ENCRYPTED FILE ~~~~~\r\n"
#define PCP_ZFILE_HEADER "~~~~~ BEGIN Z85 ENCODED FILE ~~~~~"
#define PCP_ZFILE_FOOTER "~~~~~ END Z85 ENCODED FILE ~~~~~"
diff --git a/include/pcp/pcpstream.h b/include/pcp/pcpstream.h
index 65e9832..1c03a9e 100644
--- a/include/pcp/pcpstream.h
+++ b/include/pcp/pcpstream.h
@@ -70,6 +70,8 @@ struct _pcp_stream_t {
uint8_t firstread; /**< Internal flag, will be set after first read() */
size_t linewr; /**< Used for Z85 writing, number of chars written on last line */
size_t blocksize; /**< Blocksize used for z85, if requested */
+ uint8_t is_output; /**< marks the stream as output stream */
+ size_t pos; /**< remember i/o position */
};
/** The name used everywhere */
@@ -254,6 +256,15 @@ void ps_setdetermine(Pcpstream *stream, size_t blocksize);
*/
void ps_armor(Pcpstream *stream, size_t blocksize);
+
+/** Disable Z85 encoding for an output stream.
+
+ \param[in] stream The stream object.
+ */
+void ps_unarmor(Pcpstream *stream);
+
+
+
/* read from primary source, decode z85 and out into cache.
if buf != NULL, consider it as the start of encoded data
and remove headers and comments, then continue as normal. */
@@ -280,6 +291,9 @@ void ps_write_encode(Pcpstream *stream, Buffer *dst);
/* really write the buffer z into the output stream */
size_t ps_write_buf(Pcpstream *stream, Buffer *z);
+/* tell if we really reached eof, caching or not. 1=eof, 0=ok */
+int ps_left(Pcpstream *stream);
+
#endif // HAVE_PCP_PCPSTEAM_H
diff --git a/include/pcp/version.h b/include/pcp/version.h
index ae152e9..b3a672d 100644
--- a/include/pcp/version.h
+++ b/include/pcp/version.h
@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
- Copyright (C) 2013 T.Linden.
+ Copyright (C) 2013-2014 T.v.Dein.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
- You can contact me by mail: .
+ You can contact me by mail: .
*/
@@ -25,7 +25,7 @@
#define PCP_VERSION_MAJOR 0
#define PCP_VERSION_MINOR 2
-#define PCP_VERSION_PATCH 1
+#define PCP_VERSION_PATCH 2
#define PCP_MAKE_VERSION(major, minor, patch) \
((major) * 10000 + (minor) * 100 + (patch))
diff --git a/libpcp/buffer.c b/libpcp/buffer.c
index 4b5510a..5cae71e 100644
--- a/libpcp/buffer.c
+++ b/libpcp/buffer.c
@@ -125,11 +125,16 @@ byte *buffer_get(Buffer *b) {
}
size_t buffer_get_chunk(Buffer *b, void *buf, size_t len) {
- if(len > b->end - b->offset || len == 0) {
+ if(len > b->end - b->offset) {
fatal("[buffer %s] attempt to read %ld bytes data from buffer with %ld bytes left at offset %ld\n",
b->name, len, b->end - b->offset, b->offset);
return 0;
}
+ else if(len == 0) {
+ /* FIXME: check how this happens */
+ return 0;
+ }
+
memcpy(buf, b->buf + b->offset, len);
b->offset += len;
@@ -137,11 +142,15 @@ size_t buffer_get_chunk(Buffer *b, void *buf, size_t len) {
}
size_t buffer_get_chunk_tobuf(Buffer *b, Buffer *dst, size_t len) {
- if(len > b->end - b->offset || len == 0) {
+ if(len > b->end - b->offset) {
fatal("[buffer %s] attempt to read %ld bytes data from buffer with %ld bytes left at offset %ld\n",
b->name, len, b->end - b->offset, b->offset);
return 0;
}
+ else if(len == 0) {
+ /* FIXME: check how this happens */
+ return 0;
+ }
buffer_resize(dst, len);
memcpy(dst->buf+buffer_size(dst), b->buf + b->offset, len);
diff --git a/libpcp/crypto.c b/libpcp/crypto.c
index a928d1a..ec66b4f 100644
--- a/libpcp/crypto.c
+++ b/libpcp/crypto.c
@@ -178,7 +178,7 @@ size_t pcp_decrypt_stream(Pcpstream *in, Pcpstream* out, pcp_key_t *s, byte *sym
nrec = recmatch = self = 0;
- if(ps_tell(in) == 1) {
+ if(ps_tell(in) > 1) {
/* header has already been determined outside the lib */
if(symkey != NULL)
self = 1;
@@ -233,7 +233,7 @@ size_t pcp_decrypt_stream(Pcpstream *in, Pcpstream* out, pcp_key_t *s, byte *sym
for(nrec=0; nrecdetermine = 1;
stream->blocksize = blocksize + (5 - (blocksize % 5));
if(stream->cache == NULL) {
- stream->cache = buffer_new(32, "Pcpstreamcache");
- stream->next = buffer_new(32, "Pcpstreamcachenext");
+ stream->cache = buffer_new(32, "Pcpstreamcachedetermine");
+ stream->next = buffer_new(32, "Pcpstreamcachenextdetermin");
}
}
@@ -75,6 +75,10 @@ void ps_armor(Pcpstream *stream, size_t blocksize) {
}
}
+void ps_unarmor(Pcpstream *stream) {
+ stream->armor = 0;
+}
+
size_t ps_read_raw(Pcpstream *stream, void *buf, size_t readbytes) {
size_t gotbytes = 0;
@@ -107,18 +111,28 @@ size_t ps_read_raw(Pcpstream *stream, void *buf, size_t readbytes) {
/* return readbytes from cache. if it is more than left in the cache
fetch (and decode) the next chunk, append it to cache and return from
that */
+
size_t ps_read_cached(Pcpstream *stream, void *buf, size_t readbytes) {
- if(buffer_left(stream->cache) <= readbytes && buffer_left(stream->cache) > 0 && readbytes <= stream->blocksize) {
+ /*
+ fprintf(stderr, "%ld <= %ld && %ld <= %ld\n",
+ readbytes, buffer_left(stream->cache), readbytes, stream->blocksize) ;
+
+ fprintf(stderr, "%d == 1 && %ld >= %ld\n", ps_left(stream), readbytes, buffer_left(stream->cache));
+ */
+ if(readbytes <= buffer_left(stream->cache) && readbytes <= stream->blocksize) {
/* enough left in current cache */
return buffer_get_chunk(stream->cache, buf, readbytes);
}
+ else if(ps_left(stream) == 1 && readbytes >= buffer_left(stream->cache)) {
+ return buffer_get_chunk(stream->cache, buf, buffer_left(stream->cache));
+ }
else {
/* request for chunk larger than what we've got in the cache */
- Buffer *tmp = buffer_new(stream->blocksize, "Pcpreadover");
+ Buffer *tmp = buffer_new(stream->blocksize, "Pcpreadchunktmp");
if( buffer_left(stream->cache) > 0) {
/* put the remaining cache into dest */
- buffer_get_chunk_tobuf(stream->cache, tmp, buffer_size(stream->cache));
+ buffer_get_chunk_tobuf(stream->cache, tmp, buffer_left(stream->cache));
}
/* how much left to fetch */
@@ -170,26 +184,31 @@ size_t ps_read_cached(Pcpstream *stream, void *buf, size_t readbytes) {
/* read and decode the next chunk and put it into stream->next */
size_t ps_read_next(Pcpstream *stream) {
- if(stream->armor == 1) {
- /* fetch next chunk and decode it */
- return ps_read_decode(stream, NULL, 0);
- }
- else {
- /* unencoded source, fetch as is */
- void *buf = ucmalloc(stream->blocksize);
- size_t got = ps_read_raw(stream, buf, stream->blocksize);
- buffer_add(stream->next, buf, got);
- return got;
+ if(ps_left(stream) == 0) {
+ if(stream->armor == 1) {
+ /* fetch next chunk and decode it */
+ return ps_read_decode(stream, NULL, 0);
+ }
+ else {
+ /* unencoded source, fetch as is */
+ void *buf = ucmalloc(stream->blocksize);
+ size_t got = ps_read_raw(stream, buf, stream->blocksize);
+ buffer_add(stream->next, buf, got);
+ return got;
+ }
}
+ else
+ return 0;
}
size_t ps_read(Pcpstream *stream, void *buf, size_t readbytes) {
+ size_t got = 0;
if(stream->cache == NULL) {
- return ps_read_raw(stream, buf, readbytes);
+ got = ps_read_raw(stream, buf, readbytes);
}
else if(buffer_size(stream->cache) > 0) {
/* use cache */
- return ps_read_cached(stream, buf, readbytes);
+ got = ps_read_cached(stream, buf, readbytes);
}
else {
/* no cache yet */
@@ -197,18 +216,22 @@ size_t ps_read(Pcpstream *stream, void *buf, size_t readbytes) {
/* fetch the first chunk into the cache and decode, if required,
recursively call ps_read() again to return the apropriate data */
ps_determine(stream);
- return ps_read(stream, buf, readbytes);
+ got = ps_read(stream, buf, readbytes);
}
else if(stream->armor == 1) {
/* z85 encoding has already been determined, therefore the cache
is now filled, use it then */
- return ps_read_cached(stream, buf, readbytes);
+ got = ps_read_cached(stream, buf, readbytes);
}
else {
/* read directly from source */
- return ps_read_raw(stream, buf, readbytes);
+ got = ps_read_raw(stream, buf, readbytes);
}
}
+
+ stream->pos += got;
+
+ return got;
}
void ps_determine(Pcpstream *stream) {
@@ -285,6 +308,8 @@ size_t ps_read_decode(Pcpstream *stream, void *buf, size_t bufsize) {
size_t ps_write(Pcpstream *stream, void *buf, size_t writebytes) {
Buffer *z = buffer_new(32, "Pcpwritetemp");
+ stream->is_output = 1;
+
if(stream->armor == 1) {
if(buffer_size(stream->cache) + writebytes < stream->blocksize) {
/* just put it into the cache and done with it */
@@ -350,13 +375,16 @@ size_t ps_write(Pcpstream *stream, void *buf, size_t writebytes) {
/* actually put it out */
size_t outsize = ps_write_buf(stream, z);
buffer_free(z);
- return outsize;
+ writebytes = outsize;
}
else {
/* buf has been put into the cache only, no writing required */
buffer_free(z);
- return writebytes;
}
+
+ stream->pos += writebytes;
+
+ return writebytes;
}
void ps_write_encode(Pcpstream *stream, Buffer *dst) {
@@ -372,14 +400,14 @@ void ps_write_encode(Pcpstream *stream, Buffer *dst) {
}
/* z85 encode */
- zlen = (buffer_size(stream->cache) * 5 / 4);
+ zlen = (buffer_size(stream->cache) * 5 / 4) + 1;
char *z85 = ucmalloc(zlen);
zmq_z85_encode(z85, buffer_get(stream->cache), buffer_size(stream->cache));
/* add newlines */
pos = stream->linewr;
- for(i=0; i= 71) {
buffer_add8(dst, '\r');
buffer_add8(dst, '\n');
@@ -445,7 +473,11 @@ size_t ps_print(Pcpstream *stream, const char * fmt, ...) {
void ps_close(Pcpstream *stream) {
if(stream->cache != NULL) {
- assert(buffer_left(stream->cache) == 0); /* there's something left in the cache, call ps_finish() */
+ if(stream->is_output == 1) {
+ if(buffer_left(stream->cache) != 0)
+ buffer_info(stream->cache);
+ assert(buffer_left(stream->cache) == 0); /* there's something left in the cache, call ps_finish() */
+ }
buffer_free(stream->cache);
}
@@ -467,25 +499,31 @@ void ps_close(Pcpstream *stream) {
int ps_end(Pcpstream *stream) {
/* simulate open file if there's still something in the cache */
if(stream->cache != NULL)
- if(buffer_left(stream->cache) > 0)
+ if(buffer_left(stream->cache) > 0) {
return 0;
+ }
return stream->eof;
}
+int ps_left(Pcpstream *stream) {
+ /* used internally to determine if we reached end of source */
+ if(stream->is_buffer) {
+ if(buffer_left(stream->b) == 0)
+ return 1;
+ else
+ return 0;
+ }
+ else {
+ return feof(stream->fd);
+ }
+}
+
int ps_err(Pcpstream *stream) {
return stream->err;
}
size_t ps_tell(Pcpstream *stream) {
- if(stream->is_buffer) {
- if(stream->b->end > stream->b->offset)
- return stream->b->end; /* write buffer */
- else
- return stream->b->offset; /* read buffer */
- }
- else {
- return ftell(stream->fd);
- }
+ return stream->pos;
}
Buffer *ps_buffer(Pcpstream *stream) {
diff --git a/man/details.pod b/man/details.pod
index fc6c77b..58f19cc 100644
--- a/man/details.pod
+++ b/man/details.pod
@@ -448,6 +448,9 @@ secret key, R is the recipient list, L is the number of recipients,
T is the filetype header, I is a block of input with a size
of 32k, N is a nonce (new per block) and S the symmetric key.
+The encrypted output maybe Z85 encoded. In this case the Z85
+encoding will be done blockwise with blocks of 16k bytes. The
+decoded content inside will be as described above.
=head2 SIGNATURE FORMAT
@@ -552,6 +555,19 @@ secret signing key and S the symmetric key.
=head2 Z85 ENCODING
B uses Z85 to encode exported keys and armored signatures.
+Comments in encoded files are surrounded by the tilde character.
+We're using the tilde because it's not part of the Z85 base
+charset. Sample:
+
+ ~~~ Header ~~~
+ ~ Version: 1 ~
+ 246ge]+yn={}]Xi3*N3Xx34Y^0rz:r.5j
+ v#6Sh/m3XKwy?VlA+h8ks]9:kVj{D[fd7]NA]T-(ne+xo!W5X5-gIUWqM
+ ~~~ Footer ~~~
+
+Multiple tildes can be used as long as their number is uneven.
+
+This is a proprietary PCP extension.
=head3 Z85 BACKGROUND
diff --git a/man/options.pod b/man/options.pod
index d8b941b..10688ce 100644
--- a/man/options.pod
+++ b/man/options.pod
@@ -75,6 +75,7 @@
If none of -i or -r has been given, encrypt
the message symetrically. This is the same
as -m (self-encryption mode).
+ Add -z to ascii armor the output using Z85.
-m --encrypt-me Sym-Encrypt a message. Specify -I and/or
-O for input/output file. You will be asked
for a passphrase. No key material will
@@ -112,9 +113,9 @@
If used with encryption or singing operation
encode its output. Otherwise encode a plain
file. Use -I and -O respectively, otherwise it
- stdin/stdout.
+ uses stdin/stdout.
-Z --z85-decode Decode (dearmor) something from Z85 encoding.
Use -I and -O respectively, otherwise it
- stdin/stdout
+ uses stdin/stdout
diff --git a/src/encryption.c b/src/encryption.c
index 070c5ae..ad13695 100644
--- a/src/encryption.c
+++ b/src/encryption.c
@@ -1,7 +1,7 @@
/*
This file is part of Pretty Curved Privacy (pcp1).
- Copyright (C) 2013 T.Linden.
+ Copyright (C) 2013-2014 T.v.Dein.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
- You can contact me by mail: .
+ You can contact me by mail: .
*/
@@ -48,9 +48,15 @@ int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, i
}
}
+ Pcpstream *pin = ps_new_file(in);
+ Pcpstream *pout = ps_new_file(out);
+
+ ps_setdetermine(pin, PCP_BLOCK_SIZE/2);
+
/* determine crypt mode */
- fread(&head, 1, 1, in);
- if(!feof(in) && !ferror(in)) {
+ ps_read(pin, &head, 1);
+
+ if(!ps_end(pin) && !ps_err(pin)) {
if(head == PCP_SYM_CIPHER) {
/* symetric mode */
byte *salt = ucmalloc(90);
@@ -110,9 +116,6 @@ int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, i
goto errde3;
}
- Pcpstream *pin = ps_new_file(in);
- Pcpstream *pout = ps_new_file(out);
-
if(symkey == NULL)
dlen = pcp_decrypt_stream(pin, pout, secret, NULL, verify);
else
@@ -136,7 +139,7 @@ int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, i
-int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt) {
+int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt, int armor) {
FILE *in = NULL;
FILE *out = NULL;
pcp_pubkey_t *pubhash = NULL; /* FIXME: add free() */
@@ -266,14 +269,26 @@ int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *rec
Pcpstream *pin = ps_new_file(in);
Pcpstream *pout = ps_new_file(out);
+ if(armor == 1) {
+ ps_print(pout, PCP_ENFILE_HEADER);
+ ps_armor(pout, PCP_BLOCK_SIZE/2);
+ }
+
if(self == 1)
clen = pcp_encrypt_stream_sym(pin, pout, symkey, 0, NULL);
else
clen = pcp_encrypt_stream(pin, pout, secret, pubhash, signcrypt);
- ps_close(pin);
+ if(armor == 1) {
+ ps_finish(pout);
+ ps_unarmor(pout);
+ ps_print(pout, PCP_ENFILE_FOOTER);
+ }
+
ps_close(pout);
+ ps_close(pin);
+
if(clen > 0) {
if(id == NULL && recipient == NULL)
fprintf(stderr, "Encrypted %ld bytes symetrically\n", clen);
diff --git a/src/encryption.h b/src/encryption.h
index e757412..535fa7e 100644
--- a/src/encryption.h
+++ b/src/encryption.h
@@ -38,6 +38,6 @@
#include "pcpstream.h"
int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, int verify);
-int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt);
+int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt, int armor);
#endif /* _HAVE_ENCRYPTION_H */
diff --git a/src/pcp.c b/src/pcp.c
index 7effe14..b2819e5 100644
--- a/src/pcp.c
+++ b/src/pcp.c
@@ -474,11 +474,11 @@ int main (int argc, char **argv) {
if(useid == 1 && userec == 0) {
/* one dst, FIXME: make id a list as well */
id = pcp_normalize_id(keyid);
- pcpencrypt(id, infile, outfile, xpass, NULL, signcrypt);
+ pcpencrypt(id, infile, outfile, xpass, NULL, signcrypt, armor);
}
else if(useid == 0 && userec == 1) {
/* multiple dst */
- pcpencrypt(NULL, infile, outfile, xpass, recipient, signcrypt);
+ pcpencrypt(NULL, infile, outfile, xpass, recipient, signcrypt, armor);
}
else {
/* -i and -r specified */
@@ -558,7 +558,7 @@ int main (int argc, char **argv) {
break;
case PCP_MODE_ENCRYPT_ME:
- pcpencrypt(NULL, infile, outfile, xpass, NULL, 0);
+ pcpencrypt(NULL, infile, outfile, xpass, NULL, 0, armor);
break;
case PCP_MODE_TEXT:
diff --git a/tests/pipetest.c b/tests/pipetest.c
index 605c28e..1621793 100644
--- a/tests/pipetest.c
+++ b/tests/pipetest.c
@@ -1,6 +1,7 @@
#include
#include
#include
+#include
#include
diff --git a/tests/unittests.cfg b/tests/unittests.cfg
index c35e94c..880ad91 100644
--- a/tests/unittests.cfg
+++ b/tests/unittests.cfg
@@ -35,6 +35,40 @@ include keys.cfg
+
+
+ md5 = `md5 -q ../COPYING`
+ cmd = ./pipetest 8 8 e < ../COPYING | ./pipetest 8 8 d | md5 -q
+ expect = /$md5/
+
+
+ md5 = `md5 -q ../COPYING`
+ cmd = ./pipetest 8 16 e < ../COPYING | ./pipetest 8 16 d | md5 -q
+ expect = /$md5/
+
+
+ md5 = `md5 -q ../COPYING`
+ cmd = ./pipetest 16 8 e < ../COPYING | ./pipetest 16 8 d | md5 -q
+ expect = /$md5/
+
+
+ md5 = `md5 -q ../COPYING`
+ cmd = ./pipetest 64 32 e < ../COPYING | ./pipetest 64 32 d | md5 -q
+ expect = /$md5/
+
+
+ md5 = `md5 -q ../COPYING`
+ cmd = ./pipetest 32 64 e < ../COPYING | ./pipetest 32 64 d | md5 -q
+ expect = /$md5/
+
+
+ md5 = `md5 -q ../COPYING`
+ cmd = ./pipetest 64 64 e < ../COPYING | ./pipetest 64 64 d | md5 -q
+ expect = /$md5/
+
+
+
+
cmd = $pcp -h
expect = /export/
diff --git a/tests/unittests.pl b/tests/unittests.pl
index 09f4e16..cf39818 100755
--- a/tests/unittests.pl
+++ b/tests/unittests.pl
@@ -73,6 +73,10 @@ sub runtest {
my($cfg, $name) = @_;
my($in, $out, $error, $timeout);
+ foreach my $key (keys %{$cfg}) {
+ $cfg->{$key} =~ s/\`([^\`]*)\`/my $result = `$1`; chomp $result; $result/ge;
+ }
+
if (exists $cfg->{prepare}) {
print STDERR " executing prepare command: $cfg->{prepare}\n" if ($verbose);
if ($cfg->{prepare} =~ />/) {