Coverage Report

Created: 2026-01-25 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-imap/imap-envelope.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "llist.h"
5
#include "istream.h"
6
#include "str.h"
7
#include "message-address.h"
8
#include "message-part-data.h"
9
#include "message-parser.h"
10
#include "imap-parser.h"
11
#include "imap-envelope.h"
12
#include "imap-quote.h"
13
14
/*
15
 * Envelope write
16
 */
17
18
static void imap_write_address(string_t *str, struct message_address *addr)
19
9.00k
{
20
9.00k
  if (addr == NULL) {
21
6.89k
    str_append(str, "NIL");
22
6.89k
    return;
23
6.89k
  }
24
25
2.10k
  str_append_c(str, '(');
26
6.20M
  while (addr != NULL) {
27
6.20M
    str_append_c(str, '(');
28
6.20M
    if (addr->name == NULL)
29
267
      str_append(str, "NIL");
30
6.20M
    else {
31
6.20M
      imap_append_string_for_humans(str,
32
6.20M
        (const void *)addr->name, strlen(addr->name));
33
6.20M
    }
34
6.20M
    str_append_c(str, ' ');
35
6.20M
    imap_append_nstring(str, addr->route);
36
6.20M
    str_append_c(str, ' ');
37
6.20M
    imap_append_nstring(str, addr->mailbox);
38
6.20M
    str_append_c(str, ' ');
39
6.20M
    imap_append_nstring(str, addr->domain);
40
6.20M
    str_append_c(str, ')');
41
42
6.20M
    addr = addr->next;
43
6.20M
  }
44
2.10k
  str_append_c(str, ')');
45
2.10k
}
46
47
void imap_envelope_write(struct message_part_envelope *data,
48
           string_t *str)
49
1.50k
{
50
3.00k
#define NVL(str, nullstr) ((str) != NULL ? (str) : (nullstr))
51
1.50k
  static const char *empty_envelope =
52
1.50k
    "NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL";
53
54
1.50k
  if (data == NULL) {
55
0
    str_append(str, empty_envelope);
56
0
    return;
57
0
  }
58
59
1.50k
  imap_append_nstring_nolf(str, data->date);
60
1.50k
  str_append_c(str, ' ');
61
1.50k
  if (data->subject == NULL)
62
225
    str_append(str, "NIL");
63
1.27k
  else {
64
1.27k
    imap_append_string_for_humans(str,
65
1.27k
      (const unsigned char *)data->subject,
66
1.27k
      strlen(data->subject));
67
1.27k
  }
68
69
1.50k
  str_append_c(str, ' ');
70
1.50k
  imap_write_address(str, data->from.head);
71
1.50k
  str_append_c(str, ' ');
72
1.50k
  imap_write_address(str, NVL(data->sender.head, data->from.head));
73
1.50k
  str_append_c(str, ' ');
74
1.50k
  imap_write_address(str, NVL(data->reply_to.head, data->from.head));
75
1.50k
  str_append_c(str, ' ');
76
1.50k
  imap_write_address(str, data->to.head);
77
1.50k
  str_append_c(str, ' ');
78
1.50k
  imap_write_address(str, data->cc.head);
79
1.50k
  str_append_c(str, ' ');
80
1.50k
  imap_write_address(str, data->bcc.head);
81
82
1.50k
  str_append_c(str, ' ');
83
1.50k
  imap_append_nstring_nolf(str, data->in_reply_to);
84
1.50k
  str_append_c(str, ' ');
85
1.50k
  imap_append_nstring_nolf(str, data->message_id);
86
1.50k
}
87
88
/*
89
 * ENVELOPE parsing
90
 */
91
92
static bool
93
imap_envelope_parse_address(const struct imap_arg *arg,
94
  pool_t pool, struct message_address **addr_r)
95
8.26M
{
96
8.26M
  struct message_address *addr;
97
8.26M
  const struct imap_arg *list_args;
98
8.26M
  const char *name, *route, *mailbox, *domain;
99
8.26M
  unsigned int list_count;
100
101
8.26M
  if (!imap_arg_get_list_full(arg, &list_args, &list_count))
102
7
    return FALSE;
103
104
  /* we require 4 arguments, strings or NILs */
105
8.26M
  if (list_count < 4)
106
6
    return FALSE;
107
108
8.26M
  if (!imap_arg_get_nstring(&list_args[0], &name))
109
6
    return FALSE;
110
8.26M
  if (!imap_arg_get_nstring(&list_args[1], &route))
111
1
    return FALSE;
112
8.26M
  if (!imap_arg_get_nstring(&list_args[2], &mailbox))
113
1
    return FALSE;
114
8.26M
  if (!imap_arg_get_nstring(&list_args[3], &domain))
115
3
    return FALSE;
116
117
8.26M
  addr = p_new(pool, struct message_address, 1);
118
8.26M
  addr->name = p_strdup(pool, name);
119
8.26M
  addr->route = p_strdup(pool, route);
120
8.26M
  addr->mailbox = p_strdup(pool, mailbox);
121
8.26M
  addr->domain = p_strdup(pool, domain);
122
123
8.26M
  *addr_r = addr;
124
8.26M
  return TRUE;
125
8.26M
}
126
127
static bool
128
imap_envelope_parse_addresses(const struct imap_arg *arg,
129
  pool_t pool, struct message_address_list *addrs_r)
