masqmail

view src/get.c @ 64:0379789a847b

improved comments in test cases
author meillo@marmaro.de
date Sun, 30 May 2010 16:52:35 +0200
parents 26e34ae9a3e3
children dc89737b27aa
line source
1 /* MasqMail
2 Copyright (C) 2000-2002 Oliver Kurth
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
19 #include <sys/wait.h>
20 #include <sys/file.h>
21 #include <sys/types.h>
23 #include "masqmail.h"
24 #include "pop3_in.h"
26 #ifdef ENABLE_POP3
28 static int volatile sighup_seen = 0;
30 static void
31 sighup_handler(int sig)
32 {
33 sighup_seen = 1;
34 signal(SIGHUP, sighup_handler);
35 }
37 static void
38 sigchld_handler(int sig)
39 {
40 pid_t pid;
41 int status;
43 pid = waitpid(0, &status, 0);
44 if (pid > 0) {
45 if (WEXITSTATUS(status) != EXIT_SUCCESS)
46 logwrite(LOG_WARNING, "process %d exited with %d\n", pid, WEXITSTATUS(status));
47 if (WIFSIGNALED(status))
48 logwrite(LOG_WARNING, "process with pid %d got signal: %d\n", pid, WTERMSIG(status));
49 }
50 signal(SIGCHLD, sigchld_handler);
51 }
53 static int
54 get_lock(get_conf * gc)
55 {
56 #ifdef USE_DOTLOCK
57 gboolean ok = FALSE;
58 gchar *hitch_name;
59 gchar *lock_name;
61 /* the name of the lock is constructed from the user
62 and the server name, to prevent more than one connection at the same time
63 to the same server and the same user. This way concurrent connections
64 are possible to different servers or different users */
65 hitch_name = g_strdup_printf("%s/masqmail-get-%s@%s-%d.lock",
66 conf.lock_dir, gc->login_user, gc->server_name, getpid());
67 lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
68 conf.lock_dir, gc->login_user, gc->server_name);
70 ok = dot_lock(lock_name, hitch_name);
71 if (!ok)
72 logwrite(LOG_WARNING, "getting mail for %s@%s is locked\n", gc->login_user, gc->server_name);
74 g_free(lock_name);
75 g_free(hitch_name);
77 return ok;
78 #else
79 gchar *lock_name;
80 int fd;
82 lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock", conf.lock_dir, gc->login_user, gc->server_name);
84 if ((fd = open(lock_name, O_WRONLY | O_NDELAY | O_APPEND | O_CREAT, 0600)) >= 0) {
85 if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
86 close(fd);
87 logwrite(LOG_WARNING, "getting mail for %s@%s is locked\n", gc->login_user, gc->server_name);
88 fd = -1;
89 }
90 } else
91 logwrite(LOG_WARNING, "could not open lock %s: %s\n", lock_name, strerror(errno));
93 g_free(lock_name);
95 return fd;
96 #endif
97 }
99 #ifdef USE_DOTLOCK
100 static gboolean
101 get_unlock(get_conf * gc)
102 {
103 gchar *lock_name lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
104 conf.lock_dir, gc->login_user, gc->server_name);
106 dot_unlock(lock_name);
107 g_free(lock_name);
109 return TRUE;
110 }
111 #else
112 static void
113 get_unlock(get_conf * gc, int fd)
114 {
115 gchar *lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
116 conf.lock_dir, gc->login_user, gc->server_name);
118 flock(fd, LOCK_UN);
119 close(fd);
121 unlink(lock_name);
122 g_free(lock_name);
123 }
124 #endif
126 gboolean
127 get_from_file(gchar * fname)
128 {
129 guint flags = 0;
130 get_conf *gc = read_get_conf(fname);
131 gboolean ok = TRUE;
132 int lock;
134 if (gc) {
135 if (!gc->do_keep)
136 flags |= POP3_FLAG_DELETE;
137 if (gc->do_uidl)
138 flags |= POP3_FLAG_UIDL;
139 if (gc->do_uidl_dele)
140 flags |= POP3_FLAG_UIDL_DELE;
142 if (!(gc->server_name)) {
143 logwrite(LOG_ALERT, "no server name given in %s\n", fname);
144 return FALSE;
145 }
146 if (!(gc->address)) {
147 logwrite(LOG_ALERT, "no address given in %s\n", fname);
148 return FALSE;
149 }
150 if (!(gc->login_user)) {
151 logwrite(LOG_ALERT, "no user name given in %s\n", fname);
152 return FALSE;
153 }
154 if (!(gc->login_pass)) {
155 logwrite(LOG_ALERT, "no password given in %s\n", fname);
156 return FALSE;
157 }
159 DEBUG(3) debugf("flags = %d\n", flags);
161 if ((strcmp(gc->protocol, "pop3") == 0) || (strcmp(gc->protocol, "apop") == 0)) {
162 pop3_base *popb = NULL;
164 if (strcmp(gc->protocol, "apop") == 0) {
165 flags |= POP3_FLAG_APOP;
166 DEBUG(3) debugf("attempting to get mail for user %s at host %s for %s@%s with apop\n",
167 gc->login_user, gc->server_name, gc->address->local_part, gc->address->domain);
168 } else {
169 DEBUG(3) debugf("attempting to get mail for user %s at host %s for %s@%s with pop3\n",
170 gc->login_user, gc->server_name, gc->address->local_part, gc->address->domain);
171 }
172 #ifdef USE_DOTLOCK
173 if ((lock = get_lock(gc))) {
174 #else
175 if ((lock = get_lock(gc)) >= 0) {
176 #endif
177 if (gc->wrapper) {
178 popb = pop3_in_open_child(gc->wrapper, flags);
179 /* quick hack */
180 popb->remote_host = gc->server_name;
181 } else {
182 popb = pop3_in_open(gc->server_name, gc->server_port, gc->resolve_list, flags);
183 }
184 if (popb) {
185 ok = pop3_get(popb, gc->login_user, gc->login_pass, gc->address, gc->return_path,
186 gc->max_count, gc->max_size, gc->max_size_delete);
187 pop3_in_close(popb);
188 } else {
189 ok = FALSE;
190 logwrite(LOG_ALERT, "failed to connect to host %s\n", gc->server_name);
191 }
192 #ifdef USE_DOTLOCK
193 get_unlock(gc);
194 #else
195 get_unlock(gc, lock);
196 #endif
197 }
198 } else {
199 logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
200 ok = FALSE;
201 }
203 destroy_get_conf(gc);
204 }
205 return ok;
206 }
208 gboolean
209 get_from_name(gchar * name)
210 {
211 gchar *fname = (gchar *) table_find(conf.get_names, name);
212 if (fname)
213 return get_from_file(fname);
214 return FALSE;
215 }
217 gboolean
218 get_all()
219 {
220 GList *get_table = conf.get_names;
221 GList *get_node;
222 void (*old_signal) (int);
224 old_signal = signal(SIGCHLD, SIG_DFL);
226 foreach(get_table, get_node) {
227 table_pair *pair = (table_pair *) (get_node->data);
228 gchar *fname = (gchar *) pair->value;
229 pid_t pid;
231 pid = fork();
232 if (pid == 0) {
233 signal(SIGCHLD, old_signal);
234 exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
235 } else if (pid > 0) {
236 int status;
237 waitpid(pid, &status, 0);
238 if (WEXITSTATUS(status) != EXIT_SUCCESS)
239 logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
240 if (WIFSIGNALED(status))
241 logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
242 } else
243 logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
244 }
246 signal(SIGCHLD, old_signal);
248 return TRUE;
249 }
251 void
252 get_online()
253 {
254 GList *gf_list = NULL;
255 gchar *connect_name = detect_online();
257 if (connect_name != NULL) {
258 void (*old_signal) (int);
260 old_signal = signal(SIGCHLD, SIG_DFL);
262 logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
263 /* we are online! */
264 gf_list = (GList *) table_find(conf.online_gets, connect_name);
265 if (gf_list != NULL) {
266 GList *node;
267 foreach(gf_list, node) {
268 gchar *fname = (gchar *) (node->data);
269 pid_t pid;
271 if (fname[0] != '/')
272 fname = (gchar *) table_find(conf.get_names, fname);
274 if (fname != NULL) {
275 pid = fork();
276 if (pid == 0) {
277 signal(SIGCHLD, old_signal);
278 exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
279 } else if (pid > 0) {
280 int status;
281 waitpid(pid, &status, 0);
282 if (WEXITSTATUS(status) != EXIT_SUCCESS)
283 logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
284 if (WIFSIGNALED(status))
285 logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
286 } else
287 logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
288 }
289 }
290 }
291 signal(SIGCHLD, old_signal);
292 }
293 }
295 void
296 get_daemon(gint gival, char *argv[])
297 {
298 struct timeval tm;
299 time_t time_before, time_now;
300 int sel_ret;
302 /* setup handler for HUP signal: */
303 signal(SIGHUP, sighup_handler);
305 /* we can give up root privileges */
306 if (!conf.run_as_user) {
307 if (setegid(conf.mail_gid) != 0) {
308 logwrite(LOG_ALERT, "could not change gid to %d: %s\n", conf.mail_gid, strerror(errno));
309 exit(EXIT_FAILURE);
310 }
311 if (seteuid(conf.mail_uid) != 0) {
312 logwrite(LOG_ALERT, "could not change uid to %d: %s\n", conf.mail_uid, strerror(errno));
313 exit(EXIT_FAILURE);
314 }
315 }
317 /* sel_ret = 0; */
318 time(&time_before);
319 time_before -= gival;
320 sel_ret = -1;
322 while (1) {
323 /* see listen_port() in listen.c */
324 if (gival > 0) {
325 time(&time_now);
326 if (sel_ret == 0) { /* we are either just starting or did a queue run */
327 tm.tv_sec = gival;
328 tm.tv_usec = 0;
329 time_before = time_now;
330 } else {
331 tm.tv_sec = gival - (time_now - time_before);
332 tm.tv_usec = 0;
334 /* race condition, very unlikely (but possible): */
335 if (tm.tv_sec < 0)
336 tm.tv_sec = 0;
337 }
338 }
340 if ((sel_ret = select(0, NULL, NULL, NULL, &tm)) < 0) {
341 if (errno != EINTR) {
342 logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno));
343 exit(EXIT_FAILURE);
344 } else {
345 if (sighup_seen) {
346 logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n");
348 if (argv == NULL)
349 exit(EXIT_SUCCESS);
351 execv(argv[0], &(argv[0]));
352 logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno));
353 exit(EXIT_FAILURE);
355 }
356 }
357 } else {
358 /* If select returns 0, the interval time has elapsed. We start a new get process */
359 int pid;
360 signal(SIGCHLD, sigchld_handler);
361 if ((pid = fork()) == 0) {
362 get_online();
364 _exit(EXIT_SUCCESS);
365 } else if (pid < 0) {
366 logwrite(LOG_ALERT, "could not fork for get run");
367 }
368 }
369 }
370 }
372 gboolean
373 pop_before_smtp(gchar * fname)
374 {
375 gboolean ok = FALSE;
376 GList *resolve_list = NULL;
377 get_conf *gc = read_get_conf(fname);
378 guint flags = 0;
380 #ifdef ENABLE_RESOLVER
381 resolve_list = g_list_append(resolve_list, resolve_dns_a);
382 #endif
383 resolve_list = g_list_append(resolve_list, resolve_byname);
385 if (strcmp(gc->protocol, "pop3") == 0) {
386 DEBUG(3) debugf("attempting to login for user %s, host = %s with pop3\n", gc->login_user, gc->server_name);
387 ok = pop3_login(gc->server_name, gc->server_port, resolve_list, gc->login_user, gc->login_pass, flags);
388 } else if (strcmp(gc->protocol, "apop") == 0) {
389 DEBUG(3) debugf ("attempting to login for user %s, host = %s with apop\n", gc->login_user, gc->server_name);
390 ok = pop3_login(gc->server_name, gc->server_port, resolve_list, gc->login_user,
391 gc->login_pass, flags | POP3_FLAG_APOP);
392 } else {
393 logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
394 }
395 return ok;
396 }
398 #endif