]> jfr.im git - irc/evilnet/x3.git/blame - src/mail-sendmail.c
Rewrote PHP X3 DB parser function sample code as a class and faster code
[irc/evilnet/x3.git] / src / mail-sendmail.c
CommitLineData
1136f709 1/* mail-common.c - mail sending utilities
2 * Copyright 2002-2004, 2007 srvx Development Team
d76ed9a9 3 *
1136f709 4 * This file is part of srvx.
d76ed9a9 5 *
1136f709 6 * srvx is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
be2c97a5 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9 9 * (at your option) any later version.
10 *
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.
15 *
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.
19 */
20
1136f709 21#include "mail-common.c"
d76ed9a9 22
23#ifdef HAVE_SYS_WAIT_H
24#include <sys/wait.h>
25#endif
26
d76ed9a9 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.
30 */
31static void
32send_flowed_text(FILE *where, const char *para)
33{
34 const char *eol = strchr(para, '\n');
35 unsigned int shift;
36
37 while (*para) {
38 /* Do we need to space-stuff the line? */
39 if ((*para == ' ') || (*para == '>') || !strncmp(para, "From ", 5)) {
40 fputc(' ', where);
41 shift = 1;
42 } else {
43 shift = 0;
44 }
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. */
48 fputs(para, where);
49 fputs("\n", where);
50 break;
51 } else if (eol && (eol < para + (80 - shift))) {
52 /* Newline inside paragraph, no need to wrap. */
2f61d1d7 53 fprintf(where, "%.*s\n", (int)(eol - para), para);
d76ed9a9 54 para = eol + 1;
55 } else {
56 int pos;
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);
62 para += pos + 1;
63 }
64 if (eol && (eol < para)) eol = strchr(para, '\n');
65 }
66}
67
68void
1136f709 69mail_send(struct userNode *from, struct handle_info *to, const char *subject, const char *body, int first_time)
d76ed9a9 70{
1136f709 71 struct sigaction sv;
d76ed9a9 72 pid_t child;
73 int infds[2], outfds[2];
1136f709 74 const char *fromaddr;
75 const char *str;
d76ed9a9 76
77 /* Grab some config items first. */
78 str = conf_get_data("mail/enable", RECDB_QSTRING);
79 if (!str || !enabled_string(str))
80 return;
81 fromaddr = conf_get_data("mail/from_address", RECDB_QSTRING);
82
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
87 * success or failure.
88 */
89
90 child = fork();
91 if (child < 0) {
92 log_module(MAIN_LOG, LOG_ERROR, "sendmail() to %s couldn't fork(): %s (%d)", to->email_addr, strerror(errno), errno);
93 return;
94 } else if (child > 0) {
95 return;
96 }
1136f709 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);
d76ed9a9 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);
105 _exit(1);
106 }
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);
109 _exit(1);
110 }
111 child = fork();
112 if (child < 0) {
113 log_module(MAIN_LOG, LOG_ERROR, "sendmail() child to %s couldn't fork(): %s (%d)", to->email_addr, strerror(errno), errno);
114 _exit(1);
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;
119 unsigned int nn;
120 int res, rv;
121
122 /* Close the end of pipes we do not use. */
123 close(infds[0]);
124 close(outfds[1]);
125
126 /* Do we have any "extra" headers to send? */
127 extras = conf_get_data("mail/extra_headers", RECDB_STRING_LIST);
128 if (extras) {
129 for (nn=0; nn<extras->used; nn++) {
130 fputs(extras->list[nn], out);
131 fputs("\n", out);
132 }
133 }
134
135 /* Content type? (format=flowed is a standard for plain text
136 * that lets the receiver reconstruct paragraphs, defined in
137 * RFC 2646. See comment above send_flowed_text() for more.)
138 */
139 if (!(str = conf_get_data("mail/charset", RECDB_QSTRING))) str = "us-ascii";
140 fprintf(out, "Content-Type: text/plain; charset=%s; format=flowed\n", str);
141
142 /* Send From, To and Subject headers */
143 if (!fromaddr) fromaddr = "admin@poorly.configured.network";
144 fprintf(out, "From: %s <%s>\n", from->nick, fromaddr);
145 fprintf(out, "To: \"%s\" <%s>\n", to->handle, to->email_addr);
146 fprintf(out, "Subject: %s\n", subject);
147
148 /* Send mail body */
149 fputs("\n", out); /* terminate headers */
150 extras = conf_get_data((first_time?"mail/body_prefix_first":"mail/body_prefix"), RECDB_STRING_LIST);
151 if (extras) {
152 for (nn=0; nn<extras->used; nn++) {
153 send_flowed_text(out, extras->list[nn]);
154 }
155 fputs("\n", out);
156 }
157 send_flowed_text(out, body);
158 extras = conf_get_data((first_time?"mail/body_suffix_first":"mail/body_suffix"), RECDB_STRING_LIST);
159 if (extras) {
160 fputs("\n", out);
161 for (nn=0; nn<extras->used; nn++)
162 send_flowed_text(out, extras->list[nn]);
163 }
164
165 /* Close file (sending mail) and check for return code */
166 fflush(out);
167 fclose(out);
168 do {
169 rv = wait4(child, &res, 0, NULL);
170 } while ((rv == -1) && (errno == EINTR));
171 if (rv == child) {
172 /* accept the wait() result */
173 } else {
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);
175 _exit(1);
176 }
177 if (res) {
178 log_module(MAIN_LOG, LOG_ERROR, "sendmail() grandchild to %s: Exited with code %d", to->email_addr, res);
179 _exit(1);
180 } else {
181 log_module(MAIN_LOG, LOG_INFO, "sendmail() sent email to %s <%s>: %s", to->handle, to->email_addr, subject);
182 }
183 _exit(0);
184 } else {
185 /* Grandchild; dup2 the fds and exec the mailer. */
186 const char *argv[10], *mpath;
187 unsigned int argc = 0;
188
189 /* Close the end of pipes we do not use. */
190 close(infds[1]);
191 close(outfds[0]);
192
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;
198 if (fromaddr) {
199 argv[argc++] = "-f";
200 argv[argc++] = fromaddr;
201 }
202 argv[argc++] = to->email_addr;
203 argv[argc++] = NULL;
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);
206 }
207 _exit(1);
208 }
209}
210
d76ed9a9 211void
1136f709 212mail_init(void)
d76ed9a9 213{
1136f709 214 mail_common_init();
d76ed9a9 215}