Coverage Report

Created: 2026-04-27 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}