masqmail

diff src/get.c @ 0:08114f7dcc23

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 diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/get.c	Fri Sep 26 17:05:23 2008 +0200
     1.3 @@ -0,0 +1,410 @@
     1.4 +/*  MasqMail
     1.5 +    Copyright (C) 2000-2002 Oliver Kurth
     1.6 +
     1.7 +    This program is free software; you can redistribute it and/or modify
     1.8 +    it under the terms of the GNU General Public License as published by
     1.9 +    the Free Software Foundation; either version 2 of the License, or
    1.10 +    (at your option) any later version.
    1.11 +
    1.12 +    This program is distributed in the hope that it will be useful,
    1.13 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.15 +    GNU General Public License for more details.
    1.16 +
    1.17 +    You should have received a copy of the GNU General Public License
    1.18 +    along with this program; if not, write to the Free Software
    1.19 +    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    1.20 +*/
    1.21 +
    1.22 +#include <sys/wait.h>
    1.23 +#include <sys/file.h>
    1.24 +#include <sys/types.h>
    1.25 +
    1.26 +#include "masqmail.h"
    1.27 +#include "pop3_in.h"
    1.28 +
    1.29 +#ifdef ENABLE_POP3
    1.30 +
    1.31 +static int volatile sighup_seen = 0;
    1.32 +
    1.33 +static
    1.34 +void sighup_handler(int sig)
    1.35 +{
    1.36 +  sighup_seen = 1;
    1.37 +  signal(SIGHUP, sighup_handler);
    1.38 +}
    1.39 +
    1.40 +static
    1.41 +void sigchld_handler(int sig)
    1.42 +{
    1.43 +  pid_t pid;
    1.44 +  int status;
    1.45 +  
    1.46 +  pid = waitpid(0, &status, 0);
    1.47 +  if(pid > 0){
    1.48 +    if(WEXITSTATUS(status) != EXIT_SUCCESS)
    1.49 +      logwrite(LOG_WARNING, "process %d exited with %d\n",
    1.50 +	       pid, WEXITSTATUS(status));
    1.51 +    if(WIFSIGNALED(status))
    1.52 +      logwrite(LOG_WARNING,
    1.53 +	       "process with pid %d got signal: %d\n",
    1.54 +	       pid, WTERMSIG(status));
    1.55 +  }
    1.56 +  signal(SIGCHLD, sigchld_handler);
    1.57 +}
    1.58 +
    1.59 +static
    1.60 +int get_lock(get_conf *gc)
    1.61 +{
    1.62 +#ifdef USE_DOTLOCK
    1.63 +  gboolean ok = FALSE;
    1.64 +  gchar *hitch_name;
    1.65 +  gchar *lock_name;
    1.66 +
    1.67 +  /* the name of the lock is constructed from the user
    1.68 +     and the server name, to prevent more than one connection at the same time
    1.69 +     to the same server and the same user. This way concurrent connections
    1.70 +     are possible to different servers or different users */
    1.71 +  hitch_name = g_strdup_printf("%s/masqmail-get-%s@%s-%d.lock",
    1.72 +			       conf.lock_dir, gc->login_user,
    1.73 +			       gc->server_name, getpid());
    1.74 +  lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
    1.75 +			      conf.lock_dir, gc->login_user, gc->server_name);
    1.76 +  
    1.77 +  ok = dot_lock(lock_name, hitch_name);
    1.78 +  if(!ok) logwrite(LOG_WARNING,
    1.79 +		   "getting mail for %s@%s is locked\n",
    1.80 +		   gc->login_user, gc->server_name);
    1.81 +
    1.82 +  g_free(lock_name);
    1.83 +  g_free(hitch_name);
    1.84 +
    1.85 +  return ok;
    1.86 +#else
    1.87 +  gchar *lock_name;
    1.88 +  int fd;
    1.89 +
    1.90 +  lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
    1.91 +			      conf.lock_dir, gc->login_user, gc->server_name);
    1.92 +
    1.93 +  if((fd = open(lock_name, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) >= 0){
    1.94 +    if(flock(fd, LOCK_EX|LOCK_NB) != 0){
    1.95 +      close(fd);
    1.96 +      logwrite(LOG_WARNING,
    1.97 +	       "getting mail for %s@%s is locked\n",
    1.98 +	       gc->login_user, gc->server_name);
    1.99 +      fd = -1;
   1.100 +    }
   1.101 +  }else
   1.102 +    logwrite(LOG_WARNING,
   1.103 +	     "could not open lock %s: %s\n", lock_name, strerror(errno));
   1.104 +
   1.105 +  g_free(lock_name);
   1.106 +
   1.107 +  return fd;
   1.108 +#endif
   1.109 +}
   1.110 +
   1.111 +#ifdef USE_DOTLOCK
   1.112 +static
   1.113 +gboolean get_unlock(get_conf *gc)
   1.114 +{
   1.115 +  gchar *lock_name lock_name =
   1.116 +    g_strdup_printf("%s/masqmail-get-%s@%s.lock",
   1.117 +		    conf.lock_dir, gc->login_user, gc->server_name);
   1.118 +  
   1.119 +  dot_unlock(lock_name);
   1.120 +
   1.121 +  g_free(lock_name);
   1.122 +
   1.123 +  return TRUE;
   1.124 +}
   1.125 +#else
   1.126 +static void get_unlock(get_conf *gc, int fd)
   1.127 +{
   1.128 +  gchar *lock_name =
   1.129 +    g_strdup_printf("%s/masqmail-get-%s@%s.lock",
   1.130 +		    conf.lock_dir, gc->login_user, gc->server_name);
   1.131 +
   1.132 +  flock(fd, LOCK_UN);
   1.133 +  close(fd);
   1.134 +
   1.135 +  unlink(lock_name);
   1.136 +  g_free(lock_name);
   1.137 +}
   1.138 +#endif
   1.139 +
   1.140 +gboolean get_from_file(gchar *fname)
   1.141 +{
   1.142 +  guint flags = 0;
   1.143 +  get_conf *gc = read_get_conf(fname);
   1.144 +  gboolean ok = TRUE;
   1.145 +  int lock;
   1.146 +
   1.147 +  if(gc){
   1.148 +    if(!gc->do_keep) flags |= POP3_FLAG_DELETE;
   1.149 +    if(gc->do_uidl) flags |= POP3_FLAG_UIDL;
   1.150 +    if(gc->do_uidl_dele) flags |= POP3_FLAG_UIDL_DELE;
   1.151 +    
   1.152 +    if(!(gc->server_name)){
   1.153 +      logwrite(LOG_ALERT, "no server name given in %s\n", fname); return FALSE;
   1.154 +    }
   1.155 +    if(!(gc->address)){
   1.156 +      logwrite(LOG_ALERT, "no address given in %s\n", fname); return FALSE;
   1.157 +    }
   1.158 +    if(!(gc->login_user)){
   1.159 +      logwrite(LOG_ALERT, "no user name given in %s\n", fname); return FALSE;
   1.160 +    }
   1.161 +    if(!(gc->login_pass)){
   1.162 +      logwrite(LOG_ALERT, "no password given in %s\n", fname); return FALSE;
   1.163 +    }
   1.164 +
   1.165 +    DEBUG(3) debugf("flags = %d\n", flags);
   1.166 +    
   1.167 +    if((strcmp(gc->protocol, "pop3") == 0) || (strcmp(gc->protocol, "apop") == 0)){
   1.168 +      pop3_base *popb = NULL;
   1.169 +
   1.170 +      if(strcmp(gc->protocol, "apop") == 0){
   1.171 +	flags |= POP3_FLAG_APOP;
   1.172 +	DEBUG(3) debugf("attempting to get mail for user %s at host %s"
   1.173 +			" for %s@%s with apop\n",
   1.174 +			gc->login_user, gc->server_name,
   1.175 +			gc->address->local_part, gc->address->domain);
   1.176 +      }else{
   1.177 +	DEBUG(3) debugf("attempting to get mail for user %s at host %s"
   1.178 +			" for %s@%s with pop3\n",
   1.179 +			gc->login_user, gc->server_name,
   1.180 +			gc->address->local_part, gc->address->domain);
   1.181 +      }
   1.182 +#ifdef USE_DOTLOCK
   1.183 +      if((lock = get_lock(gc))){
   1.184 +#else
   1.185 +      if((lock = get_lock(gc)) >= 0){
   1.186 +#endif
   1.187 +	if(gc->wrapper){
   1.188 +	  popb = pop3_in_open_child(gc->wrapper, flags);
   1.189 +	  /* quick hack */
   1.190 +	  popb->remote_host = gc->server_name;
   1.191 +	}else{
   1.192 +	  popb = pop3_in_open(gc->server_name, gc->server_port,
   1.193 +			      gc->resolve_list, flags);
   1.194 +	}
   1.195 +	if(popb){
   1.196 +	  ok = pop3_get(popb, gc->login_user, gc->login_pass,
   1.197 +			gc->address, gc->return_path,
   1.198 +			gc->max_count, gc->max_size, gc->max_size_delete);
   1.199 +	  pop3_in_close(popb);
   1.200 +	}else{
   1.201 +	  ok = FALSE;
   1.202 +	  logwrite(LOG_ALERT, "failed to connect to host %s\n", gc->server_name);
   1.203 +	}
   1.204 +#ifdef USE_DOTLOCK
   1.205 +	get_unlock(gc);
   1.206 +#else
   1.207 +	get_unlock(gc, lock);
   1.208 +#endif
   1.209 +      }
   1.210 +    }else{
   1.211 +      logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
   1.212 +      ok = FALSE;
   1.213 +    }
   1.214 +
   1.215 +    destroy_get_conf(gc);
   1.216 +  }
   1.217 +  return ok;
   1.218 +}
   1.219 +
   1.220 +gboolean get_from_name(gchar *name)
   1.221 +{
   1.222 +  gchar *fname = (gchar *)table_find(conf.get_names, name);
   1.223 +  if(fname)
   1.224 +    return get_from_file(fname);
   1.225 +  return FALSE;
   1.226 +}
   1.227 +
   1.228 +gboolean get_all()
   1.229 +{
   1.230 +  GList *get_table = conf.get_names;
   1.231 +  GList *get_node;
   1.232 +  void (*old_signal)(int);
   1.233 +
   1.234 +  old_signal = signal(SIGCHLD, SIG_DFL);
   1.235 +
   1.236 +  foreach(get_table, get_node){
   1.237 +    table_pair *pair = (table_pair *)(get_node->data);
   1.238 +    gchar *fname = (gchar *)pair->value;
   1.239 +    pid_t pid;
   1.240 +
   1.241 +    pid = fork();
   1.242 +    if(pid == 0){
   1.243 +      signal(SIGCHLD, old_signal);    
   1.244 +      exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
   1.245 +    }else if(pid > 0){
   1.246 +      int status;
   1.247 +      waitpid(pid, &status, 0);
   1.248 +      if(WEXITSTATUS(status) != EXIT_SUCCESS)
   1.249 +	logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
   1.250 +      if(WIFSIGNALED(status))
   1.251 +	logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
   1.252 +    }else
   1.253 +      logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
   1.254 +  }
   1.255 +    
   1.256 +  signal(SIGCHLD, old_signal);    
   1.257 +
   1.258 +  return TRUE;
   1.259 +}
   1.260 +
   1.261 +void get_online()
   1.262 +{
   1.263 +  GList *gf_list = NULL;
   1.264 +  gchar *connect_name = detect_online();
   1.265 +
   1.266 +  if(connect_name != NULL){
   1.267 +    void (*old_signal)(int);
   1.268 +
   1.269 +    old_signal = signal(SIGCHLD, SIG_DFL);
   1.270 +
   1.271 +    logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
   1.272 +    /* we are online! */
   1.273 +    gf_list = (GList *)table_find(conf.online_gets, connect_name);
   1.274 +    if(gf_list != NULL){
   1.275 +      GList *node;
   1.276 +      foreach(gf_list, node){
   1.277 +	gchar *fname = (gchar *)(node->data);
   1.278 +	pid_t pid;
   1.279 +
   1.280 +	if(fname[0] != '/')
   1.281 +	  fname = (gchar *)table_find(conf.get_names, fname);
   1.282 +
   1.283 +	if(fname != NULL){
   1.284 +	  pid = fork();
   1.285 +	  if(pid == 0){
   1.286 +	    signal(SIGCHLD, old_signal);    
   1.287 +	    exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
   1.288 +	  }else if(pid > 0){
   1.289 +	    int status;
   1.290 +	    waitpid(pid, &status, 0);
   1.291 +	    if(WEXITSTATUS(status) != EXIT_SUCCESS)
   1.292 +	      logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
   1.293 +	    if(WIFSIGNALED(status))
   1.294 +	      logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
   1.295 +	  }else
   1.296 +	    logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
   1.297 +	}
   1.298 +      }
   1.299 +    }
   1.300 +    signal(SIGCHLD, old_signal);    
   1.301 +  }
   1.302 +}
   1.303 +
   1.304 +void get_daemon(gint gival, char *argv[])
   1.305 +{
   1.306 +  struct timeval tm;
   1.307 +  time_t time_before, time_now;
   1.308 +  int sel_ret;
   1.309 +
   1.310 +  /* setup handler for HUP signal: */
   1.311 +  signal(SIGHUP, sighup_handler);
   1.312 +
   1.313 +  /* we can give up root privileges */
   1.314 +  if(!conf.run_as_user){
   1.315 +    if(setegid(conf.mail_gid) != 0){
   1.316 +      logwrite(LOG_ALERT, "could not change gid to %d: %s\n",
   1.317 +	       conf.mail_gid, strerror(errno));
   1.318 +      exit(EXIT_FAILURE);
   1.319 +    }
   1.320 +    if(seteuid(conf.mail_uid) != 0){
   1.321 +      logwrite(LOG_ALERT, "could not change uid to %d: %s\n",
   1.322 +	       conf.mail_uid, strerror(errno));
   1.323 +      exit(EXIT_FAILURE);
   1.324 +    }
   1.325 +  }
   1.326 +
   1.327 +  /*  sel_ret = 0;*/
   1.328 +  time(&time_before);
   1.329 +  time_before -= gival;
   1.330 +  sel_ret = -1;
   1.331 +
   1.332 +  while (1){
   1.333 +    /* see listen_port() in listen.c */
   1.334 +    if(gival > 0){
   1.335 +      time(&time_now);
   1.336 +      if(sel_ret == 0){ /* we are either just starting or did a queue run */
   1.337 +	tm.tv_sec = gival;
   1.338 +	tm.tv_usec = 0;
   1.339 +	time_before = time_now;
   1.340 +      }else{
   1.341 +	tm.tv_sec = gival - (time_now - time_before);
   1.342 +	tm.tv_usec = 0;
   1.343 +
   1.344 +	/* race condition, very unlikely (but possible): */
   1.345 +	if(tm.tv_sec < 0)
   1.346 +	  tm.tv_sec = 0;
   1.347 +      }
   1.348 +    }
   1.349 +
   1.350 +    if ((sel_ret = select(0, NULL, NULL, NULL, &tm)) < 0){
   1.351 +      if(errno != EINTR){
   1.352 +	logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno));
   1.353 +	exit (EXIT_FAILURE);
   1.354 +      }else{
   1.355 +	if(sighup_seen){
   1.356 +	  logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n");
   1.357 +
   1.358 +	  if(argv == NULL) exit(EXIT_SUCCESS);
   1.359 +
   1.360 +	  execv(argv[0], &(argv[0]));
   1.361 +	  logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno));
   1.362 +	  exit(EXIT_FAILURE);
   1.363 +
   1.364 +	}
   1.365 +      }
   1.366 +    }else{
   1.367 +      /* If select returns 0, the interval time has elapsed.
   1.368 +	 We start a new get process */
   1.369 +      int pid;
   1.370 +      signal(SIGCHLD, sigchld_handler);
   1.371 +      if((pid = fork()) == 0){
   1.372 +	get_online();
   1.373 +
   1.374 +	_exit(EXIT_SUCCESS);
   1.375 +      }
   1.376 +      else if(pid < 0){
   1.377 +	logwrite(LOG_ALERT, "could not fork for get run");
   1.378 +      }
   1.379 +    }
   1.380 +  }
   1.381 +}
   1.382 +
   1.383 +gboolean pop_before_smtp(gchar *fname)
   1.384 +{
   1.385 +  gboolean ok = FALSE;
   1.386 +  GList *resolve_list = NULL;
   1.387 +  get_conf *gc = read_get_conf(fname);
   1.388 +  guint flags = 0;
   1.389 +
   1.390 +#ifdef ENABLE_RESOLVER
   1.391 +  resolve_list = g_list_append(resolve_list, resolve_dns_a);
   1.392 +#endif
   1.393 +  resolve_list = g_list_append(resolve_list, resolve_byname);
   1.394 +
   1.395 +  if(strcmp(gc->protocol, "pop3") == 0){
   1.396 +    DEBUG(3) debugf("attempting to login for user %s, host = %s with pop3\n",
   1.397 +		    gc->login_user, gc->server_name);
   1.398 +    ok = pop3_login(gc->server_name, gc->server_port, resolve_list,
   1.399 +		    gc->login_user, gc->login_pass,
   1.400 +		    flags);
   1.401 +  }else if(strcmp(gc->protocol, "apop") == 0){
   1.402 +    DEBUG(3) debugf("attempting to login for user %s, host = %s with apop\n",
   1.403 +		    gc->login_user, gc->server_name);
   1.404 +    ok = pop3_login(gc->server_name, gc->server_port, resolve_list,
   1.405 +		    gc->login_user, gc->login_pass,
   1.406 +		    flags | POP3_FLAG_APOP);
   1.407 +  }else{
   1.408 +    logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
   1.409 +  }
   1.410 +  return ok;
   1.411 +}
   1.412 +
   1.413 +#endif