~ubuntu-branches/ubuntu/wily/dovecot-antispam/wily-proposed

« back to all changes in this revision

Viewing changes to pipe.c

  • Committer: Bazaar Package Importer
  • Author(s): Ron Lee
  • Date: 2011-03-06 07:49:56 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110306074956-j8w77n81y7r0tsfd
Tags: 1.4~rc2-1
Drop all the extra builds again.
All of the backends build into a single plugin now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * mailing backend for dovecot antispam plugin
 
3
 *
 
4
 * Copyright (C) 2007       Johannes Berg <johannes@sipsolutions.net>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License Version 2 as
 
8
 * published by the Free Software Foundation.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
18
 */
 
19
 
 
20
#include <unistd.h>
 
21
#include <stdlib.h>
 
22
#include <sys/wait.h>
 
23
#include <fcntl.h>
 
24
 
 
25
#include "lib.h"
 
26
#include "dict.h"
 
27
#include "mail-storage-private.h"
 
28
#include "ostream.h"
 
29
#include "istream.h"
 
30
 
 
31
#include "antispam-plugin.h"
 
32
 
 
33
static const char *spam_arg = NULL;
 
34
static const char *ham_arg = NULL;
 
35
static const char *pipe_binary = "/usr/sbin/sendmail";
 
36
static const char *tmpdir = "/tmp";
 
37
static char **extra_args = NULL;
 
38
static int extra_args_num = 0;
 
39
 
 
40
static int run_pipe(int mailfd, enum classification wanted)
 
41
{
 
42
        const char *dest;
 
43
        pid_t pid;
 
44
        int status;
 
45
 
 
46
        switch (wanted) {
 
47
        case CLASS_SPAM:
 
48
                dest = spam_arg;
 
49
                break;
 
50
        case CLASS_NOTSPAM:
 
51
                dest = ham_arg;
 
52
                break;
 
53
        }
 
54
 
 
55
        if (!dest)
 
56
                return -1;
 
57
 
 
58
        pid = fork();
 
59
 
 
60
        if (pid == -1)
 
61
                return -1;
 
62
 
 
63
        debug("running mailtrain backend program %s", pipe_binary);
 
64
 
 
65
        if (pid) {
 
66
                if (waitpid(pid, &status, 0) == -1)
 
67
                        return -1;
 
68
                if (!WIFEXITED(status))
 
69
                        return -1;
 
70
                return WEXITSTATUS(status);
 
71
        } else {
 
72
                char **argv;
 
73
                int sz = sizeof(char *) * (2 + extra_args_num + 1);
 
74
                int i, fd;
 
75
 
 
76
                argv = i_malloc(sz);
 
77
                memset(argv, 0, sz);
 
78
 
 
79
                argv[0] = (char *) pipe_binary;
 
80
 
 
81
                for (i = 0; i < extra_args_num; i++)
 
82
                        argv[i + 1] = (char *) extra_args[i];
 
83
 
 
84
                argv[i + 1] = (char *) dest;
 
85
 
 
86
                dup2(mailfd, 0);
 
87
                fd = open("/dev/null", O_WRONLY);
 
88
                dup2(fd, 1);
 
89
                dup2(fd, 2);
 
90
                close(fd);
 
91
                execv(pipe_binary, argv);
 
92
                _exit(1);
 
93
                /* not reached */
 
94
                return -1;
 
95
        }
 
96
}
 
97
 
 
98
struct antispam_transaction_context {
 
99
        char *tmpdir;
 
100
        int count;
 
101
        int tmplen;
 
102
};
 
103
 
 
104
static struct antispam_transaction_context *
 
105
backend_start(struct mailbox *box __attr_unused__)
 
106
{
 
107
        struct antispam_transaction_context *ast;
 
108
        char *tmp;
 
109
 
 
110
        ast = i_new(struct antispam_transaction_context, 1);
 
111
 
 
112
        ast->count = 0;
 
113
 
 
114
        tmp = i_strconcat(tmpdir, "/antispam-mail-XXXXXX", NULL);
 
115
 
 
116
        ast->tmpdir = mkdtemp(tmp);
 
117
        if (!ast->tmpdir)
 
118
                i_free(tmp);
 
119
        else
 
120
                ast->tmplen = strlen(ast->tmpdir);
 
121
 
 
122
        return ast;
 
123
}
 
124
 
 
125
static int process_tmpdir(struct mailbox_transaction_context *ctx,
 
126
                          struct antispam_transaction_context *ast)
 
