Coverage Report

Created: 2026-10-28 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/util/mail-raw.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "istream.h"
5
#include "istream-seekable.h"
6
#include "str.h"
7
#include "str-sanitize.h"
8
#include "strescape.h"
9
#include "safe-mkstemp.h"
10
#include "path-util.h"
11
#include "message-address.h"
12
#include "mbox-from.h"
13
#include "raw-storage.h"
14
#include "mail-storage-service.h"
15
#include "mail-namespace.h"
16
#include "master-service.h"
17
#include "master-service-settings.h"
18
#include "settings-parser.h"
19
#include "mail-raw.h"
20
21
#include <stdio.h>
22
#include <unistd.h>
23
#include <fcntl.h>
24
#include <pwd.h>
25
26
/*
27
 * Configuration
28
 */
29
30
0
#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
31
32
/* After buffer grows larger than this, create a temporary file to /tmp
33
   where to read the mail. */
34
0
#define MAIL_MAX_MEMORY_BUFFER (1024*128)
35
36
static const char *wanted_headers[] = {
37
  "From", "Message-ID", "Subject", "Return-Path",
38
  NULL
39
};
40
41
/*
42
 * Global data
43
 */
44
45
struct mail_raw_user {
46
  struct mail_namespace *ns;
47
  struct mail_user *mail_user;
48
};
49
50
/*
51
 * Raw mail implementation
52
 */
53
54
static int seekable_fd_callback(const char **path_r, void *context)
55
0
{
56
0
  struct mail_user *ruser = (struct mail_user *)context;
57
0
  string_t *path;
58
0
  int fd;
59
60
0
  path = t_str_new(128);
61
0
  mail_user_set_get_temp_prefix(path, ruser->set);
62
0
  fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
63
0
  if (fd == -1) {
64
0
    i_error("safe_mkstemp(%s) failed: %m", str_c(path));
65
0
    return -1;
66
0
  }
67
68
  /* we just want the fd, unlink it */
69
0
  if (i_unlink(str_c(path)) < 0) {
70
    /* shouldn't happen.. */
71
0
    i_close_fd(&fd);
72
0
    return -1;
73
0
  }
74
75
0
  *path_r = str_c(path);
76
0
  return fd;
77
0
}
78
79
static struct istream *
80
mail_raw_create_stream(struct mail_user *ruser, int fd, time_t *mtime_r,
81
           const char **sender)
82
0
{
83
0
  struct istream *input, *input2, *input_list[2];
84
0
  const unsigned char *data;
85
0
  size_t i, size;
86
0
  int ret, tz;
87
0
  char *env_sender = NULL;
88
89
0
  *mtime_r = (time_t)-1;
90
0
  fd_set_nonblock(fd, FALSE);
91
92
0
  input = i_stream_create_fd(fd, 4096);
93
0
  input->blocking = TRUE;
94
  /* If input begins with a From-line, drop it */
95
0
  ret = i_stream_read_bytes(input, &data, &size, 5);
96
0
  if (ret > 0 && memcmp(data, "From ", 5) == 0) {
97
    /* Skip until the first LF */
98
0
    i_stream_skip(input, 5);
99
0
    while (i_stream_read_more(input, &data, &size) > 0) {
100
0
      for (i = 0; i < size; i++) {
101
0
        if (data[i] == '\n')
102
0
          break;
103
0
      }
104
0
      if (i != size) {
105
0
        (void)mbox_from_parse(data, i, mtime_r,
106
0
                  &tz, &env_sender);
107
0
        i_stream_skip(input, i + 1);
108
0
        break;
109
0
      }
110
0
      i_stream_skip(input, size);
111
0
    }
112
0
  }
113
114
0
  if (env_sender != NULL && sender != NULL)
115
0
    *sender = t_strdup(env_sender);
116
0
  i_free(env_sender);
117
118
0
  if (input->v_offset == 0) {
119
0
    input2 = input;
120
0
    i_stream_ref(input2);
121
0
  } else {
122
0
    input2 = i_stream_create_limit(input, (uoff_t)-1);
123
0
  }
124
0
  i_stream_unref(&input);
125
126
0
  input_list[0] = input2; input_list[1] = NULL;
127
0
  input = i_stream_create_seekable(input_list, MAIL_MAX_MEMORY_BUFFER,
128
0
           seekable_fd_callback, ruser);
129
0
  i_stream_unref(&input2);
130
0
  return input;
131
0
}
132
133
/*
134
 * Init/Deinit
135
 */
