]>
jfr.im git - irc/evilnet/x3.git/blob - src/mail-sendmail.c
1 /* mail-common.c - mail sending utilities
2 * Copyright 2002-2004, 2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 #include "mail-common.c"
23 #ifdef HAVE_SYS_WAIT_H
27 /* This function sends the given "paragraph" as flowed text, as
28 * defined in RFC 2646. It lets us only worry about line wrapping
29 * here, and not in the code that generates mail.
32 send_flowed_text(FILE *where
, const char *para
)
34 const char *eol
= strchr(para
, '\n');
38 /* Do we need to space-stuff the line? */
39 if ((*para
== ' ') || (*para
== '>') || !strncmp(para
, "From ", 5)) {
45 /* How much can we put on this line? */
46 if (!eol
&& (strlen(para
) < (80 - shift
))) {
47 /* End of paragraph; can put on one line. */
51 } else if (eol
&& (eol
< para
+ (80 - shift
))) {
52 /* Newline inside paragraph, no need to wrap. */
53 fprintf(where
, "%.*s\n", (int)(eol
- para
), para
);
57 /* Need to wrap. Where's the last space in the line? */
58 for (pos
=72-shift
; pos
&& (para
[pos
] != ' '); pos
--) ;
59 /* If we didn't find a space, look ahead instead. */
60 if (pos
== 0) pos
= strcspn(para
, " \n");
61 fprintf(where
, "%.*s\n", pos
+1, para
);
64 if (eol
&& (eol
< para
)) eol
= strchr(para
, '\n');
69 mail_send(struct userNode
*from
, struct handle_info
*to
, const char *subject
, const char *body
, int first_time
)
73 int infds
[2], outfds
[2];
77 /* Grab some config items first. */
78 str
= conf_get_data("mail/enable", RECDB_QSTRING
);
79 if (!str
|| !enabled_string(str
))
81 fromaddr
= conf_get_data("mail/from_address", RECDB_QSTRING
);
83 /* How this works: We fork, and the child tries to send the mail.
84 * It does this by setting up a pipe pair, and forking again (the
85 * grandchild exec()'s the mailer program). The mid-level child
86 * sends the text to the grandchild's stdin, and then logs the
92 log_module(MAIN_LOG
, LOG_ERROR
, "sendmail() to %s couldn't fork(): %s (%d)", to
->email_addr
, strerror(errno
), errno
);
94 } else if (child
> 0) {
97 /* Reset the SIGCHLD signal handler to the default. */
98 memset(&sv
, 0, sizeof(sv
));
99 sigemptyset(&sv
.sa_mask
);
100 sv
.sa_handler
= SIG_DFL
;
101 sigaction(SIGCHLD
, &sv
, NULL
);
102 /* We're in a child now; must _exit() to die properly. */
103 if (pipe(infds
) < 0) {
104 log_module(MAIN_LOG
, LOG_ERROR
, "sendmail() child to %s couldn't pipe(infds): %s (%d)", to
->email_addr
, strerror(errno
), errno
);
107 if (pipe(outfds
) < 0) {
108 log_module(MAIN_LOG
, LOG_ERROR
, "sendmail() child to %s couldn't pipe(outfds): %s (%d)", to
->email_addr
, strerror(errno
), errno
);
113 log_module(MAIN_LOG
, LOG_ERROR
, "sendmail() child to %s couldn't fork(): %s (%d)", to
->email_addr
, strerror(errno
), errno
);
115 } else if (child
> 0) {
116 /* Mid-level child; get ready to send the mail. */
117 FILE *out
= fdopen(infds
[1], "w");
118 struct string_list
*extras
;
122 /* Close the end of pipes we do not use. */
126 /* Content type? (format=flowed is a standard for plain text
127 * that lets the receiver reconstruct paragraphs, defined in
128 * RFC 2646. See comment above send_flowed_text() for more.)
130 if (!(str
= conf_get_data("mail/charset", RECDB_QSTRING
))) str
= "us-ascii";
131 fprintf(out
, "Content-Type: text/plain; charset=%s; format=flowed\n", str
);
133 /* Send From, To and Subject headers */
134 if (!fromaddr
) fromaddr
= "admin@poorly.configured.network";
135 fprintf(out
, "From: %s <%s>\n", from
->nick
, fromaddr
);
136 fprintf(out
, "To: \"%s\" <%s>\n", to
->handle
, to
->email_addr
);
137 fprintf(out
, "Subject: %s\n", subject
);
139 /* Do we have any "extra" headers to send? */
140 extras
= conf_get_data("mail/extra_headers", RECDB_STRING_LIST
);
142 for (nn
=0; nn
<extras
->used
; nn
++) {
143 fputs(extras
->list
[nn
], out
);
149 fputs("\n", out
); /* terminate headers */
150 extras
= conf_get_data((first_time
?"mail/body_prefix_first":"mail/body_prefix"), RECDB_STRING_LIST
);
152 for (nn
=0; nn
<extras
->used
; nn
++) {
153 send_flowed_text(out
, extras
->list
[nn
]);
157 send_flowed_text(out
, body
);
158 extras
= conf_get_data((first_time
?"mail/body_suffix_first":"mail/body_suffix"), RECDB_STRING_LIST
);
161 for (nn
=0; nn
<extras
->used
; nn
++)
162 send_flowed_text(out
, extras
->list
[nn
]);
165 /* Close file (sending mail) and check for return code */
169 rv
= wait4(child
, &res
, 0, NULL
);
170 } while ((rv
== -1) && (errno
== EINTR
));
172 /* accept the wait() result */
174 log_module(MAIN_LOG
, LOG_ERROR
, "sendmail() child to %s: Bad wait() return code %d: %s (%d)", to
->email_addr
, rv
, strerror(errno
), errno
);
178 log_module(MAIN_LOG
, LOG_ERROR
, "sendmail() grandchild to %s: Exited with code %d", to
->email_addr
, res
);
181 log_module(MAIN_LOG
, LOG_INFO
, "sendmail() sent email to %s <%s>: %s", to
->handle
, to
->email_addr
, subject
);
185 /* Grandchild; dup2 the fds and exec the mailer. */
186 const char *argv
[10], *mpath
;
187 unsigned int argc
= 0;
189 /* Close the end of pipes we do not use. */
193 dup2(infds
[0], STDIN_FILENO
);
194 dup2(outfds
[1], STDOUT_FILENO
);
195 mpath
= conf_get_data("mail/mailer", RECDB_QSTRING
);
196 if (!mpath
) mpath
= "/usr/sbin/sendmail";
197 argv
[argc
++] = mpath
;
200 argv
[argc
++] = fromaddr
;
202 argv
[argc
++] = to
->email_addr
;
204 if (execv(mpath
, (char**)argv
) < 0) {
205 log_module(MAIN_LOG
, LOG_ERROR
, "sendmail() grandchild to %s couldn't execv(): %s (%d)", to
->email_addr
, strerror(errno
), errno
);