masqmail
view src/local.c @ 304:d5ce2ba71e7b
manual formating of Received: hdrs; changed hdr for local receival
Now the Received: headers are much friendlier to read.
About folding: We must fold any line at 998 chars before transfer.
We should fold the lines we produce at 78 chars. That is what RFC
2821 requests. We should think about it, somewhen.
The header for locally (i.e. non-SMTP) received mail is changed
to the format postfix uses. This matches RFC 2821 better. The
`from' clause should contain a domain or IP, not a user name. Also,
the `with' clause should contain a registered standard protocol
name, which ``local'' is not.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Thu, 09 Dec 2010 18:28:11 -0300 |
parents | 996b53a50f55 |
children | 63efd381e27b |
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
20 #include <sys/wait.h>
21 #include <sys/stat.h>
23 #include "masqmail.h"
24 #include "peopen.h"
26 static void
27 message_stream(FILE * out, message * msg, GList * hdr_list, guint flags)
28 {
29 time_t now = time(NULL);
30 GList *node;
32 if (flags & MSGSTR_FROMLINE) {
33 fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, msg->return_path->domain, ctime(&now));
34 }
36 foreach(hdr_list, node) {
37 header *hdr = (header *) (node->data);
38 fputs(hdr->header, out);
39 }
40 putc('\n', out);
41 foreach(msg->data_list, node) {
42 /* From hack: */
43 if (flags & MSGSTR_FROMHACK) {
44 if (strncmp(node->data, "From ", 5) == 0)
45 putc('>', out);
46 }
47 fputs(node->data, out);
48 }
49 putc('\n', out);
50 }
52 gboolean
53 append_file(message * msg, GList * hdr_list, gchar * user)
54 {
55 struct passwd *pw;
56 gboolean ok = FALSE;
58 /* headers may be special for a local delivery */
59 if (hdr_list == NULL)
60 hdr_list = msg->hdr_list;
62 if ((pw = getpwnam(user))) {
63 uid_t saved_uid = geteuid();
64 gid_t saved_gid = getegid();
65 gboolean uid_ok = TRUE, gid_ok = TRUE;
67 if (!conf.run_as_user) {
68 uid_ok = (seteuid(0) == 0);
69 if (uid_ok) {
70 gid_ok = (setegid(conf.mail_gid) == 0);
71 uid_ok = (seteuid(pw->pw_uid) == 0);
72 }
73 }
75 DEBUG(5) debugf("running as euid %d, egid %d\n", geteuid(), getegid());
77 if (uid_ok && gid_ok) {
78 gchar *filename;
79 FILE *out;
81 filename = g_strdup_printf("%s/%s", conf.mail_dir, user);
82 if ((out = fopen(filename, "a"))) {
83 #ifdef USE_LIBLOCKFILE
84 gint err;
85 /* lock file using liblockfile */
86 err = maillock(user, 3);
87 if (err == 0) {
88 #else
89 /* lock file: */
90 struct flock lock;
91 lock.l_type = F_WRLCK;
92 lock.l_whence = SEEK_END;
93 lock.l_start = lock.l_len = 0;
94 if (fcntl(fileno(out), F_SETLK, &lock) != -1) {
95 #endif
96 fchmod(fileno(out), 0600);
97 message_stream(out, msg, hdr_list, MSGSTR_FROMLINE | MSGSTR_FROMHACK);
98 ok = TRUE;
100 /* close when still user */
101 fclose(out);
102 #ifdef USE_LIBLOCKFILE
103 mailunlock();
104 #endif
105 } else {
106 fclose(out);
107 #ifdef USE_LIBLOCKFILE
108 DEBUG(3) debugf("could not lock file %s: error %d\n", filename, err);
109 } /* XEmacs indenting convenience... */
110 #else
111 DEBUG(3) debugf("could not lock file %s: %s\n", filename, strerror(errno));
112 }
113 #endif
114 } else {
115 logwrite(LOG_ALERT, "could not open file %s: %s\n", filename, strerror(errno));
116 }
117 g_free(filename);
119 if (!conf.run_as_user) {
120 uid_ok = (seteuid(0) == 0);
121 if (uid_ok) {
122 gid_ok = (setegid(saved_gid) == 0);
123 uid_ok = (seteuid(saved_uid) == 0);
124 }
125 }
127 if (!uid_ok || !gid_ok) {
128 /* FIXME: if this fails we HAVE to exit, because we shall not run
129 with some users id. But we do not return, and so this message
130 will not be finished, so the user will get the message again
131 next time a delivery is attempted... */
132 logwrite(LOG_ALERT, "could not set back uid or gid after local delivery: %s\n", strerror(errno));
133 logwrite(LOG_ALERT, "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n",
134 getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid);
135 exit(1);
136 }
137 } else {
138 logwrite(LOG_ALERT, "could not set uid or gid for local delivery, uid = %d: %s\n", pw->pw_uid, strerror(errno));
139 }
140 } else {
141 logwrite(LOG_ALERT, "could not find password entry for user %s\n", user);
142 errno = ENOENT; /* getpwnam does not set errno correctly */
143 }
145 return ok;
146 }
148 gboolean
149 pipe_out(message * msg, GList * hdr_list, address * rcpt, gchar * cmd, guint flags)
150 {
151 gchar *envp[40];
152 FILE *out;
153 uid_t saved_uid = geteuid();
154 gid_t saved_gid = getegid();
155 gboolean ok = FALSE;
156 gint i, n;
157 pid_t pid;
158 void (*old_signal) (int);
159 int status;
161 /* set uid and gid to the mail ids */
162 if (!conf.run_as_user) {
163 set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid);
164 }
166 /* set environment */
167 {
168 gint i = 0;
169 address *ancestor = addr_find_ancestor(rcpt);
171 envp[i++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain);
172 envp[i++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain);
173 envp[i++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part);
174 envp[i++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : "");
176 envp[i++] = g_strdup_printf("RETURN_PATH=%s@%s", msg->return_path->local_part, msg->return_path->domain);
177 envp[i++] = g_strdup_printf("DOMAIN=%s", ancestor->domain);
179 envp[i++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part);
180 envp[i++] = g_strdup_printf("USER=%s", ancestor->local_part);
181 envp[i++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part);
183 envp[i++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid);
184 envp[i++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name);
186 envp[i] = NULL;
187 n = i;
188 }
190 old_signal = signal(SIGCHLD, SIG_DFL);
192 out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid);
193 if (out != NULL) {
194 message_stream(out, msg, hdr_list, flags);
196 fclose(out);
198 waitpid(pid, &status, 0);
200 if (WEXITSTATUS(status) != 0) {
201 int exstat = WEXITSTATUS(status);
202 logwrite(LOG_ALERT, "process returned %d (%s)\n", exstat, ext_strerror(1024 + exstat));
203 errno = 1024 + exstat;
204 } else if (WIFSIGNALED(status)) {
205 logwrite(LOG_ALERT, "process got signal %d\n", WTERMSIG(status));
206 } else
207 ok = TRUE;
209 } else
210 logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno));
212 signal(SIGCHLD, old_signal);
214 /* free environment */
215 for (i = 0; i < n; i++) {
216 g_free(envp[i]);
217 }
219 /* set uid and gid back */
220 if (!conf.run_as_user) {
221 set_euidgid(saved_uid, saved_gid, NULL, NULL);
222 }
224 return ok;
225 }