136
137
struct mail_user *mail_raw_user_create(struct mail_user *mail_user)
138
0
{
139
0
  struct mail_storage_service_ctx *storage_service =
140
0
    mail_storage_service_user_get_service_ctx(
141
0
      mail_user->service_user);
142
0
  struct settings_instance *set_instance =
143
0
    mail_storage_service_user_get_settings_instance(mail_user->service_user);
144
0
  return raw_storage_create_from_set(storage_service, set_instance);
145
0
}
146
147
/*
148
 * Open raw mail data
149
 */
150
151
static struct mail_raw *
152
mail_raw_create(struct mail_user *ruser, struct istream *input,
153
    const char *mailfile, const char *sender, time_t mtime)
154
0
{
155
0
  struct mail_raw *mailr;
156
0
  struct mailbox_header_lookup_ctx *headers_ctx;
157
0
  const char *envelope_sender, *error;
158
0
  int ret;
159
160
0
  if (mailfile != NULL && *mailfile != '/') {
161
0
    if (t_abspath(mailfile, &mailfile, &error) < 0)
162
0
      i_fatal("t_abspath(%s) failed: %s", mailfile, error);
163
0
  }
164
165
0
  mailr = i_new(struct mail_raw, 1);
166
167
0
  envelope_sender = sender != NULL ? sender : DEFAULT_ENVELOPE_SENDER;
168
0
  if (mailfile == NULL) {
169
0
    ret = raw_mailbox_alloc_stream(ruser, input, mtime,
170
0
                 envelope_sender, &mailr->box);
171
0
  } else {
172
0
    ret = raw_mailbox_alloc_path(ruser, mailfile, (time_t)-1,
173
0
               envelope_sender, &mailr->box);
174
0
  }
175
176
0
  if (ret < 0) {
177
0
    if (mailfile == NULL) {
178
0
      i_fatal("Can't open delivery mail as raw: %s",
179
0
        mailbox_get_last_internal_error(
180
0
          mailr->box, NULL));
181
0
    } else {
182
0
      i_fatal("Can't open delivery mail as raw (file=%s): %s",
183
0
        mailfile,
184
0
        mailbox_get_last_internal_error(
185
0
          mailr->box, NULL));
186
0
    }
187
0
  }
188
189
0
  mailr->trans = mailbox_transaction_begin(mailr->box, 0, __func__);
190
0
  headers_ctx = mailbox_header_lookup_init(mailr->box, wanted_headers);
191
0
  mailr->mail = mail_alloc(mailr->trans, 0, headers_ctx);
192
0
  mailbox_header_lookup_unref(&headers_ctx);
193
0
  mail_set_seq(mailr->mail, 1);
194
195
0
  return mailr;
196
0
}
197
198
struct mail_raw *
199
mail_raw_open_stream(struct mail_user *ruser, struct istream *input)
200
0
{
201
0
  struct mail_raw *mailr;
202
203
0
  i_assert(input->seekable);
204
0
  i_stream_set_name(input, "data");
205
0
  mailr = mail_raw_create(ruser, input, NULL, NULL, (time_t)-1);
206
207
0
  return mailr;
208
0
}
209
210
struct mail_raw *
211
mail_raw_open_data(struct mail_user *ruser, string_t *mail_data)
212
0
{
213
0
  struct mail_raw *mailr;
214
0
  struct istream *input;
215
216
0
  input = i_stream_create_copy_from_data(str_data(mail_data),
217
0
                 str_len(mail_data));
218
219
0
  mailr = mail_raw_open_stream(ruser, input);
220
221
0
  i_stream_unref(&input);
222
0
  return mailr;
223
0
}
224
225
struct mail_raw *mail_raw_open_file(struct mail_user *ruser, const char *path)
226
0
{
227
0
  struct mail_raw *mailr;
228
0
  struct istream *input = NULL;
229
0
  time_t mtime = (time_t)-1;
230
0
  const char *sender = NULL;
231
232
0
  if (path == NULL || strcmp(path, "-") == 0) {
233
0
    path = NULL;
234
0
    input = mail_raw_create_stream(ruser, 0, &mtime, &sender);
235
0
  }
236
237
0
  mailr = mail_raw_create(ruser, input, path, sender, mtime);
238
0
  i_stream_unref(&input);
239
240
0
  return mailr;
241
0
}
242
243
void mail_raw_close(struct mail_raw **mailr)
244
0
{
245
0
  mail_free(&(*mailr)->mail);
246
0
  mailbox_transaction_rollback(&(*mailr)->trans);
247
0
  mailbox_free(&(*mailr)->box);
248
249
0
  i_free(*mailr);
250
  *mailr = NULL;
251
0
}