diff src/pop3_in.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/pop3_in.c	Fri Sep 26 17:05:23 2008 +0200
@@ -0,0 +1,820 @@
+/* pop3_in.c, Copyright (C) 2000 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* see RFC 1725 */
+
+#include "masqmail.h"
+#include "pop3_in.h"
+#include "readsock.h"
+
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#ifdef USE_LIB_CRYPTO
+#include <openssl/md5.h>
+#else
+#include "md5/global.h"
+#include "md5/md5.h"
+#endif
+
+#ifdef ENABLE_POP3
+
+/* experimental feature */
+#define DO_WRITE_UIDL_EARLY 1
+
+static
+gchar *MD5String (char *string)
+{
+  MD5_CTX context;
+  unsigned char digest[16];
+  char str_digest[33];
+  int i;
+
+#ifdef USE_LIB_CRYPTO
+  MD5(string, strlen(string), digest);
+#else
+  MD5Init(&context);
+  MD5Update(&context, string, strlen(string));
+  MD5Final(digest, &context);
+#endif
+  for (i = 0;  i < 16;  i++) 
+    sprintf(str_digest+2*i, "%02x", digest[i]);
+
+  return g_strdup(str_digest);
+}
+
+static
+pop3_base *create_pop3base(gint sock, guint flags)
+{
+  gint dup_sock;
+
+  pop3_base *popb = (pop3_base *)g_malloc(sizeof(pop3_base));
+  if(popb){
+    memset(popb, 0, sizeof(pop3_base));
+
+    popb->error = pop3_ok;
+
+    popb->buffer = (gchar *)g_malloc(POP3_BUF_LEN);
+
+    dup_sock = dup(sock);
+    popb->out = fdopen(sock, "w");
+    popb->in = fdopen(dup_sock, "r");
+    
+    popb->flags = flags;
+  }
+  return popb;
+}
+
+static
+void pop3_printf(FILE *out, gchar *fmt, ...)
+{
+  va_list args;
+  va_start(args, fmt);
+
+  DEBUG(4){
+    gchar buf[256];
+    va_list args_copy;
+
+    va_copy(args_copy, args);
+    vsnprintf(buf, 255, fmt, args_copy);
+    va_end(args_copy);
+
+    debugf(">>>%s", buf);
+  }
+
+  vfprintf(out, fmt, args);  fflush(out);
+
+  va_end(args);
+}
+
+static
+gboolean find_uid(pop3_base *popb, gchar *str)
+{
+  GList *node, *node_next;
+
+  for(node = popb->list_uid_old; node; node=node_next){
+    gchar *uid = (gchar *)(node->data);
+    node_next = node->next;
+    if(strcmp(uid, str) == 0){
+#if 1
+      popb->list_uid_old = g_list_remove_link(popb->list_uid_old, node);
+      g_list_free_1(node);
+      g_free(uid);
+#endif
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+static
+gboolean write_uidl(pop3_base *popb, gchar *user)
+{
+  gboolean ok = FALSE;
+  GList *node;
+  gchar *filename = g_strdup_printf("%s/popuidl/%s@%s",
+				    conf.spool_dir,
+				    user, popb->remote_host);
+  gchar *tmpname = g_strdup_printf("%s.tmp", filename);
+  FILE *fptr = fopen(tmpname, "wt");
+
+  if(fptr){
+    foreach(popb->drop_list, node){
+      msg_info *info = (msg_info *)(node->data);
+      if(info->is_fetched || info->is_in_uidl)
+	fprintf(fptr, "%s\n", info->uid);
+    }
+    fclose(fptr);
+    ok = (rename(tmpname, filename) != -1);
+  }
+  
+  g_free(tmpname);
+  g_free(filename);
+  return ok;
+}
+
+static
+gboolean read_uidl_fname(pop3_base *popb, gchar *filename)
+{
+  gboolean ok = FALSE;
+  FILE *fptr = fopen(filename, "rt");
+  gchar buf[256];
+
+  if(fptr){
+    popb->list_uid_old = NULL;
+    while(fgets(buf, 255, fptr)){
+      if(buf[strlen(buf)-1] == '\n'){
+	g_strchomp(buf);
+	popb->list_uid_old =
+	  g_list_append(popb->list_uid_old, g_strdup(buf));
+      }else{
+	logwrite(LOG_ALERT, "broken uid: %s\n", buf);
+	break;
+      }
+    }
+    fclose(fptr);
+    ok = TRUE;
+  }else
+    logwrite(LOG_ALERT, "opening of %s failed: %s", filename, strerror(errno));
+  return ok;
+}
+
+static
+gboolean read_uidl(pop3_base *popb, gchar *user)
+{
+  gboolean ok = FALSE;
+  struct stat statbuf;
+  gchar *filename = g_strdup_printf("%s/popuidl/%s@%s",
+				    conf.spool_dir,
+				    user, popb->remote_host);
+
+  if(stat(filename, &statbuf) == 0){
+    ok = read_uidl_fname(popb, filename);
+    if(ok){
+      GList *drop_node;
+      foreach(popb->drop_list, drop_node){
+	msg_info *info = (msg_info *)(drop_node->data);
+	if(find_uid(popb, info->uid)){
+	  DEBUG(5) debugf("msg with uid '%s' already known\n", info->uid);
+	  info->is_in_uidl = TRUE;
+	  popb->uidl_known_cnt++;
+	}else
+	  DEBUG(5) debugf("msg with uid '%s' not known\n", info->uid);
+      }
+    }
+  }else{
+    logwrite(LOG_DEBUG, "no uidl file '%s' found\n", filename);
+    ok = TRUE;
+  }
+
+  g_free(filename);
+  return ok; /* return code is irrelevant, do not check... */
+}
+
+static
+gboolean read_response(pop3_base *popb, int timeout)
+{
+  gint len;
+
+  len = read_sockline(popb->in, popb->buffer, POP3_BUF_LEN, timeout, READSOCKL_CHUG);
+
+  if(len == -3){
+    popb->error = pop3_timeout;
+    return FALSE;
+  }
+  else if(len == -2){
+    popb->error = pop3_syntax;
+    return FALSE;
+  }
+  else if(len == -1){
+    popb->error = pop3_eof;
+    return FALSE;
+  }
+  
+  return TRUE;
+}
+
+static
+gboolean check_response(pop3_base *popb)
+{
+  char c = popb->buffer[0];
+
+  if(c == '+'){
+    popb->error = pop3_ok;
+    return TRUE;
+  }else if(c == '-')
+    popb->error = pop3_fail;
+  else
+    popb->error = pop3_syntax;
+  return FALSE;
+}
+
+static
+gboolean strtoi(gchar *p, gchar **pend, gint *val)
+{
+  gchar buf[12];
+  gint i = 0;
+
+  while(*p && isspace(*p)) p++;
+  if(*p){
+    while((i < 11) && isdigit(*p))
+      buf[i++] = *(p++);
+    buf[i] = 0;
+    *val = atoi(buf);
+    *pend = p;
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static
+gboolean check_response_int_int(pop3_base *popb, gint *arg0, gint *arg1)
+{
+  if(check_response(popb)){
+    gchar *p = &(popb->buffer[3]);
+    gchar *pe;
+
+    if(strtoi(p, &pe, arg0)){
+      DEBUG(5) debugf("arg0 = %d\n", *arg0);
+      p = pe;
+      if(strtoi(p, &pe, arg1))
+	DEBUG(5) debugf("arg1 = %d\n", *arg1);
+	return TRUE;
+    }
+    popb->error = pop3_syntax;
+  }
+  return FALSE;
+}
+
+static
+gboolean get_drop_listing(pop3_base *popb)
+{
+  gchar buf[64];
+
+  DEBUG(5) debugf("get_drop_listing() entered\n");
+
+  while(1){
+    gint len = read_sockline(popb->in, buf, 64, POP3_CMD_TIMEOUT, READSOCKL_CHUG);
+    if(len > 0){
+      if(buf[0] == '.')
+	return TRUE;
+      else{
+	gint number, msg_size;
+	gchar *p = buf, *pe;
+	if(strtoi(p, &pe, &number)){
+	  p = pe;
+	  if(strtoi(p, &pe, &msg_size)){
+	    msg_info *info = g_malloc(sizeof(msg_info));
+	    info->number = number;
+	    info->size = msg_size;
+
+	    DEBUG(5) debugf("get_drop_listing(), number = %d, msg_size = %d\n", number, msg_size);
+
+	    info->uid = NULL;
+	    info->is_fetched = FALSE;
+	    info->is_in_uidl = FALSE;
+	    popb->drop_list = g_list_append(popb->drop_list, info);
+	  }else{
+	    popb->error = pop3_syntax;
+	    break;
+	  }
+	}else{
+	  popb->error = pop3_syntax;
+	  break;
+	}
+      }
+    }else{
+      popb->error = (len == -1) ? pop3_eof : pop3_timeout;
+      return FALSE;
+    }
+  }
+  return FALSE;
+}
+
+static
+gboolean get_uid_listing(pop3_base *popb)
+{
+  gchar buf[64];
+
+  while(1){
+    gint len = read_sockline(popb->in, buf, 64, POP3_CMD_TIMEOUT, READSOCKL_CHUG);
+    if(len > 0){
+      if(buf[0] == '.')
+	return TRUE;
+      else{
+	gint number;
+	gchar *p = buf, *pe;
+	if(strtoi(p, &pe, &number)){
+	  msg_info *info = NULL;
+	  GList *drop_node;
+
+	  p = pe;
+	  while(*p && isspace(*p)) p++;
+
+	  foreach(popb->drop_list, drop_node){
+	    msg_info *curr_info = (msg_info *)(drop_node->data);
+	    if(curr_info->number == number){
+	      info = curr_info;
+	      break;
+	    }
+	  }
+	  if(info){
+	    info->uid = g_strdup(p);
+	    g_strchomp(info->uid);
+	  }
+
+	}else{
+	  popb->error = pop3_syntax;
+	  break;
+	}
+      }
+    }
+  }
+  return FALSE;
+}
+
+static
+gboolean check_init_response(pop3_base *popb)
+{
+  if(check_response(popb)){
+    gchar buf[256];
+    gchar *p = popb->buffer;
+    gint i = 0;
+    if(*p){
+      while(*p && (*p != '<')) p++;
+      while(*p && (*p != '>') && (i < 254))
+	buf[i++] = *(p++);
+      buf[i++] = '>';
+      buf[i] = 0;
+
+      popb->timestamp = g_strdup(buf);
+
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+void pop3_in_close(pop3_base *popb)
+{
+  GList *node;
+
+  fclose(popb->in);
+  fclose(popb->out);
+
+  close(popb->sock);
+
+  foreach(popb->list_uid_old, node){
+    gchar *uid = (gchar *)(node->data);
+    g_free(uid);
+  }
+  g_list_free(popb->list_uid_old);
+
+  foreach(popb->drop_list, node){
+    msg_info *info = (msg_info *)(node->data);
+    if(info->uid) g_free(info->uid);
+    g_free(info);
+  }
+  g_list_free(popb->drop_list);
+
+  if(popb->buffer) g_free(popb->buffer);
+  if(popb->timestamp) g_free(popb->timestamp);
+}
+
+pop3_base *pop3_in_open(gchar *host, gint port, GList *resolve_list, guint flags)
+{
+  pop3_base *popb;
+  gint sock;
+  mxip_addr *addr;
+
+  DEBUG(5) debugf("pop3_in_open entered, host = %s\n", host);
+
+  if((addr = connect_resolvelist(&sock, host, port, resolve_list))){
+    /* create structure to hold status data: */
+    popb = create_pop3base(sock, flags);
+    popb->remote_host = addr->name;
+
+    DEBUG(5){
+      struct sockaddr_in name;
+      int len;
+      getsockname(sock, (struct sockaddr *)(&name), &len);
+      debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
+    }
+    return popb;
+  }
+  return NULL;
+}
+
+pop3_base *pop3_in_open_child(gchar *cmd, guint flags)
+{
+  pop3_base *popb;
+  gint sock;
+
+  DEBUG(5) debugf("pop3_in_open_child entered, cmd = %s\n", cmd);
+
+  sock = child(cmd);
+
+  if(sock > 0){
+
+    popb = create_pop3base(sock, flags);
+    popb->remote_host = NULL;
+
+    return popb;
+  }
+  logwrite(LOG_ALERT, "child failed (sock = %d): %s\n", sock, strerror(errno));
+
+  return NULL;
+}
+
+gboolean pop3_in_init(pop3_base *popb)
+{
+  gboolean ok;
+
+  if((ok = read_response(popb, POP3_INITIAL_TIMEOUT))){
+    ok = check_init_response(popb);
+  }
+  if(!ok)
+    /*    pop3_in_log_failure(popb, NULL);*/
+    logwrite(LOG_ALERT, "pop3 failed\n");
+  return ok;
+}
+
+gboolean pop3_in_login(pop3_base *popb, gchar *user, gchar *pass)
+{
+  if(popb->flags & POP3_FLAG_APOP){
+
+    gchar *string = g_strdup_printf("%s%s", popb->timestamp, pass);
+    gchar *digest = MD5String(string);
+    pop3_printf(popb->out, "APOP %s %s\r\n", user, digest);
+    g_free(string);
+    g_free(digest);
+    if(read_response(popb, POP3_CMD_TIMEOUT)){
+      if(check_response(popb))
+	return TRUE;
+      else
+	popb->error = pop3_login_failure;
+    }
+
+  }else{
+
+    pop3_printf(popb->out, "USER %s\r\n", user);
+    if(read_response(popb, POP3_CMD_TIMEOUT)){
+      if(check_response(popb)){
+	pop3_printf(popb->out, "PASS %s\r\n", pass);
+	if(read_response(popb, POP3_CMD_TIMEOUT)){
+	  if(check_response(popb))
+	    return TRUE;
+	  else
+	    popb->error = pop3_login_failure;
+	}
+      }else{
+	popb->error = pop3_login_failure;
+      }
+    }
+  }
+  return FALSE;
+}
+
+gboolean pop3_in_stat(pop3_base *popb)
+{
+  pop3_printf(popb->out, "STAT\r\n");
+  if(read_response(popb, POP3_CMD_TIMEOUT)){
+    gint msg_cnt, mbox_size;
+    if(check_response_int_int(popb, &msg_cnt, &mbox_size)){
+      popb->msg_cnt = msg_cnt;
+      popb->mbox_size = mbox_size;
+
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+gboolean pop3_in_list(pop3_base *popb)
+{
+  pop3_printf(popb->out, "LIST\r\n");
+  if(read_response(popb, POP3_CMD_TIMEOUT)){
+    if(get_drop_listing(popb)){
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+gboolean pop3_in_dele(pop3_base *popb, gint number)
+{
+  pop3_printf(popb->out, "DELE %d\r\n", number);
+  if(read_response(popb, POP3_CMD_TIMEOUT)){
+    return TRUE;
+  }
+  return FALSE;
+}
+
+message *pop3_in_retr(pop3_base *popb, gint number, address *rcpt)
+{
+  accept_error err;
+
+  pop3_printf(popb->out, "RETR %d\r\n", number);
+  if(read_response(popb, POP3_CMD_TIMEOUT)){
+    message *msg = create_message();
+    msg->received_host = popb->remote_host;
+    msg->received_prot = (popb->flags & POP3_FLAG_APOP) ? PROT_APOP : PROT_POP3;
+    msg->transfer_id = (popb->next_id)++;
+    msg->rcpt_list = g_list_append(NULL, copy_address(rcpt));
+
+    if((err = accept_message(popb->in, msg,
+			     ACC_MAIL_FROM_HEAD|(conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0)))
+       == AERR_OK)
+      return msg;
+
+    destroy_message(msg);
+  }
+  return NULL;
+}  
+
+gboolean pop3_in_uidl(pop3_base *popb)
+{
+  pop3_printf(popb->out, "UIDL\r\n");
+  if(read_response(popb, POP3_CMD_TIMEOUT)){
+    if(get_uid_listing(popb)){
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+gboolean pop3_in_quit(pop3_base *popb)
+{
+  pop3_printf(popb->out, "QUIT\r\n");
+  
+  DEBUG(4) debugf("QUIT\n");
+
+  signal(SIGALRM, SIG_DFL);
+
+  return TRUE;
+}
+
+/* Send a DELE command for each message in (the old) uid listing.
+   This is to prevent mail from to be kept on server, if a previous
+   transaction was interupted. */
+gboolean pop3_in_uidl_dele(pop3_base *popb)
+{
+  GList *drop_node;
+
+  foreach(popb->drop_list, drop_node){
+    msg_info *info = (msg_info *)(drop_node->data);
+    /*    if(find_uid(popb, info->uid)){*/
+    if(info->is_in_uidl){
+      if(!pop3_in_dele(popb, info->number))
+	return FALSE;
+      /* TODO: it probably makes sense to also
+	 delete this uid from the listing */
+    }
+  }
+  return TRUE;
+}
+
+gboolean pop3_get(pop3_base *popb,
+		  gchar *user, gchar *pass, address *rcpt, address *return_path,
+		  gint max_count, gint max_size, gboolean max_size_delete)
+{
+  gboolean ok = FALSE;
+  gint num_children = 0;
+  
+  DEBUG(5) debugf("rcpt = %s@%s\n", rcpt->local_part, rcpt->domain);
+
+  signal(SIGCHLD, SIG_DFL);
+
+  if(pop3_in_init(popb)){
+    if(pop3_in_login(popb, user, pass)){
+      if(pop3_in_stat(popb)){
+	if(popb->msg_cnt > 0){
+
+	  logwrite(LOG_NOTICE|LOG_VERBOSE, "%d message(s) for user %s at %s\n",
+		   popb->msg_cnt, user, popb->remote_host);
+
+	  if(pop3_in_list(popb)){
+	    gboolean do_get = !(popb->flags & POP3_FLAG_UIDL);
+	    if(!do_get) do_get = pop3_in_uidl(popb);
+	    if(do_get){
+	      gint count = 0;
+	      GList *drop_node;
+
+	      if(popb->flags & POP3_FLAG_UIDL){
+		read_uidl(popb, user);
+		logwrite(LOG_VERBOSE|LOG_NOTICE, "%d message(s) already in uidl.\n",
+			 popb->uidl_known_cnt);
+	      }
+	      if((popb->flags & POP3_FLAG_UIDL) && (popb->flags & POP3_FLAG_UIDL_DELE))
+		pop3_in_uidl_dele(popb);
+
+	      foreach(popb->drop_list, drop_node){
+
+		msg_info *info = (msg_info *)(drop_node->data);
+		gboolean do_get_this = !(popb->flags & POP3_FLAG_UIDL);
+		/*		if(!do_get_this) do_get_this = !find_uid(popb, info->uid);*/
+		if(!do_get_this) do_get_this = !(info->is_in_uidl);
+		if(do_get_this){
+
+		  if((info->size < max_size) || (max_size == 0)){
+		    message *msg;
+
+		    logwrite(LOG_VERBOSE|LOG_NOTICE, "receiving message %d\n", info->number);
+		    msg = pop3_in_retr(popb, info->number, rcpt);
+
+		    if(msg){
+		      if(return_path)
+			msg->return_path = copy_address(return_path);
+		      if(spool_write(msg, TRUE)){
+			pid_t pid;
+			logwrite(LOG_NOTICE, "%s <= %s host=%s with %s\n",
+				 msg->uid,
+				 addr_string(msg->return_path),
+				 popb->remote_host,
+				 (popb->flags & POP3_FLAG_APOP) ?
+				 prot_names[PROT_APOP] : prot_names[PROT_POP3]
+				 );
+			info->is_fetched = TRUE;
+			count++;
+#if DO_WRITE_UIDL_EARLY
+			if(popb->flags & POP3_FLAG_UIDL) write_uidl(popb, user);
+#endif
+			if(!conf.do_queue){
+
+			  /* wait for child processes. If there are too many,
+			     we wait blocking, before we fork another one */
+			  while(num_children > 0){
+			    int status, options = WNOHANG;
+			    pid_t pid;
+
+			    if(num_children >= POP3_MAX_CHILDREN){
+			      logwrite(LOG_NOTICE, "too many children - waiting\n");
+			      options = 0;
+			    }
+			    if((pid = waitpid(0, &status, options)) > 0){
+			      num_children--;
+			      if(WEXITSTATUS(status) != EXIT_SUCCESS)
+				logwrite(LOG_WARNING,
+					 "delivery process with pid %d returned %d\n",
+					 pid, WEXITSTATUS(status));
+			      if(WIFSIGNALED(status))
+				logwrite(LOG_WARNING,
+					 "delivery process with pid %d got signal: %d\n",
+					 pid, WTERMSIG(status));
+			    }else if(pid < 0){
+			      logwrite(LOG_WARNING, "wait got error: %s\n", strerror(errno));
+			    }
+			  }
+
+			  if((pid = fork()) == 0){
+			    deliver(msg);
+			    _exit(EXIT_SUCCESS);
+			  }else if(pid < 0){
+			    logwrite(LOG_ALERT|LOG_VERBOSE,
+				     "could not fork for delivery, id = %s: %s\n",
+				     msg->uid, strerror(errno));
+			  }else
+			    num_children++;
+			}else{
+			  DEBUG(1) debugf("queuing forced by configuration or option.\n");
+			}
+			if(popb->flags & POP3_FLAG_DELETE)
+			  pop3_in_dele(popb, info->number);
+
+			destroy_message(msg);
+		      }/* if(spool_write(msg, TRUE)) */
+		    }else{
+		      logwrite(LOG_ALERT,
+			       "retrieving of message %d failed: %d\n",
+			       info->number, popb->error);
+		    }
+		  }/* if((info->size > max_size) ... */
+		  else{
+		    logwrite(LOG_NOTICE|LOG_VERBOSE, "size of message #%d (%d) > max_size (%d)\n",
+			     info->number, info->size, max_size);
+		    if(max_size_delete)
+			if(popb->flags & POP3_FLAG_DELETE)
+			  pop3_in_dele(popb, info->number);
+		  }
+		}/* if(do_get_this) ... */
+		else{
+		  if(popb->flags & POP3_FLAG_UIDL){
+		    info->is_fetched = TRUE; /* obsolete? */
+		    logwrite(LOG_VERBOSE, "message %d already known\n",
+			     info->number);
+		    DEBUG(1) debugf("message %d (uid = %s) not fetched\n",
+				    info->number, info->uid);
+#if 0
+#if DO_WRITE_UIDL_EARLY
+		    write_uidl(popb, user); /* obsolete? */
+#endif
+#endif
+		  }
+		}
+		if((max_count != 0) && (count >= max_count))
+		  break;
+	      }/* foreach() */
+#if DO_WRITE_UIDL_EARLY
+#else
+	      if(popb->flags & POP3_FLAG_UIDL) write_uidl(popb, user);
+#endif
+	    }/* if(pop3_in_uidl(popb) ... */
+	  }/* if(pop3_in_list(popb)) */
+	}/* if(popb->msg_cnt > 0) */
+	else{
+	  logwrite(LOG_NOTICE|LOG_VERBOSE,
+		   "no messages for user %s at %s\n", user, popb->remote_host);
+	}
+	ok = TRUE;
+      }
+      pop3_in_quit(popb);
+    }else{
+      logwrite(LOG_ALERT|LOG_VERBOSE,
+	       "pop3 login failed for user %s, host = %s\n", user, popb->remote_host);
+    }
+  }
+  if(!ok){
+    logwrite(LOG_ALERT|LOG_VERBOSE, "pop3 failed, error = %d\n", popb->error);
+  }
+
+  while(num_children > 0){
+    int status;
+    pid_t pid;
+    if((pid = wait(&status)) > 0){
+      num_children--;
+      if(WEXITSTATUS(status) != EXIT_SUCCESS)
+	logwrite(LOG_WARNING,
+		 "delivery process with pid %d returned %d\n",
+		 pid, WEXITSTATUS(status));
+      if(WIFSIGNALED(status))
+	logwrite(LOG_WARNING,
+		 "delivery process with pid %d got signal: %d\n",
+		 pid, WTERMSIG(status));
+    }else{
+      logwrite(LOG_WARNING, "wait got error: %s\n", strerror(errno));
+    }
+  }
+
+  return ok;
+}
+
+/* function just to log into a pop server,
+   for pop_before_smtp (or is it smtp_after_pop?)
+*/
+
+gboolean pop3_login(gchar *host, gint port, GList *resolve_list,
+		    gchar *user, gchar *pass, guint flags)
+{
+  gboolean ok = FALSE;
+  pop3_base *popb;
+
+  signal(SIGCHLD, SIG_IGN);
+
+  if((popb = pop3_in_open(host, port, resolve_list, flags))){
+    if(pop3_in_init(popb)){
+      if(pop3_in_login(popb, user, pass))
+	ok = TRUE;
+      else
+	logwrite(LOG_ALERT|LOG_VERBOSE,
+		 "pop3 login failed for user %s, host = %s\n", user, host);
+    }
+    pop3_in_close(popb);
+  }
+  return ok;
+}
+
+#endif