meillo@0: /* MasqMail meillo@0: Copyright (C) 2000-2002 Oliver Kurth meillo@0: meillo@0: This program is free software; you can redistribute it and/or modify meillo@0: it under the terms of the GNU General Public License as published by meillo@0: the Free Software Foundation; either version 2 of the License, or meillo@0: (at your option) any later version. meillo@0: meillo@0: This program is distributed in the hope that it will be useful, meillo@0: but WITHOUT ANY WARRANTY; without even the implied warranty of meillo@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the meillo@0: GNU General Public License for more details. meillo@0: meillo@0: You should have received a copy of the GNU General Public License meillo@0: along with this program; if not, write to the Free Software meillo@0: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. meillo@0: */ meillo@0: meillo@0: #include meillo@0: #include meillo@0: #include meillo@0: meillo@0: #include "masqmail.h" meillo@0: #include "pop3_in.h" meillo@0: meillo@0: #ifdef ENABLE_POP3 meillo@0: meillo@0: static int volatile sighup_seen = 0; meillo@0: meillo@0: static meillo@0: void sighup_handler(int sig) meillo@0: { meillo@0: sighup_seen = 1; meillo@0: signal(SIGHUP, sighup_handler); meillo@0: } meillo@0: meillo@0: static meillo@0: void sigchld_handler(int sig) meillo@0: { meillo@0: pid_t pid; meillo@0: int status; meillo@0: meillo@0: pid = waitpid(0, &status, 0); meillo@0: if(pid > 0){ meillo@0: if(WEXITSTATUS(status) != EXIT_SUCCESS) meillo@0: logwrite(LOG_WARNING, "process %d exited with %d\n", meillo@0: pid, WEXITSTATUS(status)); meillo@0: if(WIFSIGNALED(status)) meillo@0: logwrite(LOG_WARNING, meillo@0: "process with pid %d got signal: %d\n", meillo@0: pid, WTERMSIG(status)); meillo@0: } meillo@0: signal(SIGCHLD, sigchld_handler); meillo@0: } meillo@0: meillo@0: static meillo@0: int get_lock(get_conf *gc) meillo@0: { meillo@0: #ifdef USE_DOTLOCK meillo@0: gboolean ok = FALSE; meillo@0: gchar *hitch_name; meillo@0: gchar *lock_name; meillo@0: meillo@0: /* the name of the lock is constructed from the user meillo@0: and the server name, to prevent more than one connection at the same time meillo@0: to the same server and the same user. This way concurrent connections meillo@0: are possible to different servers or different users */ meillo@0: hitch_name = g_strdup_printf("%s/masqmail-get-%s@%s-%d.lock", meillo@0: conf.lock_dir, gc->login_user, meillo@0: gc->server_name, getpid()); meillo@0: lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock", meillo@0: conf.lock_dir, gc->login_user, gc->server_name); meillo@0: meillo@0: ok = dot_lock(lock_name, hitch_name); meillo@0: if(!ok) logwrite(LOG_WARNING, meillo@0: "getting mail for %s@%s is locked\n", meillo@0: gc->login_user, gc->server_name); meillo@0: meillo@0: g_free(lock_name); meillo@0: g_free(hitch_name); meillo@0: meillo@0: return ok; meillo@0: #else meillo@0: gchar *lock_name; meillo@0: int fd; meillo@0: meillo@0: lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock", meillo@0: conf.lock_dir, gc->login_user, gc->server_name); meillo@0: meillo@0: if((fd = open(lock_name, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) >= 0){ meillo@0: if(flock(fd, LOCK_EX|LOCK_NB) != 0){ meillo@0: close(fd); meillo@0: logwrite(LOG_WARNING, meillo@0: "getting mail for %s@%s is locked\n", meillo@0: gc->login_user, gc->server_name); meillo@0: fd = -1; meillo@0: } meillo@0: }else meillo@0: logwrite(LOG_WARNING, meillo@0: "could not open lock %s: %s\n", lock_name, strerror(errno)); meillo@0: meillo@0: g_free(lock_name); meillo@0: meillo@0: return fd; meillo@0: #endif meillo@0: } meillo@0: meillo@0: #ifdef USE_DOTLOCK meillo@0: static meillo@0: gboolean get_unlock(get_conf *gc) meillo@0: { meillo@0: gchar *lock_name lock_name = meillo@0: g_strdup_printf("%s/masqmail-get-%s@%s.lock", meillo@0: conf.lock_dir, gc->login_user, gc->server_name); meillo@0: meillo@0: dot_unlock(lock_name); meillo@0: meillo@0: g_free(lock_name); meillo@0: meillo@0: return TRUE; meillo@0: } meillo@0: #else meillo@0: static void get_unlock(get_conf *gc, int fd) meillo@0: { meillo@0: gchar *lock_name = meillo@0: g_strdup_printf("%s/masqmail-get-%s@%s.lock", meillo@0: conf.lock_dir, gc->login_user, gc->server_name); meillo@0: meillo@0: flock(fd, LOCK_UN); meillo@0: close(fd); meillo@0: meillo@0: unlink(lock_name); meillo@0: g_free(lock_name); meillo@0: } meillo@0: #endif meillo@0: meillo@0: gboolean get_from_file(gchar *fname) meillo@0: { meillo@0: guint flags = 0; meillo@0: get_conf *gc = read_get_conf(fname); meillo@0: gboolean ok = TRUE; meillo@0: int lock; meillo@0: meillo@0: if(gc){ meillo@0: if(!gc->do_keep) flags |= POP3_FLAG_DELETE; meillo@0: if(gc->do_uidl) flags |= POP3_FLAG_UIDL; meillo@0: if(gc->do_uidl_dele) flags |= POP3_FLAG_UIDL_DELE; meillo@0: meillo@0: if(!(gc->server_name)){ meillo@0: logwrite(LOG_ALERT, "no server name given in %s\n", fname); return FALSE; meillo@0: } meillo@0: if(!(gc->address)){ meillo@0: logwrite(LOG_ALERT, "no address given in %s\n", fname); return FALSE; meillo@0: } meillo@0: if(!(gc->login_user)){ meillo@0: logwrite(LOG_ALERT, "no user name given in %s\n", fname); return FALSE; meillo@0: } meillo@0: if(!(gc->login_pass)){ meillo@0: logwrite(LOG_ALERT, "no password given in %s\n", fname); return FALSE; meillo@0: } meillo@0: meillo@0: DEBUG(3) debugf("flags = %d\n", flags); meillo@0: meillo@0: if((strcmp(gc->protocol, "pop3") == 0) || (strcmp(gc->protocol, "apop") == 0)){ meillo@0: pop3_base *popb = NULL; meillo@0: meillo@0: if(strcmp(gc->protocol, "apop") == 0){ meillo@0: flags |= POP3_FLAG_APOP; meillo@0: DEBUG(3) debugf("attempting to get mail for user %s at host %s" meillo@0: " for %s@%s with apop\n", meillo@0: gc->login_user, gc->server_name, meillo@0: gc->address->local_part, gc->address->domain); meillo@0: }else{ meillo@0: DEBUG(3) debugf("attempting to get mail for user %s at host %s" meillo@0: " for %s@%s with pop3\n", meillo@0: gc->login_user, gc->server_name, meillo@0: gc->address->local_part, gc->address->domain); meillo@0: } meillo@0: #ifdef USE_DOTLOCK meillo@0: if((lock = get_lock(gc))){ meillo@0: #else meillo@0: if((lock = get_lock(gc)) >= 0){ meillo@0: #endif meillo@0: if(gc->wrapper){ meillo@0: popb = pop3_in_open_child(gc->wrapper, flags); meillo@0: /* quick hack */ meillo@0: popb->remote_host = gc->server_name; meillo@0: }else{ meillo@0: popb = pop3_in_open(gc->server_name, gc->server_port, meillo@0: gc->resolve_list, flags); meillo@0: } meillo@0: if(popb){ meillo@0: ok = pop3_get(popb, gc->login_user, gc->login_pass, meillo@0: gc->address, gc->return_path, meillo@0: gc->max_count, gc->max_size, gc->max_size_delete); meillo@0: pop3_in_close(popb); meillo@0: }else{ meillo@0: ok = FALSE; meillo@0: logwrite(LOG_ALERT, "failed to connect to host %s\n", gc->server_name); meillo@0: } meillo@0: #ifdef USE_DOTLOCK meillo@0: get_unlock(gc); meillo@0: #else meillo@0: get_unlock(gc, lock); meillo@0: #endif meillo@0: } meillo@0: }else{ meillo@0: logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol); meillo@0: ok = FALSE; meillo@0: } meillo@0: meillo@0: destroy_get_conf(gc); meillo@0: } meillo@0: return ok; meillo@0: } meillo@0: meillo@0: gboolean get_from_name(gchar *name) meillo@0: { meillo@0: gchar *fname = (gchar *)table_find(conf.get_names, name); meillo@0: if(fname) meillo@0: return get_from_file(fname); meillo@0: return FALSE; meillo@0: } meillo@0: meillo@0: gboolean get_all() meillo@0: { meillo@0: GList *get_table = conf.get_names; meillo@0: GList *get_node; meillo@0: void (*old_signal)(int); meillo@0: meillo@0: old_signal = signal(SIGCHLD, SIG_DFL); meillo@0: meillo@0: foreach(get_table, get_node){ meillo@0: table_pair *pair = (table_pair *)(get_node->data); meillo@0: gchar *fname = (gchar *)pair->value; meillo@0: pid_t pid; meillo@0: meillo@0: pid = fork(); meillo@0: if(pid == 0){ meillo@0: signal(SIGCHLD, old_signal); meillo@0: exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE); meillo@0: }else if(pid > 0){ meillo@0: int status; meillo@0: waitpid(pid, &status, 0); meillo@0: if(WEXITSTATUS(status) != EXIT_SUCCESS) meillo@0: logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status)); meillo@0: if(WIFSIGNALED(status)) meillo@0: logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status)); meillo@0: }else meillo@0: logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno)); meillo@0: } meillo@0: meillo@0: signal(SIGCHLD, old_signal); meillo@0: meillo@0: return TRUE; meillo@0: } meillo@0: meillo@0: void get_online() meillo@0: { meillo@0: GList *gf_list = NULL; meillo@0: gchar *connect_name = detect_online(); meillo@0: meillo@0: if(connect_name != NULL){ meillo@0: void (*old_signal)(int); meillo@0: meillo@0: old_signal = signal(SIGCHLD, SIG_DFL); meillo@0: meillo@0: logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name); meillo@0: /* we are online! */ meillo@0: gf_list = (GList *)table_find(conf.online_gets, connect_name); meillo@0: if(gf_list != NULL){ meillo@0: GList *node; meillo@0: foreach(gf_list, node){ meillo@0: gchar *fname = (gchar *)(node->data); meillo@0: pid_t pid; meillo@0: meillo@0: if(fname[0] != '/') meillo@0: fname = (gchar *)table_find(conf.get_names, fname); meillo@0: meillo@0: if(fname != NULL){ meillo@0: pid = fork(); meillo@0: if(pid == 0){ meillo@0: signal(SIGCHLD, old_signal); meillo@0: exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE); meillo@0: }else if(pid > 0){ meillo@0: int status; meillo@0: waitpid(pid, &status, 0); meillo@0: if(WEXITSTATUS(status) != EXIT_SUCCESS) meillo@0: logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status)); meillo@0: if(WIFSIGNALED(status)) meillo@0: logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status)); meillo@0: }else meillo@0: logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno)); meillo@0: } meillo@0: } meillo@0: } meillo@0: signal(SIGCHLD, old_signal); meillo@0: } meillo@0: } meillo@0: meillo@0: void get_daemon(gint gival, char *argv[]) meillo@0: { meillo@0: struct timeval tm; meillo@0: time_t time_before, time_now; meillo@0: int sel_ret; meillo@0: meillo@0: /* setup handler for HUP signal: */ meillo@0: signal(SIGHUP, sighup_handler); meillo@0: meillo@0: /* we can give up root privileges */ meillo@0: if(!conf.run_as_user){ meillo@0: if(setegid(conf.mail_gid) != 0){ meillo@0: logwrite(LOG_ALERT, "could not change gid to %d: %s\n", meillo@0: conf.mail_gid, strerror(errno)); meillo@0: exit(EXIT_FAILURE); meillo@0: } meillo@0: if(seteuid(conf.mail_uid) != 0){ meillo@0: logwrite(LOG_ALERT, "could not change uid to %d: %s\n", meillo@0: conf.mail_uid, strerror(errno)); meillo@0: exit(EXIT_FAILURE); meillo@0: } meillo@0: } meillo@0: meillo@0: /* sel_ret = 0;*/ meillo@0: time(&time_before); meillo@0: time_before -= gival; meillo@0: sel_ret = -1; meillo@0: meillo@0: while (1){ meillo@0: /* see listen_port() in listen.c */ meillo@0: if(gival > 0){ meillo@0: time(&time_now); meillo@0: if(sel_ret == 0){ /* we are either just starting or did a queue run */ meillo@0: tm.tv_sec = gival; meillo@0: tm.tv_usec = 0; meillo@0: time_before = time_now; meillo@0: }else{ meillo@0: tm.tv_sec = gival - (time_now - time_before); meillo@0: tm.tv_usec = 0; meillo@0: meillo@0: /* race condition, very unlikely (but possible): */ meillo@0: if(tm.tv_sec < 0) meillo@0: tm.tv_sec = 0; meillo@0: } meillo@0: } meillo@0: meillo@0: if ((sel_ret = select(0, NULL, NULL, NULL, &tm)) < 0){ meillo@0: if(errno != EINTR){ meillo@0: logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno)); meillo@0: exit (EXIT_FAILURE); meillo@0: }else{ meillo@0: if(sighup_seen){ meillo@0: logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n"); meillo@0: meillo@0: if(argv == NULL) exit(EXIT_SUCCESS); meillo@0: meillo@0: execv(argv[0], &(argv[0])); meillo@0: logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno)); meillo@0: exit(EXIT_FAILURE); meillo@0: meillo@0: } meillo@0: } meillo@0: }else{ meillo@0: /* If select returns 0, the interval time has elapsed. meillo@0: We start a new get process */ meillo@0: int pid; meillo@0: signal(SIGCHLD, sigchld_handler); meillo@0: if((pid = fork()) == 0){ meillo@0: get_online(); meillo@0: meillo@0: _exit(EXIT_SUCCESS); meillo@0: } meillo@0: else if(pid < 0){ meillo@0: logwrite(LOG_ALERT, "could not fork for get run"); meillo@0: } meillo@0: } meillo@0: } meillo@0: } meillo@0: meillo@0: gboolean pop_before_smtp(gchar *fname) meillo@0: { meillo@0: gboolean ok = FALSE; meillo@0: GList *resolve_list = NULL; meillo@0: get_conf *gc = read_get_conf(fname); meillo@0: guint flags = 0; meillo@0: meillo@0: #ifdef ENABLE_RESOLVER meillo@0: resolve_list = g_list_append(resolve_list, resolve_dns_a); meillo@0: #endif meillo@0: resolve_list = g_list_append(resolve_list, resolve_byname); meillo@0: meillo@0: if(strcmp(gc->protocol, "pop3") == 0){ meillo@0: DEBUG(3) debugf("attempting to login for user %s, host = %s with pop3\n", meillo@0: gc->login_user, gc->server_name); meillo@0: ok = pop3_login(gc->server_name, gc->server_port, resolve_list, meillo@0: gc->login_user, gc->login_pass, meillo@0: flags); meillo@0: }else if(strcmp(gc->protocol, "apop") == 0){ meillo@0: DEBUG(3) debugf("attempting to login for user %s, host = %s with apop\n", meillo@0: gc->login_user, gc->server_name); meillo@0: ok = pop3_login(gc->server_name, gc->server_port, resolve_list, meillo@0: gc->login_user, gc->login_pass, meillo@0: flags | POP3_FLAG_APOP); meillo@0: }else{ meillo@0: logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol); meillo@0: } meillo@0: return ok; meillo@0: } meillo@0: meillo@0: #endif