Coverage Report

Created: 2026-04-27 06:39

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