127
{
 
128
        int cnt = ast->count;
 
129
        int fd;
 
130
        char *buf;
 
131
        enum classification wanted;
 
132
        int rc = 0;
 
133
 
 
134
        t_push();
 
135
 
 
136
        buf = t_malloc(20 + ast->tmplen);
 
137
 
 
138
        while (rc == 0 && cnt > 0) {
 
139
                cnt--;
 
140
                i_snprintf(buf, 20 + ast->tmplen - 1, "%s/%d",
 
141
                           ast->tmpdir, cnt);
 
142
 
 
143
                fd = open(buf, O_RDONLY);
 
144
                read(fd, &wanted, sizeof(wanted));
 
145
 
 
146
                if ((rc = run_pipe(fd, wanted))) {
 
147
                        mail_storage_set_error(ctx->box->storage,
 
148
                                               ME(TEMP)
 
149
                                               "failed to send mail");
 
150
                        debug("run program failed with exit code %d\n", rc);
 
151
                        rc = -1;
 
152
                }
 
153
 
 
154
                close(fd);
 
155
        }
 
156
 
 
157
        t_pop();
 
158
 
 
159
        return rc;
 
160
}
 
161
 
 
162
static void clear_tmpdir(struct antispam_transaction_context *ast)
 
163
{
 
164
        char *buf;
 
165
 
 
166
        t_push();
 
167
 
 
168
        buf = t_malloc(20 + ast->tmplen);
 
169
 
 
170
        while (ast->count > 0) {
 
171
                ast->count--;
 
172
                i_snprintf(buf, 20 + ast->tmplen - 1, "%s/%d",
 
173
                           ast->tmpdir, ast->count);
 
174
                unlink(buf);
 
175
        }
 
176
        rmdir(ast->tmpdir);
 
177
 
 
178
        t_pop();
 
179
}
 
180
 
 
181
static void backend_rollback(struct antispam_transaction_context *ast)
 
182
{
 
183
        if (ast->tmpdir) {
 
184
                /* clear it! */
 
185
                clear_tmpdir(ast);
 
186
                i_free(ast->tmpdir);
 
187
        }
 
188
 
 
189
        i_free(ast);
 
190
}
 
191
 
 
192
static int backend_commit(struct mailbox_transaction_context *ctx,
 
193
                          struct antispam_transaction_context *ast)
 
194
{
 
195
        int ret;
 
196
 
 
197
        if (!ast->tmpdir) {
 
198
                i_free(ast);
 
199
                return 0;
 
200
        }
 
201
 
 
202
        ret = process_tmpdir(ctx, ast);
 
203
 
 
204
        clear_tmpdir(ast);
 
205
 
 
206
        i_free(ast->tmpdir);
 
207
        i_free(ast);
 
208
 
 
209
        return ret;
 
210
}
 
211
 
 
212
static int backend_handle_mail(struct mailbox_transaction_context *t,
 
213
                               struct antispam_transaction_context *ast,
 
214
                               struct mail *mail, enum classification wanted)
 