130
18.3k
{
131
18.3k
  struct message_address *addr;
132
18.3k
  const struct imap_arg *list_args;
133
134
18.3k
  i_zero(addrs_r);
135
18.3k
  if (arg->type == IMAP_ARG_NIL)
136
6.95k
    return TRUE;
137
138
11.3k
  if (!imap_arg_get_list(arg, &list_args))
139
21
    return FALSE;
140
141
11.3k
  addr = NULL;
142
8.28M
  for (; !IMAP_ARG_IS_EOL(list_args); list_args++) {
143
8.26M
    if (!imap_envelope_parse_address
144
8.26M
      (list_args, pool, &addr))
145
24
      return FALSE;
146
8.26M
    DLLIST2_APPEND(&addrs_r->head, &addrs_r->tail, addr);
147
8.26M
  }
148
11.3k
  return TRUE;
149
11.3k
}
150
151
bool imap_envelope_parse_args(const struct imap_arg *args,
152
  pool_t pool, struct message_part_envelope **envlp_r,
153
  const char **error_r)
154
3.08k
{
155
3.08k
  struct message_part_envelope *envlp;
156
157
3.08k
  envlp = p_new(pool, struct message_part_envelope, 1);
158
159
3.08k
  if (!imap_arg_get_nstring(args++, &envlp->date)) {
160
2
    *error_r = "Invalid date field";
161
2
    return FALSE;
162
2
  }
163
3.08k
  envlp->date = p_strdup(pool, envlp->date);
164
165
3.08k
  if (!imap_arg_get_nstring(args++, &envlp->subject)) {
166
1
    *error_r = "Invalid subject field";
167
1
    return FALSE;
168
1
  }
169
3.08k
  envlp->subject = p_strdup(pool, envlp->subject);
170
171
3.08k
  if (!imap_envelope_parse_addresses(args++, pool, &envlp->from)) {
172
26
    *error_r = "Invalid from field";
173
26
    return FALSE;
174
26
  }
175
3.05k
  if (!imap_envelope_parse_addresses(args++, pool, &envlp->sender)) {
176
6
    *error_r = "Invalid sender field";
177
6
    return FALSE;
178
6
  }
179
3.05k
  if (!imap_envelope_parse_addresses(args++, pool, &envlp->reply_to)) {
180
5
    *error_r = "Invalid reply_to field";
181
5
    return FALSE;
182
5
  }
183
3.04k
  if (!imap_envelope_parse_addresses(args++, pool, &envlp->to)) {
184
5
    *error_r = "Invalid to field";
185
5
    return FALSE;
186
5
  }
187
3.04k
  if (!imap_envelope_parse_addresses(args++, pool, &envlp->cc)) {
188
2
    *error_r = "Invalid cc field";
189
2
    return FALSE;
190
2
  }
191
3.04k
  if (!imap_envelope_parse_addresses(args++, pool, &envlp->bcc)) {
192
1
    *error_r = "Invalid bcc field";
193
1
    return FALSE;
194
1
  }
195
196
3.04k
  if (!imap_arg_get_nstring(args++, &envlp->in_reply_to)) {
197
7
    *error_r = "Invalid in_reply_to field";
198
7
    return FALSE;
199
7
  }
200
3.03k
  envlp->in_reply_to = p_strdup(pool, envlp->in_reply_to);
201
202
3.03k
  if (!imap_arg_get_nstring(args++, &envlp->message_id)) {
203
4
    *error_r = "Invalid message_id field";
204
4
    return FALSE;
205
4
  }
206
3.02k
  envlp->message_id = p_strdup(pool, envlp->message_id);
207
208
3.02k
  *envlp_r = envlp;
209
3.02k
  return TRUE;
210
3.03k
}
211
212
bool imap_envelope_parse(const char *envelope,
213
  pool_t pool, struct message_part_envelope **envlp_r,
214
  const char **error_r)
215
0
{
216
0
  struct istream *input;
217
0
  struct imap_parser *parser;
218
0
  const struct imap_arg *args;
219
0
  int ret;
220
0
  bool success;
221
222
0
  input = i_stream_create_from_data(envelope, strlen(envelope));
223
0
  (void)i_stream_read(input);
224
225
0
  parser = imap_parser_create(input, NULL, SIZE_MAX);
226
0
  ret = imap_parser_finish_line(parser, 0,
227
0
              IMAP_PARSE_FLAG_LITERAL_TYPE, &args);
228
0
  if (ret < 0) {
229
0
    *error_r = t_strdup_printf("IMAP parser failed: %s",
230
0
             imap_parser_get_error(parser, NULL));
231
0
    success = FALSE;
232
0
  } else if (ret == 0) {
233
0
    *error_r = "Empty envelope";
234
0
    success = FALSE;
235
0
  } else {
236
0
    success = imap_envelope_parse_args(args, pool, envlp_r, error_r);
237
0
  }
238
239
0
  imap_parser_unref(&parser);
240
0
  i_stream_destroy(&input);
241
0
  return success;
242
0
}