/src/dovecot/src/lib-mail/istream-attachment-connector.c
Line | Count | Source |
1 | | /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "array.h" |
5 | | #include "istream.h" |
6 | | #include "istream-concat.h" |
7 | | #include "istream-sized.h" |
8 | | #include "istream-base64.h" |
9 | | #include "istream-attachment-connector.h" |
10 | | |
11 | | struct istream_attachment_connector { |
12 | | pool_t pool; |
13 | | struct istream *base_input; |
14 | | uoff_t base_input_offset, msg_size; |
15 | | |
16 | | uoff_t encoded_offset; |
17 | | ARRAY(struct istream *) streams; |
18 | | }; |
19 | | |
20 | | struct istream_attachment_connector * |
21 | | istream_attachment_connector_begin(struct istream *base_input, uoff_t msg_size) |
22 | 0 | { |
23 | 0 | struct istream_attachment_connector *conn; |
24 | 0 | pool_t pool; |
25 | |
|
26 | 0 | pool = pool_alloconly_create("istream-attachment-connector", 1024); |
27 | 0 | conn = p_new(pool, struct istream_attachment_connector, 1); |
28 | 0 | conn->pool = pool; |
29 | 0 | conn->base_input = base_input; |
30 | 0 | conn->base_input_offset = base_input->v_offset; |
31 | 0 | conn->msg_size = msg_size; |
32 | 0 | p_array_init(&conn->streams, pool, 8); |
33 | 0 | i_stream_ref(conn->base_input); |
34 | 0 | return conn; |
35 | 0 | } |
36 | | |
37 | | int istream_attachment_connector_add(struct istream_attachment_connector *conn, |
38 | | struct istream *decoded_input, |
39 | | uoff_t start_offset, uoff_t encoded_size, |
40 | | unsigned int base64_blocks_per_line, |
41 | | bool base64_have_crlf, |
42 | | const char **error_r) |
43 | 0 | { |
44 | 0 | struct istream *input, *input2; |
45 | 0 | uoff_t base_prefix_size; |
46 | |
|
47 | 0 | if (start_offset < conn->encoded_offset) { |
48 | 0 | *error_r = t_strdup_printf( |
49 | 0 | "Attachment %s points before the previous attachment " |
50 | 0 | "(%"PRIuUOFF_T" < %"PRIuUOFF_T")", |
51 | 0 | i_stream_get_name(decoded_input), |
52 | 0 | start_offset, conn->encoded_offset); |
53 | 0 | return -1; |
54 | 0 | } |
55 | 0 | base_prefix_size = start_offset - conn->encoded_offset; |
56 | 0 | if (start_offset + encoded_size > conn->msg_size) { |
57 | 0 | *error_r = t_strdup_printf( |
58 | 0 | "Attachment %s points outside message " |
59 | 0 | "(%"PRIuUOFF_T" + %"PRIuUOFF_T" > %"PRIuUOFF_T")", |
60 | 0 | i_stream_get_name(decoded_input), |
61 | 0 | start_offset, encoded_size, |
62 | 0 | conn->msg_size); |
63 | 0 | return -1; |
64 | 0 | } |
65 | | |
66 | 0 | if (base_prefix_size > 0) { |
67 | | /* add a part of the base message before the attachment */ |
68 | 0 | input = i_stream_create_min_sized_range(conn->base_input, |
69 | 0 | conn->base_input_offset, base_prefix_size); |
70 | 0 | i_stream_set_name(input, t_strdup_printf("%s middle", |
71 | 0 | i_stream_get_name(conn->base_input))); |
72 | 0 | array_push_back(&conn->streams, &input); |
73 | 0 | conn->base_input_offset += base_prefix_size; |
74 | 0 | conn->encoded_offset += base_prefix_size; |
75 | 0 | } |
76 | 0 | conn->encoded_offset += encoded_size; |
77 | |
|
78 | 0 | if (base64_blocks_per_line == 0) { |
79 | 0 | input = decoded_input; |
80 | 0 | i_stream_ref(input); |
81 | 0 | } else { |
82 | 0 | input = i_stream_create_base64_encoder(decoded_input, |
83 | 0 | base64_blocks_per_line*4, |
84 | 0 | base64_have_crlf); |
85 | 0 | i_stream_set_name(input, t_strdup_printf("%s[base64:%u b/l%s]", |
86 | 0 | i_stream_get_name(decoded_input), |
87 | 0 | base64_blocks_per_line, |
88 | 0 | base64_have_crlf ? ",crlf" : "")); |
89 | 0 | } |
90 | 0 | input2 = i_stream_create_sized(input, encoded_size); |
91 | 0 | array_push_back(&conn->streams, &input2); |
92 | 0 | i_stream_unref(&input); |
93 | 0 | return 0; |
94 | 0 | } |
95 | | |
96 | | static void |
97 | | istream_attachment_connector_free(struct istream_attachment_connector *conn) |
98 | 0 | { |
99 | 0 | struct istream *stream; |
100 | |
|
101 | 0 | array_foreach_elem(&conn->streams, stream) |
102 | 0 | i_stream_unref(&stream); |
103 | 0 | i_stream_unref(&conn->base_input); |
104 | 0 | pool_unref(&conn->pool); |
105 | 0 | } |
106 | | |
107 | | struct istream * |
108 | | istream_attachment_connector_finish(struct istream_attachment_connector **_conn) |
109 | 0 | { |
110 | 0 | struct istream_attachment_connector *conn = *_conn; |
111 | 0 | struct istream **inputs, *input; |
112 | 0 | uoff_t trailer_size; |
113 | |
|
114 | 0 | *_conn = NULL; |
115 | |
|
116 | 0 | if (conn->base_input_offset != conn->msg_size) { |
117 | 0 | i_assert(conn->base_input_offset < conn->msg_size); |
118 | | |
119 | 0 | if (conn->msg_size != UOFF_T_MAX) { |
120 | 0 | trailer_size = conn->msg_size - conn->encoded_offset; |
121 | 0 | input = i_stream_create_sized_range(conn->base_input, |
122 | 0 | conn->base_input_offset, |
123 | 0 | trailer_size); |
124 | 0 | i_stream_set_name(input, t_strdup_printf( |
125 | 0 | "%s trailer", i_stream_get_name(conn->base_input))); |
126 | 0 | } else { |
127 | 0 | input = i_stream_create_range(conn->base_input, |
128 | 0 | conn->base_input_offset, |
129 | 0 | UOFF_T_MAX); |
130 | 0 | } |
131 | 0 | array_push_back(&conn->streams, &input); |
132 | 0 | } |
133 | 0 | array_append_zero(&conn->streams); |
134 | |
|
135 | 0 | inputs = array_front_modifiable(&conn->streams); |
136 | 0 | input = i_stream_create_concat(inputs); |
137 | |
|
138 | 0 | istream_attachment_connector_free(conn); |
139 | 0 | return input; |
140 | 0 | } |
141 | | |
142 | | void istream_attachment_connector_abort(struct istream_attachment_connector **_conn) |
143 | 0 | { |
144 | 0 | struct istream_attachment_connector *conn = *_conn; |
145 | |
|
146 | 0 | *_conn = NULL; |
147 | |
|
148 | 0 | istream_attachment_connector_free(conn); |
149 | 0 | } |