215
{
 
216
        struct istream *mailstream;
 
217
        struct ostream *outstream;
 
218
        int ret;
 
219
        char *buf;
 
220
        const unsigned char *beginning;
 
221
        size_t size;
 
222
        int fd;
 
223
 
 
224
        if (!ast->tmpdir) {
 
225
                mail_storage_set_error(t->box->storage,
 
226
                                       ME(NOTPOSSIBLE)
 
227
                                       "Failed to initialise temporary dir");
 
228
                return -1;
 
229
        }
 
230
 
 
231
        if (!ham_arg || !spam_arg) {
 
232
                mail_storage_set_error(t->box->storage,
 
233
                                       ME(NOTPOSSIBLE)
 
234
                                       "antispam plugin not configured");
 
235
                return -1;
 
236
        }
 
237
 
 
238
        mailstream = get_mail_stream(mail);
 
239
        if (!mailstream) {
 
240
                mail_storage_set_error(t->box->storage,
 
241
                                       ME(EXPUNGED)
 
242
                                       "Failed to get mail contents");
 
243
                return -1;
 
244
        }
 
245
 
 
246
        t_push();
 
247
 
 
248
        buf = t_malloc(20 + ast->tmplen);
 
249
        i_snprintf(buf, 20 + ast->tmplen - 1, "%s/%d", ast->tmpdir, ast->count);
 
250
 
 
251
        fd = creat(buf, 0600);
 
252
        if (fd < 0) {
 
253
                mail_storage_set_error(t->box->storage,
 
254
                                       ME(NOTPOSSIBLE)
 
255
                                       "Failed to create temporary file");
 
256
                ret = -1;
 
257
                goto out;
 
258
        }
 
259
 
 
260
        ast->count++;
 
261
 
 
262
        outstream = o_stream_create_from_fd(fd, t->box->pool);
 
263
        if (!outstream) {
 
264
                ret = -1;
 
265
                mail_storage_set_error(t->box->storage,
 
266
                                       ME(NOTPOSSIBLE)
 
267
                                       "Failed to stream temporary file");
 
268
                goto out_close;
 
269
        }
 
270
 
 
271
        if (o_stream_send(outstream, &wanted, sizeof(wanted))
 
272
                        != sizeof(wanted)) {
 
273
                ret = -1;
 
274
                mail_storage_set_error(t->box->storage,
 
275
                                       ME(NOTPOSSIBLE)
 
276
                                       "Failed to write marker to temp file");
 
277
                goto failed_to_copy;
 
278
        }
 
279
 
 
280
        if (i_stream_read_data(mailstream, &beginning, &size, 5) < 0 ||
 
281
            size < 5) {
 
282
                ret = -1;
 
283
                mail_storage_set_error(t->box->storage,
 
284
                                       ME(NOTPOSSIBLE)
 
285
                                       "Failed to read mail beginning");
 
286
                goto failed_to_copy;
 
287
        }
 
288
 
 
289
        /* "From "? skip line */
 
290
        if (memcmp("From ", beginning, 5) == 0)
 
291
                i_stream_read_next_line(mailstream);
 
292
 
 
293
        if (o_stream_send_istream(outstream, mailstream) < 0) {
 
294
                ret = -1;
 
295
                mail_storage_set_error(t->box->storage,
 
296
                                       ME(NOTPOSSIBLE)
 
297
                                       "Failed to copy to temporary file");
 
298
                goto failed_to_copy;
 
299
        }
 
300
 
 
301
        ret = 0;
 
302
 
 
303
 failed_to_copy:
 
304
        o_stream_destroy(&outstream);
 
305
 out_close:
 
306
        close(fd);
 
307
 out:
 
308
        t_pop();
 
309
 
 
310
        return ret;
 
311
}
 
312
 
 
313
static void backend_init(pool_t pool __attr_unused__)
 
314
{
 
315
        const char *tmp;
 
316
        int i;
 
317
 
 
318
        tmp = get_setting("PIPE_PROGRAM_SPAM_ARG");
 
319
        if (!tmp)
 
320
                tmp = get_setting("MAIL_SPAM");
 
321
        if (tmp) {
 
322
                spam_arg = tmp;
 
323
                debug("pipe backend spam argument = %s\n", tmp);
 
324
        }
 
325
 
 
326
        tmp = get_setting("PIPE_PROGRAM_NOTSPAM_ARG");
 
327
        if (!tmp)
 
328
                tmp = get_setting("MAIL_NOTSPAM");
 
329
        if (tmp) {
 
330
                ham_arg = tmp;
 
331
                debug("pipe backend not-spam argument = %s\n", tmp);
 
332
        }
 
333
 
 
334
        tmp = get_setting("PIPE_PROGRAM");
 
335
        if (!tmp)
 
336
                tmp = get_setting("MAIL_SENDMAIL");
 
337
        if (tmp) {
 
338
                pipe_binary = tmp;
 
339
                debug("pipe backend program = %s\n", tmp);
 
340
        }
 
341
 
 
342
        tmp = get_setting("PIPE_PROGRAM_ARGS");
 
343
        if (!tmp)
 
344
                tmp = get_setting("MAIL_SENDMAIL_ARGS");
 
345
        if (tmp) {
 
346
                extra_args = p_strsplit(pool, tmp, ";");
 
347
                extra_args_num = str_array_length(
 
348
                                        (const char *const *)extra_args);
 
349
                for (i = 0; i < extra_args_num; i++)
 
350
                        debug("pipe backend program arg[%d] = %s\n",
 
351
                              i, extra_args[i]);
 
352
        }
 
353
 
 
354
        tmp = get_setting("PIPE_TMPDIR");
 
355
        if (!tmp)
 
356
                tmp = get_setting("MAIL_TMPDIR");
 
357
        if (tmp)
 
358
                tmpdir = tmp;
 
359
        debug("pipe backend tmpdir %s\n", tmpdir);
 
360
}
 
361
 
 
362
static void backend_exit(void)
 
363
{
 
364
}
 
365
 
 
366
struct backend pipe_backend = {
 
367
        .init = backend_init,
 
368
        .exit = backend_exit,
 
369
        .handle_mail = backend_handle_mail,
 
370
        .start = backend_start,
 
371
        .rollback = backend_rollback,
 
372
        .commit = backend_commit,
 
373
};