Coverage Report

Created: 2025-11-11 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-mail/message-size.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "istream.h"
5
#include "message-parser.h"
6
#include "message-size.h"
7
8
int message_get_header_size(struct istream *input, struct message_size *hdr,
9
          bool *has_nuls_r)
10
0
{
11
0
  const unsigned char *msg;
12
0
  size_t i, size, startpos, missing_cr_count;
13
0
  int ret;
14
15
0
  memset(hdr, 0, sizeof(struct message_size));
16
0
  *has_nuls_r = FALSE;
17
18
0
  missing_cr_count = 0; startpos = 0;
19
0
  while ((ret = i_stream_read_bytes(input, &msg, &size, startpos + 1)) > 0) {
20
0
    for (i = startpos; i < size; i++) {
21
0
      if (msg[i] != '\n') {
22
0
        if (msg[i] == '\0')
23
0
          *has_nuls_r = TRUE;
24
0
        continue;
25
0
      }
26
27
0
      hdr->lines++;
28
0
      if (i == 0 || msg[i-1] != '\r') {
29
        /* missing CR */
30
0
        missing_cr_count++;
31
0
      }
32
33
0
      if (i == 0 || (i == 1 && msg[i-1] == '\r')) {
34
        /* no headers at all */
35
0
        break;
36
0
      }
37
38
0
      if ((i > 0 && msg[i-1] == '\n') ||
39
0
          (i > 1 && msg[i-2] == '\n' && msg[i-1] == '\r')) {
40
        /* \n\n or \n\r\n - end of headers */
41
0
        break;
42
0
      }
43
0
    }
44
45
0
    if (i < size) {
46
      /* end of header */
47
0
      startpos = i+1;
48
0
      break;
49
0
    }
50
51
    /* leave the last two characters, they may be \r\n */
52
0
    startpos = size == 1 ? 1 : 2;
53
0
    i_stream_skip(input, i - startpos);
54
55
0
    hdr->physical_size += i - startpos;
56
0
  }
57
0
  i_assert(ret == -1 || ret > 0);
58
59
0
  ret = input->stream_errno != 0 ? -1 : 0;
60
0
  i_stream_skip(input, startpos);
61
0
  hdr->physical_size += startpos;
62
63
0
  hdr->virtual_size = hdr->physical_size + missing_cr_count;
64
0
  i_assert(hdr->virtual_size >= hdr->physical_size);
65
0
  return ret;
66
0
}
67
68
int message_get_body_size(struct istream *input, struct message_size *body,
69
        bool *has_nuls_r)
70
0
{
71
0
  const unsigned char *msg;
72
0
  size_t i, size, missing_cr_count;
73
0
  int ret;
74
75
0
  memset(body, 0, sizeof(struct message_size));
76
0
  *has_nuls_r = FALSE;
77
78
0
  missing_cr_count = 0;
79
0
  if ((ret = i_stream_read_more(input, &msg, &size)) <= 0) {
80
0
    i_assert(ret == -1);
81
0
    return ret < 0 && input->stream_errno != 0 ? -1 : 0;
82
0
  }
83
84
0
  if (msg[0] == '\n')
85
0
    missing_cr_count++;
86
87
0
  do {
88
0
    for (i = 1; i < size; i++) {
89
0
      if (msg[i] > '\n')
90
0
        continue;
91
92
0
      if (msg[i] == '\n') {
93
0
        if (msg[i-1] != '\r') {
94
          /* missing CR */
95
0
          missing_cr_count++;
96
0
        }
97
98
        /* increase after making sure we didn't break
99
           at virtual \r */
100
0
        body->lines++;
101
0
      } else if (msg[i] == '\0') {
102
0
        *has_nuls_r = TRUE;
103
0
      }
104
0
    }
105
106
    /* leave the last character, it may be \r */
107
0
    i_stream_skip(input, i - 1);
108
0
    body->physical_size += i - 1;
109
0
  } while ((ret = i_stream_read_bytes(input, &msg, &size, 2)) > 0);
110
0
  i_assert(ret == -1);
111
112
0
  ret = input->stream_errno != 0 ? -1 : 0;
113
114
0
  i_stream_skip(input, 1);
115
0
  body->physical_size++;
116
117
0
  body->virtual_size = body->physical_size + missing_cr_count;
118
0
  i_assert(body->virtual_size >= body->physical_size);
119
0
  return ret;
120
0
}
121
122
void message_size_add(struct message_size *dest,
123
          const struct message_size *src)
124
570k
{
125
570k
  dest->virtual_size += src->virtual_size;
126
570k
  dest->physical_size += src->physical_size;
127
570k
  dest->lines += src->lines;
128
570k
}
129
130
int message_skip_virtual(struct istream *input, uoff_t virtual_skip,
131
       bool *last_virtual_cr_r)
132
0
{
133
0
  const unsigned char *msg;
134
0
  size_t i, size;
135
0
  bool cr_skipped = FALSE;
136
0
  int ret;
137
138
0
  *last_virtual_cr_r = FALSE;
139
0
  if (virtual_skip == 0)
140
0
    return 0;
141
142
0
  while ((ret = i_stream_read_bytes(input, &msg, &size, 1)) > 0) {
143
0
    size = I_MIN(virtual_skip, size);
144
0
    const unsigned char *p = memchr(msg, '\n', size);
145
0
    if (p == NULL) {
146
0
      i_stream_skip(input, size);
147
0
      virtual_skip -= size;
148
0
      if (virtual_skip == 0)
149
0
        return 0;
150
0
      continue;
151
0
    }
152
0
    i = p - msg;
153
0
    virtual_skip -= i + 1;
154
155
    /* LF */
156
0
    if ((i == 0 && !cr_skipped) ||
157
0
        (i > 0 && msg[i-1] != '\r')) {
158
0
      if (virtual_skip == 0) {
159
        /* CR/LF boundary */
160
0
        *last_virtual_cr_r = TRUE;
161
0
      } else {
162
0
        virtual_skip--;
163
0
        i++;
164
0
      }
165
0
    } else {
166
0
      i++;
167
0
    }
168
0
    i_stream_skip(input, i);
169
0
    if (virtual_skip == 0)
170
0
      return 0;
171
172
0
    i_assert(i > 0);
173
0
    cr_skipped = msg[i-1] == '\r';
174
0
  }
175
0
  i_assert(ret == -1);
176
0
  return input->stream_errno == 0 ? 0 : -1;
177
0
}