/* literal.c - Literal packet filters
* Copyright (C) 2002-2012 Free Software Foundation, Inc.
*
* Author: Timo Schulz
*
* This file is part of OpenCDK.
*
* The OpenCDK library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
/* Duplicate the string @s but strip of possible
relative folder names of it. */
static char *dup_trim_filename(const char *s)
{
char *p = NULL;
p = strrchr(s, '/');
if (!p)
p = strrchr(s, '\\');
if (!p)
return cdk_strdup(s);
return cdk_strdup(p + 1);
}
static cdk_error_t literal_decode(void *data, FILE * in, FILE * out)
{
literal_filter_t *pfx = data;
cdk_stream_t si, so;
cdk_packet_t pkt;
cdk_pkt_literal_t pt;
byte buf[BUFSIZE];
ssize_t nread;
int bufsize;
cdk_error_t rc;
_cdk_log_debug("literal filter: decode\n");
if (!pfx || !in || !out)
return CDK_Inv_Value;
rc = _cdk_stream_fpopen(in, STREAMCTL_READ, &si);
if (rc)
return rc;
cdk_pkt_new(&pkt);
rc = cdk_pkt_read(si, pkt);
if (rc || pkt->pkttype != CDK_PKT_LITERAL) {
cdk_pkt_release(pkt);
cdk_stream_close(si);
return !rc ? CDK_Inv_Packet : rc;
}
rc = _cdk_stream_fpopen(out, STREAMCTL_WRITE, &so);
if (rc) {
cdk_pkt_release(pkt);
cdk_stream_close(si);
return rc;
}
pt = pkt->pkt.literal;
pfx->mode = pt->mode;
if (pfx->filename && pt->namelen > 0) {
/* The name in the literal packet is more authorative. */
cdk_free(pfx->filename);
pfx->filename = dup_trim_filename(pt->name);
} else if (!pfx->filename && pt->namelen > 0)
pfx->filename = dup_trim_filename(pt->name);
else if (!pt->namelen && !pfx->filename && pfx->orig_filename) {
/* In this case, we need to derrive the output file name
from the original name and cut off the OpenPGP extension.
If this is not possible, we return an error. */
if (!stristr(pfx->orig_filename, ".gpg") &&
!stristr(pfx->orig_filename, ".pgp") &&
!stristr(pfx->orig_filename, ".asc")) {
cdk_pkt_release(pkt);
cdk_stream_close(si);
cdk_stream_close(so);
_cdk_log_debug
("literal filter: no file name and no PGP extension\n");
return CDK_Inv_Mode;
}
_cdk_log_debug
("literal filter: derrive file name from original\n");
pfx->filename = dup_trim_filename(pfx->orig_filename);
pfx->filename[strlen(pfx->filename) - 4] = '\0';
}
while (!feof(in)) {
_cdk_log_debug("literal_decode: part on %d size %lu\n",
(int) pfx->blkmode.on,
(unsigned long) pfx->blkmode.size);
if (pfx->blkmode.on)
bufsize = pfx->blkmode.size;
else
bufsize = pt->len < DIM(buf) ? pt->len : DIM(buf);
nread = cdk_stream_read(pt->buf, buf, bufsize);
if (nread == EOF) {
rc = CDK_File_Error;
break;
}
if (pfx->md_initialized)
_gnutls_hash(&pfx->md, buf, nread);
cdk_stream_write(so, buf, nread);
pt->len -= nread;
if (pfx->blkmode.on) {
pfx->blkmode.size =
_cdk_pkt_read_len(in, &pfx->blkmode.on);
if ((ssize_t) pfx->blkmode.size == EOF)
return CDK_Inv_Packet;
}
if (pt->len <= 0 && !pfx->blkmode.on)
break;
}
cdk_stream_close(si);
cdk_stream_close(so);
cdk_pkt_release(pkt);
return rc;
}
static char intmode_to_char(int mode)
{
switch (mode) {
case CDK_LITFMT_BINARY:
return 'b';
case CDK_LITFMT_TEXT:
return 't';
case CDK_LITFMT_UNICODE:
return 'u';
default:
return 'b';
}
return 'b';
}
static cdk_error_t literal_encode(void *data, FILE * in, FILE * out)
{
literal_filter_t *pfx = data;
cdk_pkt_literal_t pt;
cdk_stream_t si;
cdk_packet_t pkt;
size_t filelen;
cdk_error_t rc;
_cdk_log_debug("literal filter: encode\n");
if (!pfx || !in || !out)
return CDK_Inv_Value;
if (!pfx->filename) {
pfx->filename = cdk_strdup("_CONSOLE");
if (!pfx->filename)
return CDK_Out_Of_Core;
}
rc = _cdk_stream_fpopen(in, STREAMCTL_READ, &si);
if (rc)
return rc;
filelen = strlen(pfx->filename);
cdk_pkt_new(&pkt);
pt = pkt->pkt.literal = cdk_calloc(1, sizeof *pt + filelen);
pt->name = (char *) pt + sizeof(*pt);
if (!pt) {
cdk_pkt_release(pkt);
cdk_stream_close(si);
return CDK_Out_Of_Core;
}
memcpy(pt->name, pfx->filename, filelen);
pt->namelen = filelen;
pt->name[pt->namelen] = '\0';
pt->timestamp = (u32) gnutls_time(NULL);
pt->mode = intmode_to_char(pfx->mode);
pt->len = cdk_stream_get_length(si);
pt->buf = si;
pkt->old_ctb = 1;
pkt->pkttype = CDK_PKT_LITERAL;
rc = _cdk_pkt_write_fp(out, pkt);
cdk_pkt_release(pkt);
cdk_stream_close(si);
return rc;
}
int _cdk_filter_literal(void *data, int ctl, FILE * in, FILE * out)
{
if (ctl == STREAMCTL_READ)
return literal_decode(data, in, out);
else if (ctl == STREAMCTL_WRITE)
return literal_encode(data, in, out);
else if (ctl == STREAMCTL_FREE) {
literal_filter_t *pfx = data;
if (pfx) {
_cdk_log_debug("free literal filter\n");
cdk_free(pfx->filename);
pfx->filename = NULL;
cdk_free(pfx->orig_filename);
pfx->orig_filename = NULL;
return 0;
}
}
return CDK_Inv_Mode;
}
static int text_encode(void *data, FILE * in, FILE * out)
{
const char *s;
char buf[2048];
if (!in || !out)
return CDK_Inv_Value;
/* FIXME: This code does not work for very long lines. */
while (!feof(in)) {
/* give space for trim_string \r\n */
s = fgets(buf, DIM(buf) - 3, in);
if (!s)
break;
_cdk_trim_string(buf);
_gnutls_str_cat(buf, sizeof(buf), "\r\n");
fwrite(buf, 1, strlen(buf), out);
}
return 0;
}
static int text_decode(void *data, FILE * in, FILE * out)
{
text_filter_t *tfx = data;
const char *s;
char buf[2048];
if (!tfx || !in || !out)
return CDK_Inv_Value;
while (!feof(in)) {
s = fgets(buf, DIM(buf) - 1, in);
if (!s)
break;
_cdk_trim_string(buf);
fwrite(buf, 1, strlen(buf), out);
fwrite(tfx->lf, 1, strlen(tfx->lf), out);
}
return 0;
}
int _cdk_filter_text(void *data, int ctl, FILE * in, FILE * out)
{
if (ctl == STREAMCTL_READ)
return text_encode(data, in, out);
else if (ctl == STREAMCTL_WRITE)
return text_decode(data, in, out);
else if (ctl == STREAMCTL_FREE) {
text_filter_t *tfx = data;
if (tfx) {
_cdk_log_debug("free text filter\n");
tfx->lf = NULL;
}
}
return CDK_Inv_Mode;
}