/* * Copyright (c) 2017-2020 [Ribose Inc](https://www.ribose.com). * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #include #else #include "uniwin.h" #endif #include #include #include #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include #include "rnp.h" #include "stream-common.h" #include "types.h" #include "file-utils.h" #include "crypto/mem.h" #include #include bool src_read(pgp_source_t *src, void *buf, size_t len, size_t *readres) { size_t left = len; size_t read; pgp_source_cache_t *cache = src->cache; bool readahead = cache ? cache->readahead : false; if (src->error) { return false; } if (src->eof || (len == 0)) { *readres = 0; return true; } // Do not read more then available if source size is known if (src->knownsize && (src->readb + len > src->size)) { len = src->size - src->readb; left = len; readahead = false; } // Check whether we have cache and there is data inside if (cache && (cache->len > cache->pos)) { read = cache->len - cache->pos; if (read >= len) { memcpy(buf, &cache->buf[cache->pos], len); cache->pos += len; goto finish; } else { memcpy(buf, &cache->buf[cache->pos], read); cache->pos += read; buf = (uint8_t *) buf + read; left = len - read; } } // If we got here then we have empty cache or no cache at all while (left > 0) { if (left > sizeof(cache->buf) || !readahead || !cache) { // If there is no cache or chunk is larger then read directly if (!src->read(src, buf, left, &read)) { src->error = 1; return false; } if (!read) { src->eof = 1; len = len - left; goto finish; } left -= read; buf = (uint8_t *) buf + read; } else { // Try to fill the cache to avoid small reads if (!src->read(src, &cache->buf[0], sizeof(cache->buf), &read)) { src->error = 1; return false; } if (!read) { src->eof = 1; len = len - left; goto finish; } else if (read < left) { memcpy(buf, &cache->buf[0], read); left -= read; buf = (uint8_t *) buf + read; } else { memcpy(buf, &cache->buf[0], left); cache->pos = left; cache->len = read; goto finish; } } } finish: src->readb += len; if (src->knownsize && (src->readb == src->size)) { src->eof = 1; } *readres = len; return true; } bool src_read_eq(pgp_source_t *src, void *buf, size_t len) { size_t res = 0; return src_read(src, buf, len, &res) && (res == len); } bool src_peek(pgp_source_t *src, void *buf, size_t len, size_t *peeked) { pgp_source_cache_t *cache = src->cache; if (src->error) { return false; } if (!cache || (len > sizeof(cache->buf))) { return false; } if (src->eof) { *peeked = 0; return true; } size_t read = 0; bool readahead = cache->readahead; // Do not read more then available if source size is known if (src->knownsize && (src->readb + len > src->size)) { len = src->size - src->readb; readahead = false; } if (cache->len - cache->pos >= len) { if (buf) { memcpy(buf, &cache->buf[cache->pos], len); } *peeked = len; return true; } if (cache->pos > 0) { memmove(&cache->buf[0], &cache->buf[cache->pos], cache->len - cache->pos); cache->len -= cache->pos; cache->pos = 0; } while (cache->len < len) { read = readahead ? sizeof(cache->buf) - cache->len : len - cache->len; if (src->knownsize && (src->readb + read > src->size)) { read = src->size - src->readb; } if (!src->read(src, &cache->buf[cache->len], read, &read)) { src->error = 1; return false; } if (!read) { if (buf) { memcpy(buf, &cache->buf[0], cache->len); } *peeked = cache->len; return true; } cache->len += read; if (cache->len >= len) { if (buf) { memcpy(buf, cache->buf, len); } *peeked = len; return true; } } return false; } bool src_peek_eq(pgp_source_t *src, void *buf, size_t len) { size_t res = 0; return src_peek(src, buf, len, &res) && (res == len); } void src_skip(pgp_source_t *src, size_t len) { if (src->cache && (src->cache->len - src->cache->pos >= len)) { src->readb += len; src->cache->pos += len; return; } size_t res = 0; uint8_t sbuf[16]; if (len < sizeof(sbuf)) { (void) src_read(src, sbuf, len, &res); return; } if (src_eof(src)) { return; } void *buf = calloc(1, std::min((size_t) PGP_INPUT_CACHE_SIZE, len)); if (!buf) { src->error = 1; return; } while (len && !src_eof(src)) { if (!src_read(src, buf, std::min((size_t) PGP_INPUT_CACHE_SIZE, len), &res)) { break; } len -= res; } free(buf); } rnp_result_t src_finish(pgp_source_t *src) { rnp_result_t res = RNP_SUCCESS; if (src->finish) { res = src->finish(src); } return res; } bool src_error(const pgp_source_t *src) { return src->error; } bool src_eof(pgp_source_t *src) { if (src->eof) { return true; } /* Error on stream read is NOT considered as eof. See src_error(). */ uint8_t check; size_t read = 0; return src_peek(src, &check, 1, &read) && (read == 0); } void src_close(pgp_source_t *src) { if (src->close) { src->close(src); } if (src->cache) { free(src->cache); src->cache = NULL; } } bool src_skip_eol(pgp_source_t *src) { uint8_t eol[2]; size_t read; if (!src_peek(src, eol, 2, &read) || !read) { return false; } if (eol[0] == '\n') { src_skip(src, 1); return true; } if ((read == 2) && (eol[0] == '\r') && (eol[1] == '\n')) { src_skip(src, 2); return true; } return false; } bool src_peek_line(pgp_source_t *src, char *buf, size_t len, size_t *readres) { size_t scan_pos = 0; size_t inc = 64; len = len - 1; do { size_t to_peek = scan_pos + inc; to_peek = to_peek > len ? len : to_peek; inc = inc * 2; /* inefficient, each time we again read from the beginning */ if (!src_peek(src, buf, to_peek, readres)) { return false; } /* we continue scanning where we stopped previously */ for (; scan_pos < *readres; scan_pos++) { if (buf[scan_pos] == '\n') { if ((scan_pos > 0) && (buf[scan_pos - 1] == '\r')) { scan_pos--; } buf[scan_pos] = '\0'; *readres = scan_pos; return true; } } if (*readres < to_peek) { return false; } } while (scan_pos < len); return false; } bool init_src_common(pgp_source_t *src, size_t paramsize) { memset(src, 0, sizeof(*src)); src->cache = (pgp_source_cache_t *) calloc(1, sizeof(*src->cache)); if (!src->cache) { RNP_LOG("cache allocation failed"); return false; } src->cache->readahead = true; if (!paramsize) { return true; } src->param = calloc(1, paramsize); if (!src->param) { RNP_LOG("param allocation failed"); free(src->cache); src->cache = NULL; return false; } return true; } typedef struct pgp_source_file_param_t { int fd; } pgp_source_file_param_t; static bool file_src_read(pgp_source_t *src, void *buf, size_t len, size_t *readres) { pgp_source_file_param_t *param = (pgp_source_file_param_t *) src->param; if (!param) { return false; } int64_t rres = read(param->fd, buf, len); if (rres < 0) { return false; } *readres = rres; return true; } static void file_src_close(pgp_source_t *src) { pgp_source_file_param_t *param = (pgp_source_file_param_t *) src->param; if (param) { if (src->type == PGP_STREAM_FILE) { close(param->fd); } free(src->param); src->param = NULL; } } static rnp_result_t init_fd_src(pgp_source_t *src, int fd, uint64_t *size) { if (!init_src_common(src, sizeof(pgp_source_file_param_t))) { return RNP_ERROR_OUT_OF_MEMORY; } pgp_source_file_param_t *param = (pgp_source_file_param_t *) src->param; param->fd = fd; src->read = file_src_read; src->close = file_src_close; src->type = PGP_STREAM_FILE; src->size = size ? *size : 0; src->knownsize = !!size; return RNP_SUCCESS; } rnp_result_t init_file_src(pgp_source_t *src, const char *path) { int fd; struct stat st; if (rnp_stat(path, &st) != 0) { RNP_LOG("can't stat '%s'", path); return RNP_ERROR_READ; } /* read call may succeed on directory depending on OS type */ if (S_ISDIR(st.st_mode)) { RNP_LOG("source is directory"); return RNP_ERROR_BAD_PARAMETERS; } int flags = O_RDONLY; #ifdef HAVE_O_BINARY flags |= O_BINARY; #else #ifdef HAVE__O_BINARY flags |= _O_BINARY; #endif #endif fd = rnp_open(path, flags, 0); if (fd < 0) { RNP_LOG("can't open '%s'", path); return RNP_ERROR_READ; } uint64_t size = st.st_size; rnp_result_t ret = init_fd_src(src, fd, &size); if (ret) { close(fd); } return ret; } rnp_result_t init_stdin_src(pgp_source_t *src) { pgp_source_file_param_t *param; if (!init_src_common(src, sizeof(pgp_source_file_param_t))) { return RNP_ERROR_OUT_OF_MEMORY; } param = (pgp_source_file_param_t *) src->param; param->fd = 0; src->read = file_src_read; src->close = file_src_close; src->type = PGP_STREAM_STDIN; return RNP_SUCCESS; } typedef struct pgp_source_mem_param_t { const void *memory; bool free; size_t len; size_t pos; } pgp_source_mem_param_t; typedef struct pgp_dest_mem_param_t { unsigned maxalloc; unsigned allocated; void * memory; bool free; bool discard_overflow; bool secure; } pgp_dest_mem_param_t; static bool mem_src_read(pgp_source_t *src, void *buf, size_t len, size_t *read) { pgp_source_mem_param_t *param = (pgp_source_mem_param_t *) src->param; if (!param) { return false; } if (len > param->len - param->pos) { len = param->len - param->pos; } memcpy(buf, (uint8_t *) param->memory + param->pos, len); param->pos += len; *read = len; return true; } static void mem_src_close(pgp_source_t *src) { pgp_source_mem_param_t *param = (pgp_source_mem_param_t *) src->param; if (param) { if (param->free) { free((void *) param->memory); } free(src->param); src->param = NULL; } } rnp_result_t init_mem_src(pgp_source_t *src, const void *mem, size_t len, bool free) { if (!mem && len) { return RNP_ERROR_NULL_POINTER; } /* this is actually double buffering, but then src_peek will fail */ if (!init_src_common(src, sizeof(pgp_source_mem_param_t))) { return RNP_ERROR_OUT_OF_MEMORY; } pgp_source_mem_param_t *param = (pgp_source_mem_param_t *) src->param; param->memory = mem; param->len = len; param->pos = 0; param->free = free; src->read = mem_src_read; src->close = mem_src_close; src->finish = NULL; src->size = len; src->knownsize = 1; src->type = PGP_STREAM_MEMORY; return RNP_SUCCESS; } static bool null_src_read(pgp_source_t *src, void *buf, size_t len, size_t *read) { return false; } rnp_result_t init_null_src(pgp_source_t *src) { memset(src, 0, sizeof(*src)); src->read = null_src_read; src->type = PGP_STREAM_NULL; src->error = true; return RNP_SUCCESS; } rnp_result_t read_mem_src(pgp_source_t *src, pgp_source_t *readsrc) { pgp_dest_t dst; rnp_result_t ret; if ((ret = init_mem_dest(&dst, NULL, 0))) { return ret; } if ((ret = dst_write_src(readsrc, &dst))) { goto done; } if ((ret = init_mem_src(src, mem_dest_own_memory(&dst), dst.writeb, true))) { goto done; } ret = RNP_SUCCESS; done: dst_close(&dst, true); return ret; } rnp_result_t file_to_mem_src(pgp_source_t *src, const char *filename) { pgp_source_t fsrc = {}; rnp_result_t res = RNP_ERROR_GENERIC; if ((res = init_file_src(&fsrc, filename))) { return res; } res = read_mem_src(src, &fsrc); src_close(&fsrc); return res; } const void * mem_src_get_memory(pgp_source_t *src, bool own) { if (src->type != PGP_STREAM_MEMORY) { RNP_LOG("wrong function call"); return NULL; } if (!src->param) { return NULL; } pgp_source_mem_param_t *param = (pgp_source_mem_param_t *) src->param; if (own) { param->free = false; } return param->memory; } bool init_dst_common(pgp_dest_t *dst, size_t paramsize) { memset(dst, 0, sizeof(*dst)); dst->werr = RNP_SUCCESS; if (!paramsize) { return true; } /* allocate param */ dst->param = calloc(1, paramsize); if (!dst->param) { RNP_LOG("allocation failed"); } return dst->param; } void dst_write(pgp_dest_t *dst, const void *buf, size_t len) { /* we call write function only if all previous calls succeeded */ if ((len > 0) && (dst->write) && (dst->werr == RNP_SUCCESS)) { /* if cache non-empty and len will overflow it then fill it and write out */ if ((dst->clen > 0) && (dst->clen + len > sizeof(dst->cache))) { memcpy(dst->cache + dst->clen, buf, sizeof(dst->cache) - dst->clen); buf = (uint8_t *) buf + sizeof(dst->cache) - dst->clen; len -= sizeof(dst->cache) - dst->clen; dst->werr = dst->write(dst, dst->cache, sizeof(dst->cache)); dst->writeb += sizeof(dst->cache); dst->clen = 0; if (dst->werr != RNP_SUCCESS) { return; } } /* here everything will fit into the cache or cache is empty */ if (dst->no_cache || (len > sizeof(dst->cache))) { dst->werr = dst->write(dst, buf, len); if (!dst->werr) { dst->writeb += len; } } else { memcpy(dst->cache + dst->clen, buf, len); dst->clen += len; } } } void dst_printf(pgp_dest_t *dst, const char *format, ...) { char buf[2048]; size_t len; va_list ap; va_start(ap, format); len = vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); if (len >= sizeof(buf)) { RNP_LOG("too long dst_printf"); len = sizeof(buf) - 1; } dst_write(dst, buf, len); } void dst_flush(pgp_dest_t *dst) { if ((dst->clen > 0) && (dst->write) && (dst->werr == RNP_SUCCESS)) { dst->werr = dst->write(dst, dst->cache, dst->clen); dst->writeb += dst->clen; dst->clen = 0; } } rnp_result_t dst_finish(pgp_dest_t *dst) { rnp_result_t res = RNP_SUCCESS; if (!dst->finished) { /* flush write cache in the dst */ dst_flush(dst); if (dst->finish) { res = dst->finish(dst); } dst->finished = true; } return res; } void dst_close(pgp_dest_t *dst, bool discard) { if (!discard && !dst->finished) { dst_finish(dst); } if (dst->close) { dst->close(dst, discard); } } typedef struct pgp_dest_file_param_t { int fd; int errcode; bool overwrite; std::string path; } pgp_dest_file_param_t; static rnp_result_t file_dst_write(pgp_dest_t *dst, const void *buf, size_t len) { pgp_dest_file_param_t *param = (pgp_dest_file_param_t *) dst->param; if (!param) { RNP_LOG("wrong param"); return RNP_ERROR_BAD_PARAMETERS; } /* we assyme that blocking I/O is used so everything is written or error received */ ssize_t ret = write(param->fd, buf, len); if (ret < 0) { param->errcode = errno; RNP_LOG("write failed, error %d", param->errcode); return RNP_ERROR_WRITE; } else { param->errcode = 0; return RNP_SUCCESS; } } static void file_dst_close(pgp_dest_t *dst, bool discard) { pgp_dest_file_param_t *param = (pgp_dest_file_param_t *) dst->param; if (!param) { return; } if (dst->type == PGP_STREAM_FILE) { close(param->fd); if (discard) { rnp_unlink(param->path.c_str()); } } delete param; dst->param = NULL; } static rnp_result_t init_fd_dest(pgp_dest_t *dst, int fd, const char *path) { if (!init_dst_common(dst, 0)) { return RNP_ERROR_OUT_OF_MEMORY; } try { std::unique_ptr param(new pgp_dest_file_param_t()); param->path = path; param->fd = fd; dst->param = param.release(); } catch (const std::exception &e) { RNP_LOG("%s", e.what()); return RNP_ERROR_OUT_OF_MEMORY; } dst->write = file_dst_write; dst->close = file_dst_close; dst->type = PGP_STREAM_FILE; return RNP_SUCCESS; } rnp_result_t init_file_dest(pgp_dest_t *dst, const char *path, bool overwrite) { /* check whether file/dir already exists */ struct stat st; if (!rnp_stat(path, &st)) { if (!overwrite) { RNP_LOG("file already exists: '%s'", path); return RNP_ERROR_WRITE; } /* if we are overwriting empty directory then should first remove it */ if (S_ISDIR(st.st_mode)) { if (rmdir(path) == -1) { RNP_LOG("failed to remove directory: error %d", errno); return RNP_ERROR_BAD_PARAMETERS; } } } int flags = O_WRONLY | O_CREAT; flags |= overwrite ? O_TRUNC : O_EXCL; #ifdef HAVE_O_BINARY flags |= O_BINARY; #else #ifdef HAVE__O_BINARY flags |= _O_BINARY; #endif #endif int fd = rnp_open(path, flags, S_IRUSR | S_IWUSR); if (fd < 0) { RNP_LOG("failed to create file '%s'. Error %d.", path, errno); return RNP_ERROR_WRITE; } rnp_result_t res = init_fd_dest(dst, fd, path); if (res) { close(fd); } return res; } #define TMPDST_SUFFIX ".rnp-tmp.XXXXXX" static rnp_result_t file_tmpdst_finish(pgp_dest_t *dst) { pgp_dest_file_param_t *param = (pgp_dest_file_param_t *) dst->param; if (!param) { return RNP_ERROR_BAD_PARAMETERS; } /* close the file */ close(param->fd); param->fd = -1; /* rename the temporary file */ if (param->path.size() < strlen(TMPDST_SUFFIX)) { return RNP_ERROR_BAD_PARAMETERS; } try { /* remove suffix so we have required path */ std::string origpath(param->path.begin(), param->path.end() - strlen(TMPDST_SUFFIX)); /* check if file already exists */ struct stat st; if (!rnp_stat(origpath.c_str(), &st)) { if (!param->overwrite) { RNP_LOG("target path already exists"); return RNP_ERROR_BAD_STATE; } #ifdef _WIN32 /* rename() call on Windows fails if destination exists */ else { rnp_unlink(origpath.c_str()); } #endif /* we should remove dir if overwriting, file will be unlinked in rename call */ if (S_ISDIR(st.st_mode) && rmdir(origpath.c_str())) { RNP_LOG("failed to remove directory"); return RNP_ERROR_BAD_STATE; } } if (rnp_rename(param->path.c_str(), origpath.c_str())) { RNP_LOG("failed to rename temporary path to target file: %s", strerror(errno)); return RNP_ERROR_BAD_STATE; } return RNP_SUCCESS; } catch (const std::exception &e) { RNP_LOG("%s", e.what()); return RNP_ERROR_BAD_STATE; } } static void file_tmpdst_close(pgp_dest_t *dst, bool discard) { pgp_dest_file_param_t *param = (pgp_dest_file_param_t *) dst->param; if (!param) { return; } /* we close file in finish function, except the case when some error occurred */ if (!dst->finished && (dst->type == PGP_STREAM_FILE)) { close(param->fd); if (discard) { rnp_unlink(param->path.c_str()); } } delete param; dst->param = NULL; } rnp_result_t init_tmpfile_dest(pgp_dest_t *dst, const char *path, bool overwrite) { try { std::string tmp = std::string(path) + std::string(TMPDST_SUFFIX); /* make sure tmp.data() is zero-terminated */ tmp.push_back('\0'); #if defined(HAVE_MKSTEMP) && !defined(_WIN32) int fd = mkstemp(&tmp[0]); #else int fd = rnp_mkstemp(&tmp[0]); #endif if (fd < 0) { RNP_LOG("failed to create temporary file with template '%s'. Error %d.", tmp.c_str(), errno); return RNP_ERROR_WRITE; } rnp_result_t res = init_fd_dest(dst, fd, tmp.c_str()); if (res) { close(fd); return res; } } catch (const std::exception &e) { RNP_LOG("%s", e.what()); return RNP_ERROR_BAD_STATE; } /* now let's change some parameters to handle temporary file correctly */ pgp_dest_file_param_t *param = (pgp_dest_file_param_t *) dst->param; param->overwrite = overwrite; dst->finish = file_tmpdst_finish; dst->close = file_tmpdst_close; return RNP_SUCCESS; } rnp_result_t init_stdout_dest(pgp_dest_t *dst) { rnp_result_t res = init_fd_dest(dst, STDOUT_FILENO, ""); if (res) { return res; } dst->type = PGP_STREAM_STDOUT; return RNP_SUCCESS; } static rnp_result_t mem_dst_write(pgp_dest_t *dst, const void *buf, size_t len) { pgp_dest_mem_param_t *param = (pgp_dest_mem_param_t *) dst->param; if (!param) { return RNP_ERROR_BAD_PARAMETERS; } /* checking whether we need to realloc or discard extra bytes */ if (param->discard_overflow && (dst->writeb >= param->allocated)) { return RNP_SUCCESS; } if (param->discard_overflow && (dst->writeb + len > param->allocated)) { len = param->allocated - dst->writeb; } if (dst->writeb + len > param->allocated) { if ((param->maxalloc > 0) && (dst->writeb + len > param->maxalloc)) { RNP_LOG("attempt to alloc more then allowed"); return RNP_ERROR_OUT_OF_MEMORY; } /* round up to the page boundary and do it exponentially */ size_t alloc = ((dst->writeb + len) * 2 + 4095) / 4096 * 4096; if ((param->maxalloc > 0) && (alloc > param->maxalloc)) { alloc = param->maxalloc; } void *newalloc = param->secure ? calloc(1, alloc) : realloc(param->memory, alloc); if (!newalloc) { return RNP_ERROR_OUT_OF_MEMORY; } if (param->secure && param->memory) { memcpy(newalloc, param->memory, dst->writeb); secure_clear(param->memory, dst->writeb); free(param->memory); } param->memory = newalloc; param->allocated = alloc; } memcpy((uint8_t *) param->memory + dst->writeb, buf, len); return RNP_SUCCESS; } static void mem_dst_close(pgp_dest_t *dst, bool discard) { pgp_dest_mem_param_t *param = (pgp_dest_mem_param_t *) dst->param; if (!param) { return; } if (param->free) { if (param->secure) { secure_clear(param->memory, param->allocated); } free(param->memory); } free(param); dst->param = NULL; } rnp_result_t init_mem_dest(pgp_dest_t *dst, void *mem, unsigned len) { pgp_dest_mem_param_t *param; if (!init_dst_common(dst, sizeof(*param))) { return RNP_ERROR_OUT_OF_MEMORY; } param = (pgp_dest_mem_param_t *) dst->param; param->maxalloc = len; param->allocated = mem ? len : 0; param->memory = mem; param->free = !mem; param->secure = false; dst->write = mem_dst_write; dst->close = mem_dst_close; dst->type = PGP_STREAM_MEMORY; dst->werr = RNP_SUCCESS; dst->no_cache = true; return RNP_SUCCESS; } void mem_dest_discard_overflow(pgp_dest_t *dst, bool discard) { if (dst->type != PGP_STREAM_MEMORY) { RNP_LOG("wrong function call"); return; } pgp_dest_mem_param_t *param = (pgp_dest_mem_param_t *) dst->param; if (param) { param->discard_overflow = discard; } } void * mem_dest_get_memory(pgp_dest_t *dst) { if (dst->type != PGP_STREAM_MEMORY) { RNP_LOG("wrong function call"); return NULL; } pgp_dest_mem_param_t *param = (pgp_dest_mem_param_t *) dst->param; if (param) { return param->memory; } return NULL; } void * mem_dest_own_memory(pgp_dest_t *dst) { if (dst->type != PGP_STREAM_MEMORY) { RNP_LOG("wrong function call"); return NULL; } pgp_dest_mem_param_t *param = (pgp_dest_mem_param_t *) dst->param; if (!param) { RNP_LOG("null param"); return NULL; } dst_finish(dst); if (param->free) { if (!dst->writeb) { free(param->memory); param->memory = NULL; return param->memory; } /* it may be larger then required - let's truncate */ void *newalloc = realloc(param->memory, dst->writeb); if (!newalloc) { return NULL; } param->memory = newalloc; param->allocated = dst->writeb; param->free = false; return param->memory; } /* in this case we should copy the memory */ void *res = malloc(dst->writeb); if (res) { memcpy(res, param->memory, dst->writeb); } return res; } void mem_dest_secure_memory(pgp_dest_t *dst, bool secure) { if (!dst || (dst->type != PGP_STREAM_MEMORY)) { RNP_LOG("wrong function call"); return; } pgp_dest_mem_param_t *param = (pgp_dest_mem_param_t *) dst->param; if (param) { param->secure = secure; } } static rnp_result_t null_dst_write(pgp_dest_t *dst, const void *buf, size_t len) { return RNP_SUCCESS; } static void null_dst_close(pgp_dest_t *dst, bool discard) { ; } rnp_result_t init_null_dest(pgp_dest_t *dst) { dst->param = NULL; dst->write = null_dst_write; dst->close = null_dst_close; dst->type = PGP_STREAM_NULL; dst->writeb = 0; dst->clen = 0; dst->werr = RNP_SUCCESS; dst->no_cache = true; return RNP_SUCCESS; } rnp_result_t dst_write_src(pgp_source_t *src, pgp_dest_t *dst, uint64_t limit) { const size_t bufsize = PGP_INPUT_CACHE_SIZE; uint8_t * readbuf = (uint8_t *) malloc(bufsize); if (!readbuf) { return RNP_ERROR_OUT_OF_MEMORY; } rnp_result_t res = RNP_SUCCESS; try { size_t read; uint64_t totalread = 0; while (!src->eof) { if (!src_read(src, readbuf, bufsize, &read)) { res = RNP_ERROR_GENERIC; break; } if (!read) { continue; } totalread += read; if (limit && totalread > limit) { res = RNP_ERROR_GENERIC; break; } if (dst) { dst_write(dst, readbuf, read); if (dst->werr) { RNP_LOG("failed to output data"); res = RNP_ERROR_WRITE; break; } } } } catch (...) { free(readbuf); throw; } free(readbuf); if (res || !dst) { return res; } dst_flush(dst); return dst->werr; }