view src/get.c @ 189:dc89737b27aa

removed the pop-before-smtp (smtp-after-pop) function this kind of authentication is superseded by SMTP AUTH today removing it is a step towards removing the POP stuff completely If you still rely on pop-before-smtp, stay with 0.2.x or run an arbitrary pop client before
author meillo@marmaro.de
date Thu, 15 Jul 2010 10:33:53 +0200
parents f671821d8222
children
line wrap: on
line source

/*  MasqMail
    Copyright (C) 2000-2002 Oliver Kurth

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <sys/wait.h>
#include <sys/file.h>
#include <sys/types.h>

#include "masqmail.h"
#include "pop3_in.h"

#ifdef ENABLE_POP3

static int volatile sighup_seen = 0;

static void
sighup_handler(int sig)
{
	sighup_seen = 1;
	signal(SIGHUP, sighup_handler);
}

static void
sigchld_handler(int sig)
{
	pid_t pid;
	int status;

	pid = waitpid(0, &status, 0);
	if (pid > 0) {
		if (WEXITSTATUS(status) != EXIT_SUCCESS)
			logwrite(LOG_WARNING, "process %d exited with %d\n", pid, WEXITSTATUS(status));
		if (WIFSIGNALED(status))
			logwrite(LOG_WARNING, "process with pid %d got signal: %d\n", pid, WTERMSIG(status));
	}
	signal(SIGCHLD, sigchld_handler);
}

static int
get_lock(get_conf * gc)
{
#ifdef USE_DOTLOCK
	gboolean ok = FALSE;
	gchar *hitch_name;
	gchar *lock_name;

	/* the name of the lock is constructed from the user
	   and the server name, to prevent more than one connection at the same time
	   to the same server and the same user. This way concurrent connections
	   are possible to different servers or different users */
	hitch_name = g_strdup_printf("%s/masqmail-get-%s@%s-%d.lock",
	                             conf.lock_dir, gc->login_user, gc->server_name, getpid());
	lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
	                            conf.lock_dir, gc->login_user, gc->server_name);

	ok = dot_lock(lock_name, hitch_name);
	if (!ok)
		logwrite(LOG_WARNING, "getting mail for %s@%s is locked\n", gc->login_user, gc->server_name);

	g_free(lock_name);
	g_free(hitch_name);

	return ok;
#else
	gchar *lock_name;
	int fd;

	lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock", conf.lock_dir, gc->login_user, gc->server_name);

	if ((fd = open(lock_name, O_WRONLY | O_NDELAY | O_APPEND | O_CREAT, 0600)) >= 0) {
		if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
			close(fd);
			logwrite(LOG_WARNING, "getting mail for %s@%s is locked\n", gc->login_user, gc->server_name);
			fd = -1;
		}
	} else
		logwrite(LOG_WARNING, "could not open lock %s: %s\n", lock_name, strerror(errno));

	g_free(lock_name);

	return fd;
#endif
}

#ifdef USE_DOTLOCK
static gboolean
get_unlock(get_conf * gc)
{
	gchar *lock_name lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
	                                             conf.lock_dir, gc->login_user, gc->server_name);

	dot_unlock(lock_name);
	g_free(lock_name);

	return TRUE;
}
#else
static void
get_unlock(get_conf * gc, int fd)
{
	gchar *lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
	                                   conf.lock_dir, gc->login_user, gc->server_name);

	flock(fd, LOCK_UN);
	close(fd);

	unlink(lock_name);
	g_free(lock_name);
}
#endif

