diff src/get.c @ 0:08114f7dcc23 0.2.21

this is masqmail-0.2.21 from oliver kurth
author meillo@marmaro.de
date Fri, 26 Sep 2008 17:05:23 +0200
parents
children 26e34ae9a3e3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/get.c	Fri Sep 26 17:05:23 2008 +0200
@@ -0,0 +1,410 @@
+/*  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");
+      }
+    }
+  }
+}
+
+gboolean pop_before_smtp(gchar *fname)
+{
+  gboolean ok = FALSE;
+  GList *resolve_list = NULL;
+  get_conf *gc = read_get_conf(fname);
+  guint flags = 0;
+
+#ifdef ENABLE_RESOLVER
+  resolve_list = g_list_append(resolve_list, resolve_dns_a);
+#endif
+  resolve_list = g_list_append(resolve_list, resolve_byname);
+
+  if(strcmp(gc->protocol, "pop3") == 0){
+    DEBUG(3) debugf("attempting to login for user %s, host = %s with pop3\n",
+		    gc->login_user, gc->server_name);
+    ok = pop3_login(gc->server_name, gc->server_port, resolve_list,
+		    gc->login_user, gc->login_pass,
+		    flags);
+  }else if(strcmp(gc->protocol, "apop") == 0){
+    DEBUG(3) debugf("attempting to login for user %s, host = %s with apop\n",
+		    gc->login_user, gc->server_name);
+    ok = pop3_login(gc->server_name, gc->server_port, resolve_list,
+		    gc->login_user, gc->login_pass,
+		    flags | POP3_FLAG_APOP);
+  }else{
+    logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
+  }
+  return ok;
+}
+
+#endif