1 /* $Id$ */
2 
3 /*
4  * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "fdm.h"
29 #include "fetch.h"
30 
31 void	fetch_stdin_abort(struct account *);
32 u_int	fetch_stdin_total(struct account *);
33 void	fetch_stdin_desc(struct account *, char *, size_t);
34 
35 int	fetch_stdin_state_init(struct account *, struct fetch_ctx *);
36 int	fetch_stdin_state_mail(struct account *, struct fetch_ctx *);
37 int	fetch_stdin_state_exit(struct account *, struct fetch_ctx *);
38 
39 struct fetch fetch_stdin = {
40 	"stdin",
41 	fetch_stdin_state_init,
42 
43 	NULL,
44 	NULL,
45 	fetch_stdin_abort,
46 	NULL,
47 	fetch_stdin_desc
48 };
49 
50 /* Abort; close stdin. */
51 void
fetch_stdin_abort(unused struct account * a)52 fetch_stdin_abort(unused struct account *a)
53 {
54 	close(STDIN_FILENO);
55 }
56 
57 /* Initialise stdin fetch. */
58 int
fetch_stdin_state_init(struct account * a,struct fetch_ctx * fctx)59 fetch_stdin_state_init(struct account *a, struct fetch_ctx *fctx)
60 {
61 	/* Check stdin is valid. */
62 	if (isatty(STDIN_FILENO)) {
63 		log_warnx("%s: stdin is a tty. ignoring", a->name);
64 		return (FETCH_ERROR);
65 	}
66 	if (fcntl(STDIN_FILENO, F_GETFL) == -1) {
67 		if (errno != EBADF)
68 			fatal("fcntl failed");
69 		log_warnx("%s: stdin is invalid", a->name);
70 		return (FETCH_ERROR);
71 	}
72 
73 	fctx->state = fetch_stdin_state_mail;
74 	return (FETCH_AGAIN);
75 }
76 
77 /* Fetch mail from stdin. */
78 int
fetch_stdin_state_mail(struct account * a,struct fetch_ctx * fctx)79 fetch_stdin_state_mail(struct account *a, struct fetch_ctx *fctx)
80 {
81 	struct mail	*m = fctx->mail;
82 	struct io	*io;
83 	char		*line, *cause;
84 
85 	/* Open io for stdin. */
86 	io = io_create(STDIN_FILENO, NULL, IO_LF);
87 	if (conf.debug > 3 && !conf.syslog)
88 		io->dup_fd = STDOUT_FILENO;
89 
90 	/* Initialise the mail. */
91 	if (mail_open(m, IO_BLOCKSIZE) != 0) {
92 		log_warn("%s: failed to create mail", a->name);
93 		goto error;
94 	}
95 	m->size = 0;
96 
97 	/* Add default tags. */
98 	default_tags(&m->tags, NULL);
99 
100 	/* Loop reading the mail. */
101 	for (;;) {
102 		/*
103 		 * There can only be one mail on stdin so reentrancy is
104 		 * irrelevent. This is a good thing since we want to check for
105 		 * close which means end of mail.
106 		 */
107 		switch (io_pollline2(io,
108 		    &line, &fctx->lbuf, &fctx->llen, conf.timeout, &cause)) {
109 		case 0:
110 			/* Normal close is fine. */
111 			goto out;
112 		case -1:
113 			if (errno == EAGAIN)
114 				continue;
115 			log_warnx("%s: %s", a->name, cause);
116 			xfree(cause);
117 			goto error;
118 		}
119 
120 		if (append_line(m, line, strlen(line)) != 0) {
121 			log_warn("%s: failed to resize mail", a->name);
122 			goto error;
123 		}
124 		if (m->size > conf.max_size)
125 			break;
126 	}
127 
128 out:
129 	if (io != NULL)
130 		io_free(io);
131 
132 	fctx->state = fetch_stdin_state_exit;
133 	return (FETCH_MAIL);
134 
135 
136 error:
137 	if (io != NULL)
138 		io_free(io);
139 
140 	return (FETCH_ERROR);
141 }
142 
143 /* Fetch finished; return exit. */
144 int
fetch_stdin_state_exit(unused struct account * a,unused struct fetch_ctx * fctx)145 fetch_stdin_state_exit(unused struct account *a, unused struct fetch_ctx *fctx)
146 {
147 	close(STDIN_FILENO);
148 	return (FETCH_EXIT);
149 }
150 
151 void
fetch_stdin_desc(unused struct account * a,char * buf,size_t len)152 fetch_stdin_desc(unused struct account *a, char *buf, size_t len)
153 {
154 	strlcpy(buf, "stdin", len);
155 }
156