Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2c1b3c1
commit ee502bb
Showing
7 changed files
with
596 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ | ||
|
||
#include "lib.h" | ||
#include "buffer.h" | ||
#include "hex-binary.h" | ||
#include "qp-encoder.h" | ||
#include "istream-private.h" | ||
#include "istream-qp.h" | ||
|
||
struct qp_encoder_istream { | ||
struct istream_private istream; | ||
buffer_t *buf; | ||
struct qp_encoder *qp; | ||
}; | ||
|
||
static void i_stream_qp_encoder_close(struct iostream_private *stream, | ||
bool close_parent) | ||
{ | ||
struct qp_encoder_istream *bstream = | ||
(struct qp_encoder_istream *)stream; | ||
|
||
if (bstream->qp != NULL) | ||
qp_encoder_deinit(&bstream->qp); | ||
if (bstream->buf != NULL) | ||
buffer_free(&bstream->buf); | ||
if (close_parent) | ||
i_stream_close(bstream->istream.parent); | ||
} | ||
|
||
static ssize_t i_stream_qp_encoder_read(struct istream_private *stream) | ||
{ | ||
struct qp_encoder_istream *bstream = | ||
(struct qp_encoder_istream *)stream; | ||
const unsigned char *data; | ||
size_t size; | ||
int ret; | ||
|
||
for(;;) { | ||
if (stream->skip > 0) { | ||
i_assert(stream->skip <= bstream->buf->used); | ||
buffer_delete(bstream->buf, 0, stream->skip); | ||
stream->pos -= stream->skip; | ||
stream->skip = 0; | ||
} | ||
|
||
stream->buffer = bstream->buf->data; | ||
i_assert(stream->pos <= bstream->buf->used); | ||
|
||
if (stream->pos >= bstream->istream.max_buffer_size) { | ||
/* stream buffer still at maximum */ | ||
return -2; | ||
} | ||
|
||
/* if something is already interpolated, return as much of it as | ||
we can */ | ||
if (bstream->buf->used > 0) { | ||
size_t new_pos, bytes; | ||
|
||
/* only return up to max_buffer_size bytes, even when buffer | ||
actually has more, as not to confuse the caller */ | ||
if (bstream->buf->used <= bstream->istream.max_buffer_size) { | ||
new_pos = bstream->buf->used; | ||
if (stream->parent->eof) | ||
stream->istream.eof = TRUE; | ||
} else { | ||
new_pos = bstream->istream.max_buffer_size; | ||
} | ||
|
||
bytes = new_pos - stream->pos; | ||
stream->pos = new_pos; | ||
return (ssize_t)bytes; | ||
} | ||
|
||
/* need to read more input */ | ||
ret = i_stream_read_more(stream->parent, &data, &size); | ||
if (ret == 0) | ||
return ret; | ||
if (size == 0 && ret == -1) { | ||
stream->istream.stream_errno = | ||
stream->parent->stream_errno; | ||
stream->istream.eof = stream->parent->eof; | ||
return ret; | ||
} | ||
qp_encoder_more(bstream->qp, data, size); | ||
i_stream_skip(stream->parent, size); | ||
} | ||
} | ||
|
||
static void | ||
i_stream_qp_encoder_seek(struct istream_private *stream, | ||
uoff_t v_offset, bool mark) | ||
{ | ||
struct qp_encoder_istream *bstream = | ||
(struct qp_encoder_istream *)stream; | ||
|
||
if (v_offset < stream->istream.v_offset) { | ||
/* seeking backwards - go back to beginning and seek | ||
forward from there. */ | ||
stream->parent_expected_offset = stream->parent_start_offset; | ||
stream->skip = stream->pos = 0; | ||
stream->istream.v_offset = 0; | ||
i_stream_seek(stream->parent, 0); | ||
qp_encoder_finish(bstream->qp); | ||
buffer_set_used_size(bstream->buf, 0); | ||
} | ||
i_stream_default_seek_nonseekable(stream, v_offset, mark); | ||
} | ||
|
||
struct istream *i_stream_create_qp_encoder(struct istream *input, | ||
enum qp_encoder_flag flags) | ||
{ | ||
struct qp_encoder_istream *bstream; | ||
|
||
bstream = i_new(struct qp_encoder_istream, 1); | ||
bstream->istream.max_buffer_size = input->real_stream->max_buffer_size; | ||
bstream->buf = buffer_create_dynamic(default_pool, 128); | ||
bstream->qp = qp_encoder_init(bstream->buf, ISTREAM_QP_ENCODER_MAX_LINE_LENGTH, flags); | ||
|
||
bstream->istream.iostream.close = i_stream_qp_encoder_close; | ||
bstream->istream.read = i_stream_qp_encoder_read; | ||
bstream->istream.seek = i_stream_qp_encoder_seek; | ||
|
||
bstream->istream.istream.readable_fd = FALSE; | ||
bstream->istream.istream.blocking = input->blocking; | ||
bstream->istream.istream.seekable = input->seekable; | ||
return i_stream_create(&bstream->istream, input, | ||
i_stream_get_fd(input)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,12 @@ | ||
#ifndef ISTREAM_QP_H | ||
#define ISTREAM_QP_H | ||
|
||
#include "qp-encoder.h" | ||
|
||
#define ISTREAM_QP_ENCODER_MAX_LINE_LENGTH 75 | ||
|
||
struct istream *i_stream_create_qp_decoder(struct istream *input); | ||
struct istream *i_stream_create_qp_encoder(struct istream *input, | ||
enum qp_encoder_flag flags); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ | ||
|
||
#include "lib.h" | ||
#include "str.h" | ||
#include "istream.h" | ||
#include "istream-private.h" | ||
#include "qp-encoder.h" | ||
#include <ctype.h> | ||
|
||
struct qp_encoder { | ||
const char *linebreak; | ||
string_t *dest; | ||
size_t line_len; | ||
size_t max_len; | ||
enum qp_encoder_flag flags; | ||
bool add_header_preamble:1; | ||
bool cr_last:1; | ||
}; | ||
|
||
struct qp_encoder * | ||
qp_encoder_init(string_t *dest, unsigned int max_len, enum qp_encoder_flag flags) | ||
{ | ||
i_assert(max_len > 0); | ||
|
||
if ((flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0 && | ||
(flags & QP_ENCODER_FLAG_BINARY_DATA) != 0) | ||
i_panic("qp encoder cannot do header format with binary data"); | ||
|
||
struct qp_encoder *qp = i_new(struct qp_encoder, 1); | ||
qp->flags = flags; | ||
qp->dest = dest; | ||
qp->max_len = max_len; | ||
|
||
if ((flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0) { | ||
qp->linebreak = "?=\r\n =?utf-8?Q?"; | ||
qp->add_header_preamble = TRUE; | ||
} else { | ||
qp->linebreak = "=\r\n"; | ||
} | ||
return qp; | ||
} | ||
|
||
void qp_encoder_deinit(struct qp_encoder **qp) | ||
{ | ||
i_free(*qp); | ||
} | ||
|
||
static inline void | ||
qp_encode_or_break(struct qp_encoder *qp, unsigned char c) | ||
{ | ||
bool encode = FALSE; | ||
|
||
if ((qp->flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0) { | ||
if (c == ' ') | ||
c = '_'; | ||
else if (c != '\t' && | ||
(c == '?' || c == '_' || c == '=' || c < 33 || c > 126)) | ||
encode = TRUE; | ||
} else if (c != ' ' && c != '\t' && | ||
(c == '=' || c < 33 || c > 126)) { | ||
encode = TRUE; | ||
} | ||
|
||
/* Include terminating = as well */ | ||
if ((c == ' ' || c == '\t') && qp->line_len + 4 >= qp->max_len) { | ||
const char *ptr = strchr(qp->linebreak, '\n'); | ||
str_printfa(qp->dest, "=%02X%s", c, qp->linebreak); | ||
if (ptr != NULL) | ||
qp->line_len = strlen(ptr+1); | ||
else | ||
qp->line_len = 0; | ||
return; | ||
} | ||
|
||
/* Include terminating = as well */ | ||
if (qp->line_len + (encode?4:2) >= qp->max_len) { | ||
str_append(qp->dest, qp->linebreak); | ||
qp->line_len = 0; | ||
} | ||
|
||
if (encode) { | ||
str_printfa(qp->dest, "=%02X", c); | ||
qp->line_len += 3; | ||
} else { | ||
str_append_c(qp->dest, c); | ||
qp->line_len += 1; | ||
} | ||
} | ||
|
||
void qp_encoder_more(struct qp_encoder *qp, const void *_src, size_t src_size) | ||
{ | ||
const unsigned char *src = _src; | ||
i_assert(_src != NULL || src_size == 0); | ||
if (src_size == 0) | ||
return; | ||
if (qp->add_header_preamble) { | ||
size_t used = qp->dest->used; | ||
qp->add_header_preamble = FALSE; | ||
str_append(qp->dest, "=?utf-8?Q?"); | ||
qp->line_len = qp->dest->used - used; | ||
} | ||
for(unsigned int i = 0; i < src_size; i++) { | ||
unsigned char c = src[i]; | ||
/* if input is not binary data and we encounter newline | ||
convert it as crlf, or if the last byte was CR, preserve | ||
CRLF */ | ||
if (c == '\n' && | ||
((qp->flags & (QP_ENCODER_FLAG_BINARY_DATA|QP_ENCODER_FLAG_HEADER_FORMAT)) == 0 || | ||
qp->cr_last)) { | ||
str_append_c(qp->dest, '\r'); | ||
str_append_c(qp->dest, '\n'); | ||
/* reset line length here */ | ||
qp->line_len = 0; | ||
qp->cr_last = FALSE; | ||
continue; | ||
} else if (qp->cr_last) { | ||
qp_encode_or_break(qp, '\r'); | ||
qp->cr_last = FALSE; | ||
} | ||
if (c == '\r') { | ||
qp->cr_last = TRUE; | ||
} else { | ||
qp_encode_or_break(qp, c); | ||
} | ||
} | ||
} | ||
|
||
void qp_encoder_finish(struct qp_encoder *qp) | ||
{ | ||
if (qp->cr_last) | ||
qp_encode_or_break(qp, '\r'); | ||
|
||
if ((qp->flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0 && | ||
!qp->add_header_preamble) | ||
str_append(qp->dest, "?="); | ||
if ((qp->flags & QP_ENCODER_FLAG_HEADER_FORMAT) != 0) | ||
qp->add_header_preamble = TRUE; | ||
qp->line_len = 0; | ||
qp->cr_last = FALSE; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#ifndef QP_ENCODER_H | ||
#define QP_ENCODER_H 1 | ||
|
||
enum qp_encoder_flag { | ||
/* encode spaces as underscores, encode crlfs, adds =?utf-8?q?..?= encapsulation */ | ||
QP_ENCODER_FLAG_HEADER_FORMAT = 0x1, | ||
/* treat input as true binary, no lf => crlf conversion, only CRLF is preserved */ | ||
QP_ENCODER_FLAG_BINARY_DATA = 0x2, | ||
}; | ||
|
||
/* Initialize quoted-printable encoder. Write all the encoded output to dest. */ | ||
struct qp_encoder *qp_encoder_init(string_t *dest, unsigned int max_length, | ||
enum qp_encoder_flag flags); | ||
void qp_encoder_deinit(struct qp_encoder **qp); | ||
|
||
/* Translate more (binary) data into quoted printable. | ||
If QP_ENCODER_FLAG_BINARY_DATA is not set, text is assumed to be in | ||
UTF-8 (but not enforced). No other character sets are supported. */ | ||
void qp_encoder_more(struct qp_encoder *qp, const void *src, size_t src_size); | ||
/* Finish encoding any pending input. | ||
This function also resets the entire encoder state, so the same encoder can | ||
be used to encode more data if wanted. */ | ||
void qp_encoder_finish(struct qp_encoder *qp); | ||
|
||
#endif |
Oops, something went wrong.