~ubuntu-branches/ubuntu/trusty/dovecot/trusty-updates

« back to all changes in this revision

Viewing changes to src/lib/istream-chain.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (1.15.3) (96.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20140108093549-814nkqdcxfbvgktg
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2003-2013 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "llist.h"
 
5
#include "istream-private.h"
 
6
#include "istream-chain.h"
 
7
 
 
8
struct chain_istream;
 
9
 
 
10
struct istream_chain_link {
 
11
        struct istream_chain_link *prev, *next;
 
12
 
 
13
        struct istream *stream;
 
14
        bool eof;
 
15
};
 
16
 
 
17
struct istream_chain {
 
18
        struct istream_chain_link *head, *tail;
 
19
 
 
20
        struct chain_istream *stream;
 
21
};
 
22
 
 
23
struct chain_istream {
 
24
        struct istream_private istream;
 
25
 
 
26
        size_t prev_stream_left, prev_skip;
 
27
        
 
28
        struct istream_chain chain;
 
29
};
 
30
 
 
31
static void ATTR_NULL(2)
 
32
i_stream_chain_append_internal(struct istream_chain *chain,
 
33
                               struct istream *stream)
 
34
{
 
35
        struct istream_chain_link *link;
 
36
 
 
37
        if (stream == NULL && chain->tail != NULL && chain->tail->stream == NULL)
 
38
                return;
 
39
 
 
40
        link = i_new(struct istream_chain_link, 1);
 
41
        link->stream = stream;
 
42
        link->eof = stream == NULL;
 
43
 
 
44
        if (stream != NULL)
 
45
                i_stream_ref(stream);   
 
46
 
 
47
        if (chain->head == NULL && stream != NULL) {
 
48
                if (chain->stream->istream.max_buffer_size == 0) {
 
49
                        chain->stream->istream.max_buffer_size =
 
50
                                stream->real_stream->max_buffer_size;
 
51
                } else {
 
52
                        i_stream_set_max_buffer_size(stream,
 
53
                                chain->stream->istream.max_buffer_size);
 
54
                }
 
55
        }
 
56
        DLLIST2_APPEND(&chain->head, &chain->tail, link);
 
57
}
 
58
 
 
59
void i_stream_chain_append(struct istream_chain *chain, struct istream *stream)
 
60
{
 
61
        i_stream_chain_append_internal(chain, stream);
 
62
}
 
63
 
 
64
void i_stream_chain_append_eof(struct istream_chain *chain)
 
65
{
 
66
        i_stream_chain_append_internal(chain, NULL);
 
67
}
 
68
 
 
69
static void
 
70
i_stream_chain_set_max_buffer_size(struct iostream_private *stream,
 
71
                                    size_t max_size)
 
72
{
 
73
        struct chain_istream *cstream = (struct chain_istream *)stream;
 
74
        struct istream_chain_link *link = cstream->chain.head;
 
75
 
 
76
        cstream->istream.max_buffer_size = max_size;
 
77
        while (link != NULL) {
 
78
                if (link->stream != NULL)
 
79
                        i_stream_set_max_buffer_size(link->stream, max_size);
 
80
                link = link->next;
 
81
        }
 
82
}
 
83
 
 
84
static void i_stream_chain_destroy(struct iostream_private *stream)
 
85
{
 
86
        struct chain_istream *cstream = (struct chain_istream *)stream;
 
87
        struct istream_chain_link *link = cstream->chain.head;
 
88
 
 
89
        while (link != NULL) {
 
90
                struct istream_chain_link *next = link->next;
 
91
 
 
92
                if (link->stream != NULL)
 
93
                        i_stream_unref(&link->stream);
 
94
                i_free(link);
 
95
                link = next;
 
96
        }
 
97
        i_free(cstream->istream.w_buffer);
 
98
}
 
99
 
 
100
static void i_stream_chain_read_next(struct chain_istream *cstream)
 
101
{
 
102
        struct istream_chain_link *link = cstream->chain.head;
 
103
        struct istream *prev_input;
 
104
        const unsigned char *data;
 
105
        size_t data_size, size, cur_data_pos;
 
106
 
 
107
        i_assert(link != NULL && link->stream != NULL);
 
108
        i_assert(link->stream->eof);
 
109
 
 
110
        prev_input = link->stream;
 
111
        data = i_stream_get_data(prev_input, &data_size);
 
112
 
 
113
        DLLIST2_REMOVE(&cstream->chain.head, &cstream->chain.tail, link);
 
114
        i_free(link);
 
115
 
 
116
        /* a) we have more streams, b) we have EOF, c) we need to wait
 
117
           for more streams */
 
118
        link = cstream->chain.head;
 
119
        if (link != NULL && link->stream != NULL)
 
120
                i_stream_seek(link->stream, 0);
 
121
 
 
122
        if (cstream->prev_stream_left > 0) {
 
123
                /* we've already buffered some of the prev_input. continue
 
124
                   appending the rest to it. */
 
125
                cur_data_pos = cstream->istream.pos -
 
126
                        (cstream->istream.skip + cstream->prev_stream_left);
 
127
                i_assert(cur_data_pos <= data_size);
 
128
                data += cur_data_pos;
 
129
                data_size -= cur_data_pos;
 
130
        } else {
 
131
                cstream->istream.pos = 0;
 
132
                cstream->istream.skip = 0;
 
133
                cstream->prev_stream_left = 0;
 
134
        }
 
135
 
 
136
        /* we already verified that the data size is less than the
 
137
           maximum buffer size */
 
138
        if (data_size > 0) {
 
139
                if (!i_stream_try_alloc(&cstream->istream, data_size, &size))
 
140
                        i_unreached();
 
141
                i_assert(size >= data_size);
 
142
        }
 
143
        memcpy(cstream->istream.w_buffer + cstream->istream.pos,
 
144
               data, data_size);
 
145
        cstream->istream.pos += data_size;
 
146
        cstream->prev_stream_left += data_size;
 
147
 
 
148
        i_stream_skip(prev_input, i_stream_get_data_size(prev_input));
 
149
        i_stream_unref(&prev_input);
 
150
}
 
151
 
 
152
static ssize_t i_stream_chain_read(struct istream_private *stream)
 
153
{
 
154
        struct chain_istream *cstream = (struct chain_istream *)stream;
 
155
        struct istream_chain_link *link = cstream->chain.head;
 
156
        const unsigned char *data;
 
157
        size_t size, data_size, cur_data_pos, new_pos, bytes_skipped;
 
158
        size_t new_bytes_count;
 
159
        ssize_t ret;
 
160
 
 
161
        if (link != NULL && link->eof) {
 
162
                stream->istream.eof = TRUE;
 
163
                return -1;
 
164
        }
 
165
 
 
166
        i_assert(stream->skip >= cstream->prev_skip);
 
167
        bytes_skipped = stream->skip - cstream->prev_skip;
 
168
 
 
169
        if (cstream->prev_stream_left == 0) {
 
170
                /* no need to worry about buffers, skip everything */
 
171
        } else if (bytes_skipped < cstream->prev_stream_left) {
 
172
                /* we're still skipping inside buffer */
 
173
                cstream->prev_stream_left -= bytes_skipped;
 
174
                bytes_skipped = 0;
 
175
        } else {
 
176
                /* done with the buffer */
 
177
                bytes_skipped -= cstream->prev_stream_left;
 
178
                cstream->prev_stream_left = 0;
 
179
        }
 
180
        stream->pos -= bytes_skipped;
 
181
        stream->skip -= bytes_skipped;
 
182
        stream->buffer += bytes_skipped;
 
183
        cstream->prev_skip = stream->skip;
 
184
 
 
185
        if (link == NULL) {
 
186
                i_assert(bytes_skipped == 0);
 
187
                return 0;
 
188
        }
 
189
        i_stream_skip(link->stream, bytes_skipped);
 
190
 
 
191
        i_assert(stream->pos >= stream->skip + cstream->prev_stream_left);
 
192
        cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left);
 
193
 
 
194
        data = i_stream_get_data(link->stream, &data_size);
 
195
        if (data_size > cur_data_pos)
 
196
                ret = 0;
 
197
        else {
 
198
                /* need to read more */
 
199
                i_assert(cur_data_pos == data_size);
 
200
                ret = i_stream_read(link->stream);
 
201
                if (ret == -2 || ret == 0)
 
202
                        return ret;
 
203
 
 
204
                if (ret == -1) {
 
205
                        if (link->stream->stream_errno != 0) {
 
206
                                io_stream_set_error(&stream->iostream,
 
207
                                        "read(%s) failed: %s",
 
208
                                        i_stream_get_name(link->stream),
 
209
                                        i_stream_get_error(link->stream));
 
210
                                stream->istream.stream_errno =
 
211
                                        link->stream->stream_errno;
 
212
                                return -1;
 
213
                        }
 
214
                        /* EOF of this stream, go to next stream */
 
215
                        i_stream_chain_read_next(cstream);
 
216
                        cstream->prev_skip = stream->skip;
 
217
                        return i_stream_chain_read(stream);
 
218
                }
 
219
                /* we read something */
 
220
                data = i_stream_get_data(link->stream, &data_size);
 
221
        }
 
222
 
 
223
        if (cstream->prev_stream_left == 0) {
 
224
                /* we can point directly to the current stream's buffers */
 
225
                stream->buffer = data;
 
226
                stream->pos -= stream->skip;
 
227
                stream->skip = 0;
 
228
                new_pos = data_size;
 
229
        } else if (data_size == cur_data_pos) {
 
230
                /* nothing new read */
 
231
                i_assert(ret == 0 || ret == -1);
 
232
                stream->buffer = stream->w_buffer;
 
233
                new_pos = stream->pos;
 
234
        } else {
 
235
                /* we still have some of the previous stream left. merge the
 
236
                   new data with it. */
 
237
                i_assert(data_size > cur_data_pos);
 
238
                new_bytes_count = data_size - cur_data_pos;
 
239
                if (!i_stream_try_alloc(stream, new_bytes_count, &size)) {
 
240
                        stream->buffer = stream->w_buffer;
 
241
                        return -2;
 
242
                }
 
243
                stream->buffer = stream->w_buffer;
 
244
 
 
245
                if (new_bytes_count > size)
 
246
                        new_bytes_count = size;
 
247
                memcpy(stream->w_buffer + stream->pos,
 
248
                       data + cur_data_pos, new_bytes_count);
 
249
                new_pos = stream->pos + new_bytes_count;
 
250
        }
 
251
 
 
252
        ret = new_pos > stream->pos ? (ssize_t)(new_pos - stream->pos) :
 
253
                (ret == 0 ? 0 : -1);
 
254
        stream->pos = new_pos;
 
255
        cstream->prev_skip = stream->skip;
 
256
        return ret;
 
257
}
 
258
 
 
259
struct istream *i_stream_create_chain(struct istream_chain **chain_r)
 
260
{
 
261
        struct chain_istream *cstream;
 
262
 
 
263
        cstream = i_new(struct chain_istream, 1);
 
264
        cstream->chain.stream = cstream;
 
265
        cstream->istream.max_buffer_size = 256;
 
266
 
 
267
        cstream->istream.iostream.destroy = i_stream_chain_destroy;
 
268
        cstream->istream.iostream.set_max_buffer_size =
 
269
                i_stream_chain_set_max_buffer_size;
 
270
 
 
271
        cstream->istream.read = i_stream_chain_read;
 
272
 
 
273
        cstream->istream.istream.readable_fd = FALSE;
 
274
        cstream->istream.istream.blocking = FALSE;
 
275
        cstream->istream.istream.seekable = FALSE;
 
276
 
 
277
        *chain_r = &cstream->chain;
 
278
        return i_stream_create(&cstream->istream, NULL, -1);
 
279
}