gboolean
get_from_file(gchar * fname)
{
	guint flags = 0;
	get_conf *gc = read_get_conf(fname);
	gboolean ok = TRUE;
	int lock;

	if (gc) {
		if (!gc->do_keep)
			flags |= POP3_FLAG_DELETE;
		if (gc->do_uidl)
			flags |= POP3_FLAG_UIDL;
		if (gc->do_uidl_dele)
			flags |= POP3_FLAG_UIDL_DELE;

		if (!(gc->server_name)) {
			logwrite(LOG_ALERT, "no server name given in %s\n", fname);
			return FALSE;
		}
		if (!(gc->address)) {
			logwrite(LOG_ALERT, "no address given in %s\n", fname);
			return FALSE;
		}
		if (!(gc->login_user)) {
			logwrite(LOG_ALERT, "no user name given in %s\n", fname);
			return FALSE;
		}
		if (!(gc->login_pass)) {
			logwrite(LOG_ALERT, "no password given in %s\n", fname);
			return FALSE;
		}

		DEBUG(3) debugf("flags = %d\n", flags);

		if ((strcmp(gc->protocol, "pop3") == 0) || (strcmp(gc->protocol, "apop") == 0)) {
			pop3_base *popb = NULL;

			if (strcmp(gc->protocol, "apop") == 0) {
				flags |= POP3_FLAG_APOP;
				DEBUG(3) debugf("attempting to get mail for user %s at host %s for %s@%s with apop\n",
				                gc->login_user, gc->server_name, gc->address->local_part, gc->address->domain);
			} else {
				DEBUG(3) debugf("attempting to get mail for user %s at host %s for %s@%s with pop3\n",
				                gc->login_user, gc->server_name, gc->address->local_part, gc->address->domain);
			}
#ifdef USE_DOTLOCK
			if ((lock = get_lock(gc))) {
#else
			if ((lock = get_lock(gc)) >= 0) {
#endif
				if (gc->wrapper) {
					popb = pop3_in_open_child(gc->wrapper, flags);
					/* quick hack */
					popb->remote_host = gc->server_name;
				} else {
					popb = pop3_in_open(gc->server_name, gc->server_port, gc->resolve_list, flags);
				}
				if (popb) {
					ok = pop3_get(popb, gc->login_user, gc->login_pass, gc->address, gc->return_path,
					              gc->max_count, gc->max_size, gc->max_size_delete);
					pop3_in_close(popb);
				} else {
					ok = FALSE;
					logwrite(LOG_ALERT, "failed to connect to host %s\n", gc->server_name);
				}
#ifdef USE_DOTLOCK
				get_unlock(gc);
#else
				get_unlock(gc, lock);
#endif
			}
		} else {
			logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
			ok = FALSE;
		}

		destroy_get_conf(gc);
	}
	return ok;
}

gboolean
get_from_name(gchar * name)
{
	gchar *fname = (gchar *) table_find(conf.get_names, name);
	if (fname)
		return get_from_file(fname);
	return FALSE;
}

gboolean
get_all()
{
	GList *get_table = conf.get_names;
	GList *get_node;
	void (*old_signal) (int);

	old_signal = signal(SIGCHLD, SIG_DFL);

	foreach(get_table, get_node) {
		table_pair *pair = (table_pair *) (get_node->data);
		gchar *fname = (gchar *) pair->value;
		pid_t pid;

		pid = fork();
		if (pid == 0) {
			signal(SIGCHLD, old_signal);
			exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
		} else if (pid > 0) {
			int status;
			waitpid(pid, &status, 0);
			if (WEXITSTATUS(status) != EXIT_SUCCESS)
				logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
			if (WIFSIGNALED(status))
				logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
		} else
			logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
	}

	signal(SIGCHLD, old_signal);

	return TRUE;
}

void
get_online()
{
	GList *gf_list = NULL;
	gchar *connect_name = detect_online();

	if (connect_name != NULL) {
		void (*old_signal) (int);

		old_signal = signal(SIGCHLD, SIG_DFL);

		logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
		/* we are online! */
		gf_list = (GList *) table_find(conf.online_gets, connect_name);
		if (gf_list != NULL) {
			GList *node;
			foreach(gf_list, node) {
				gchar *fname = (gchar *) (node->data);
				pid_t pid;

				if (fname[0] != '/')
					fname = (gchar *) table_find(conf.get_names, fname);

				if (fname != NULL) {
					pid = fork();
					if (pid == 0) {
						signal(SIGCHLD, old_signal);
						exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
					} else if (pid > 0) {
						int status;
						waitpid(pid, &status, 0);
						if (WEXITSTATUS(status) != EXIT_SUCCESS)
							logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
						if (WIFSIGNALED(status))
							logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
					} else
						logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
				}
			}
		}
		signal(SIGCHLD, old_signal);
	}
}

void
get_daemon(gint gival, char *argv[])
{
	struct timeval tm;
	time_t time_before, time_now;
	int sel_ret;

	/* setup handler for HUP signal: */
	signal(SIGHUP, sighup_handler);

	/* we can give up root privileges */
	if (!conf.run_as_user) {
		if (setegid(conf.mail_gid) != 0) {
			logwrite(LOG_ALERT, "could not change gid to %d: %s\n", conf.mail_gid, strerror(errno));
			exit(EXIT_FAILURE);
		}
		if (seteuid(conf.mail_uid) != 0) {
			logwrite(LOG_ALERT, "could not change uid to %d: %s\n", conf.mail_uid, strerror(errno));
			exit(EXIT_FAILURE);
		}
	}

	/*  sel_ret = 0; */
	time(&time_before);
	time_before -= gival;
	sel_ret = -1;

	while (1) {
		/* see listen_port() in listen.c */
		if (gival > 0) {
			time(&time_now);
			if (sel_ret == 0) {	/* we are either just starting or did a queue run */
				tm.tv_sec = gival;
				tm.tv_usec = 0;
				time_before = time_now;
			} else {
				tm.tv_sec = gival - (time_now - time_before);
				tm.tv_usec = 0;

				/* race condition, very unlikely (but possible): */
				if (tm.tv_sec < 0)
					tm.tv_sec = 0;
			}
		}

		if ((sel_ret = select(0, NULL, NULL, NULL, &tm)) < 0) {
			if (errno != EINTR) {
				logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno));
				exit(EXIT_FAILURE);
			} else {
				if (sighup_seen) {
					logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n");

					if (argv == NULL)
						exit(EXIT_SUCCESS);

					execv(argv[0], &(argv[0]));
					logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno));
					exit(EXIT_FAILURE);

				}
			}
		} else {
			/* If select returns 0, the interval time has elapsed. We start a new get process */
			int pid;
			signal(SIGCHLD, sigchld_handler);
			if ((pid = fork()) == 0) {
				get_online();

				_exit(EXIT_SUCCESS);
			} else if (pid < 0) {
				logwrite(LOG_ALERT, "could not fork for get run");
			}
		}
	}
}

#endif