#include <sys/time.h>
#include <signal.h>
+#ifdef HAVE_NET_SOCKET_H
+#include <net/socket.h>
+#endif
+
#ifdef HAVE_RES_SEARCH
#include <netdb.h>
#include "mx.h"
int batchcount; /* count of messages sent in current batch */
flag peek_capable; /* can we peek for better error recovery? */
int pass; /* how many times have we re-polled? */
+int stage; /* where are we? */
int phase; /* where are we, for error-logging purposes? */
+int mytimeout; /* value of nonreponse timeout */
static const struct method *protocol;
static jmp_buf restart;
#define GENSYM (sprintf(tag, "A%04d", ++tagnum % TAGMOD), tag)
static char shroud[PASSWORDLEN]; /* string to shroud in debug output */
-static int mytimeout; /* value of nonreponse timeout */
static int timeoutcount; /* count consecutive timeouts */
static int msglen; /* actual message length */
void set_timeout(int timeleft)
/* reset the nonresponse-timeout */
{
-#ifndef __EMX__
+#if !defined(__EMX__) && !defined(__BEOS__)
struct itimerval ntimeout;
if (timeleft == 0)
}
}
+/*
+ * Return zero on a syntactically invalid address, nz on a valid one.
+ *
+ * This used to be strchr(a, '.'), but it turns out that lines like this
+ *
+ * Received: from punt-1.mail.demon.net by mailstore for markb@ordern.com
+ * id 938765929:10:27223:2; Fri, 01 Oct 99 08:18:49 GMT
+ *
+ * are not uncommon. So now we just check that the following token is
+ * not itself an email address.
+ */
+#define VALID_ADDRESS(a) !strchr(a, '@')
+
static char *parse_received(struct query *ctl, char *bufp)
/* try to extract real address from the Received line */
/* If a valid Received: line is found, we return the full address in
if (outlevel >= O_DEBUG)
report(stdout, _("analyzing Received line:\n%s"), bufp);
- /* search for whitepace-surrounded "by" followed by xxxx.yyyy */
+ /* search for whitepace-surrounded "by" followed by valid address */
for (base = bufp; ; base = ok + 2)
{
if (!(ok = strstr(base, "by")))
*tp++ = *sp;
*tp = '\0';
- /* look for embedded periods */
- if (strchr(rbuf, '.'))
+ /* look for valid address */
+ if (VALID_ADDRESS(rbuf))
break;
else
ok = sp - 1; /* arrange to skip this token */
int from_offs, reply_to_offs, resent_from_offs;
int app_from_offs, sender_offs, resent_sender_offs;
int env_offs;
- char *received_for, *rcv, *cp;
+ char *received_for, *rcv, *cp, *delivered_to;
int n, linelen, oldlen, ch, remaining, skipcount;
struct idlist *idp;
flag no_local_matches = FALSE;
/* read message headers */
msgblk.reallen = reallen;
- msgblk.headers = received_for = NULL;
+
+ /*
+ * We used to free the header block unconditionally at the end of
+ * readheaders, but it turns out that if close_sink() hits an error
+ * condition the code for sending bouncemail will actually look
+ * at the freed storage and coredump...
+ */
+ if (msgblk.headers)
+ free(msgblk.headers);
+
+ msgblk.headers = received_for = delivered_to = NULL;
from_offs = reply_to_offs = resent_from_offs = app_from_offs =
sender_offs = resent_sender_offs = env_offs = -1;
oldlen = 0;
sizeticker += linelen;
while (sizeticker >= SIZETICKER)
{
- if (!run.use_syslog)
+ if (!run.use_syslog && !isafile(1))
{
fputc('.', stdout);
fflush(stdout);
/*
* When mail delivered to a multidrop mailbox on the server is
- * addressed to multiple people, there will be one copy left
- * in the box for each recipient. Thus, if the mail is addressed
- * to N people, each recipient would get N copies.
+ * addressed to multiple people on the client machine, there
+ * will be one copy left in the box for each recipient. Thus,
+ * if the mail is addressed to N people, each recipient will
+ * get N copies. This is bad when N > 1.
*
* Foil this by suppressing all but one copy of a message with
- * a given Message-ID. Note: This implementation only catches
- * runs of successive identical messages, but that should be
- * good enough.
+ * a given Message-ID. The accept_count test ensures that
+ * multiple pieces of email with the same Message-ID, each
+ * with a *single* addressee (the N == 1 case), won't be
+ * suppressed.
+ *
+ * Note: This implementation only catches runs of successive
+ * messages with the same ID, but that should be good
+ * enough. A more general implementation would have to store
+ * ever-growing lists of seen message-IDs; in a long-running
+ * daemon this would turn into a memory leak even if the
+ * implementation were perfect.
+ *
+ * Don't mess with this code casually. It would be way too easy
+ * to break it in a way that blackholed mail. Better to pass
+ * the occasional duplicate than to do that...
*/
if (MULTIDROP(ctl) && !strncasecmp(line, "Message-ID:", 11))
{
if (ctl->lastid && !strcasecmp(ctl->lastid, line))
- return(PS_REFUSED);
+ {
+ if (accept_count > 1)
+ return(PS_REFUSED);
+ }
else
{
if (ctl->lastid)
* forward it to the user so he or she will have some clue
* that things have gone awry.
*/
+#if INET6_ENABLE
+ if (strncmp(protocol->service, "pop2", 4))
+#else /* INET6_ENABLE */
if (protocol->port != 109)
+#endif /* INET6_ENABLE */
#endif /* POP2_ENABLE */
if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) {
free(line);
continue;
}
+ /*
+ * We remove all Delivered-To: headers.
+ *
+ * This is to avoid false mail loops messages when delivering
+ * local messages to and from a Postfix/qmail mailserver.
+ *
+ * Should be controlled by an option
+ */
+ if (ctl->dropdelivered && !strncasecmp(line, "Delivered-To:", 13)) {
+ if (delivered_to) free(line);
+ else delivered_to = line;
+ continue;
+ }
+
/*
* If we see a Status line, it may have been inserted by an MUA
* on the mail host, or it may have been inserted by the server
else if (!strncasecmp("Resent-Sender:", line, 14))
resent_sender_offs = (line - msgblk.headers);
- else if (!strncasecmp("Message-Id:", buf, 11))
+#ifdef __UNUSED__
+ else if (!strncasecmp("Message-Id:", line, 11))
{
if (ctl->server.uidl)
{
char id[IDLEN+1];
- buf[IDLEN+12] = 0; /* prevent stack overflow */
- sscanf(buf+12, "%s", id);
+ line[IDLEN+12] = 0; /* prevent stack overflow */
+ sscanf(line+12, "%s", id);
if (!str_find( &ctl->newsaved, num))
{
struct idlist *new = save_str(&ctl->newsaved,id,UID_SEEN);
}
}
}
+#endif /* __UNUSED__ */
else if (!MULTIDROP(ctl))
continue;
#endif /* SDPS_ENABLE */
if (env_offs > -1) /* We have the actual envelope addressee */
find_server_names(msgblk.headers + env_offs, ctl, &msgblk.recipients);
+ else if (delivered_to && ctl->server.envelope != STRING_DISABLED &&
+ ctl->server.envelope && !strcasecmp(ctl->server.envelope, "Delivered-To"))
+ {
+ find_server_names(delivered_to, ctl, &msgblk.recipients);
+ free(delivered_to);
+ }
else if (received_for)
/*
* We have the Received for addressee.
* We haven't extracted the envelope address.
* So check all the "Resent-To" header addresses if
* they exist. If and only if they don't, consider
- * the "To" adresses.
+ * the "To" addresses.
*/
register struct addrblk *nextptr;
if (resent_to_addrchain) {
if (!run.invisible && n != -1)
{
/* utter any per-message Received information we need here */
- sprintf(buf, "Received: from %s\r\n", ctl->server.truename);
+ if (ctl->server.trueaddr) {
+ sprintf(buf, "Received: from %s [%u.%u.%u.%u]\r\n",
+ ctl->server.truename,
+ (unsigned char)ctl->server.trueaddr[0],
+ (unsigned char)ctl->server.trueaddr[1],
+ (unsigned char)ctl->server.trueaddr[2],
+ (unsigned char)ctl->server.trueaddr[3]);
+ } else {
+ sprintf(buf, "Received: from %s\r\n", ctl->server.truename);
+ }
n = stuffline(ctl, buf);
if (n != -1)
{
free_str_list(&msgblk.recipients);
return(PS_IOERR);
}
- else if (!run.use_syslog && outlevel >= O_VERBOSE)
+ else if ((run.poll_interval == 0 || nodetach) && outlevel >= O_VERBOSE && !isafile(2))
fputs("#", stderr);
/* write error notifications */
*cp++ = '\0';
stuffline(ctl, buf);
- free(msgblk.headers);
+/* free(msgblk.headers); */
free_str_list(&msgblk.recipients);
return(headers_ok ? PS_SUCCESS : PS_TRUNCATED);
}
sizeticker += linelen;
while (sizeticker >= SIZETICKER)
{
- if (!run.use_syslog && outlevel > O_SILENT)
+ if ((run.poll_interval == 0 || nodetach) && outlevel > O_SILENT && !isafile(1))
{
fputc('.', stdout);
fflush(stdout);
/* check for end of message */
if (protocol->delimited && *inbufp == '.')
+ {
if (inbufp[1] == '\r' && inbufp[2] == '\n' && inbufp[3] == '\0')
break;
else if (inbufp[1] == '\n' && inbufp[2] == '\0')
break;
else
msglen--; /* subtract the size of the dot escape */
+ }
msglen += linelen;
release_sink(ctl);
return(PS_IOERR);
}
- else if (outlevel >= O_VERBOSE)
- fputc('*', stderr);
+ else if (outlevel >= O_VERBOSE && !isafile(1))
+ {
+ fputc('*', stdout);
+ fflush(stdout);
+ }
}
}
int msg_to_send = FALSE;
struct idlist *head=NULL, *current=NULL;
int max_warning_poll_count;
-#define OVERHD "Subject: Fetchmail oversized-messages warning.\r\n\r\nThe following oversized messages remain on the mail server %s:"
head = ctl->skipped;
if (!head)
*/
if (open_warning_by_mail(ctl, (struct msgblk *)NULL))
return;
- stuff_warning(ctl, OVERHD, ctl->server.pollname);
+ stuff_warning(ctl,
+ _("Subject: Fetchmail oversized-messages warning.\r\n"
+ "\r\n"
+ "The following oversized messages remain on the mail server %s:"),
+ ctl->server.pollname);
if (run.poll_interval == 0)
max_warning_poll_count = 0;
nbr = current->val.status.mark;
size = atoi(current->id);
stuff_warning(ctl,
- _("\t%d msg %d octets long skipped by fetchmail.\n"),
+ _("\t%d msg %d octets long skipped by fetchmail.\r\n"),
nbr, size);
}
current->val.status.num++;
}
close_warning_by_mail(ctl, (struct msgblk *)NULL);
-#undef OVERHD
}
static int do_session(ctl, proto, maxfetch)
const struct method *proto; /* protocol method table */
const int maxfetch; /* maximum number of messages to fetch */
{
- int ok, js;
+ int js;
#ifdef HAVE_VOLATILE
- volatile int mailserver_socket = -1; /* pacifies -Wall */
+ volatile int ok, mailserver_socket = -1; /* pacifies -Wall */
#else
- int mailserver_socket = -1;
+ int ok, mailserver_socket = -1;
#endif /* HAVE_VOLATILE */
const char *msg;
void (*pipesave)(int);
if (js == THROW_SIGPIPE)
{
+ signal(SIGPIPE, SIG_IGN);
report(stdout,
- _("SIGPIPE thrown from an MDA or a stream socket error"));
+ _("SIGPIPE thrown from an MDA or a stream socket error\n"));
ok = PS_SOCKET;
+ goto cleanUp;
}
else if (js == THROW_TIMEOUT)
{
ctl->mda ? "MDA" : "SMTP");
else if (phase == LISTENER_WAIT)
report(stdout,
- _("timeout after %d seconds waiting for listener to respond.\n"));
+ _("timeout after %d seconds waiting for listener to respond.\n"), ctl->server.timeout);
else
report(stdout,
_("timeout after %d seconds.\n"), ctl->server.timeout);
/*
* If we've exceeded our threshold for consecutive timeouts,
* try to notify the user, then mark the connection wedged.
+ * Don't do this if the connection can idle, though; idle
+ * timeouts just mean the frequency of mail is low.
*/
if (timeoutcount > MAX_TIMEOUTS
&& !open_warning_by_mail(ctl, (struct msgblk *)NULL))
stuff_warning(ctl,
_("Subject: fetchmail sees repeated timeouts\r\n"));
stuff_warning(ctl,
- _("Fetchmail saw more than %d timouts while attempting to get mail from %s@%s.\n"),
+ _("Fetchmail saw more than %d timeouts while attempting to get mail from %s@%s.\r\n"),
MAX_TIMEOUTS,
ctl->remotename,
ctl->server.truename);
stuff_warning(ctl,
- _("This could mean that your mailserver is stuck, or that your SMTP listener"));
- stuff_warning(ctl,
- _("is wedged, or that your mailbox file on the server has been corrupted by"));
- stuff_warning(ctl,
- _("a server error. You can run `fetchmail -v -v' to diagnose the problem."));
- stuff_warning(ctl,
- _("Fetchmail won't poll this mailbox again until you restart it."));
+ _("This could mean that your mailserver is stuck, or that your SMTP\r\n" \
+ "server is wedged, or that your mailbox file on the server has been\r\n" \
+ "corrupted by a server error. You can run `fetchmail -v -v' to\r\n" \
+ "diagnose the problem.\r\n\r\n" \
+ "Fetchmail won't poll this mailbox again until you restart it.\r\n"));
close_warning_by_mail(ctl, (struct msgblk *)NULL);
ctl->wedged = TRUE;
}
/* try to clean up all streams */
release_sink(ctl);
if (ctl->smtp_socket != -1)
- close(ctl->smtp_socket);
+ SockClose(ctl->smtp_socket);
if (mailserver_socket != -1)
SockClose(mailserver_socket);
}
else
{
- char buf[POPBUFSIZE+1], *realhost;
+ char buf[MSGBUFSIZE+1], *realhost;
int len, num, count, new, bytes, deletions = 0, *msgsizes = NULL;
-#if INET6
+#if INET6_ENABLE
int fetches, dispatches, oldphase;
-#else /* INET6 */
+#else /* INET6_ENABLE */
int port, fetches, dispatches, oldphase;
-#endif /* INET6 */
+#endif /* INET6_ENABLE */
struct idlist *idp;
/* execute pre-initialization command, if any */
oldphase = phase;
phase = OPEN_WAIT;
set_timeout(mytimeout);
-#if !INET6
+#if !INET6_ENABLE
+#ifdef SSL_ENABLE
+ port = ctl->server.port ? ctl->server.port : ( ctl->use_ssl ? protocol->sslport : protocol->port );
+#else
port = ctl->server.port ? ctl->server.port : protocol->port;
-#endif /* !INET6 */
+#endif
+#endif /* !INET6_ENABLE */
realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
/* allow time for the port to be set up if we have a plugin */
if (ctl->server.plugin)
(void)sleep(1);
-#if INET6
+#if INET6_ENABLE
if ((mailserver_socket = SockOpen(realhost,
- ctl->server.service ? ctl->server.service : protocol->service,
+ ctl->server.service ? ctl->server.service : ( ctl->use_ssl ? protocol->sslservice : protocol->service ),
ctl->server.netsec, ctl->server.plugin)) == -1)
-#else /* INET6 */
+#else /* INET6_ENABLE */
if ((mailserver_socket = SockOpen(realhost, port, NULL, ctl->server.plugin)) == -1)
-#endif /* INET6 */
+#endif /* INET6_ENABLE */
{
-#if !INET6
+ char errbuf[BUFSIZ];
+#if !INET6_ENABLE
int err_no = errno;
#ifdef HAVE_RES_SEARCH
if (err_no != 0 && h_errno != 0)
* in daemon mode but the connection to the outside world
* is down.
*/
- if (err_no == EHOSTUNREACH && run.poll_interval)
- goto ehostunreach;
-
- report_build(stderr, _("fetchmail: %s connection to %s failed"),
- protocol->name, ctl->server.pollname);
-#ifdef HAVE_RES_SEARCH
- if (h_errno != 0)
+ if (!((err_no == EHOSTUNREACH || err_no == ENETUNREACH)
+ && run.poll_interval))
{
- if (h_errno == HOST_NOT_FOUND)
- report_complete(stderr, _(": host is unknown\n"));
- else if (h_errno == NO_ADDRESS)
- report_complete(stderr, _(": name is valid but has no IP address\n"));
- else if (h_errno == NO_RECOVERY)
- report_complete(stderr, _(": unrecoverable name server error\n"));
- else if (h_errno == TRY_AGAIN)
- report_complete(stderr, _(": temporary name server error\n"));
+ report_build(stderr, _("fetchmail: %s connection to %s failed"),
+ protocol->name, ctl->server.pollname);
+#ifdef HAVE_RES_SEARCH
+ if (h_errno != 0)
+ {
+ if (h_errno == HOST_NOT_FOUND)
+ strcpy(errbuf, _("host is unknown."));
+#ifndef __BEOS__
+ else if (h_errno == NO_ADDRESS)
+ strcpy(errbuf, _("name is valid but has no IP address."));
+#endif
+ else if (h_errno == NO_RECOVERY)
+ strcpy(errbuf, _("unrecoverable name server error."));
+ else if (h_errno == TRY_AGAIN)
+ strcpy(errbuf, _("temporary name server error."));
+ else
+ sprintf(errbuf, _("unknown DNS error %d."), h_errno);
+ }
else
- report_complete(stderr, _(": unknown DNS error %d\n"), h_errno);
- }
- else
#endif /* HAVE_RES_SEARCH */
- report_complete(stderr, ": %s\n", strerror(err_no));
-
- ehostunreach:
-#endif /* INET6 */
+ strcpy(errbuf, strerror(err_no));
+ report_complete(stderr, ": %s\n", errbuf);
+
+#ifdef __UNUSED
+ /*
+ * Don't use this. It was an attempt to address Debian bug
+ * #47143 (Notify user by mail when pop server nonexistent).
+ * Trouble is, that doesn't work; you trip over the case
+ * where your SLIP or PPP link is down...
+ */
+ /* warn the system administrator */
+ if (open_warning_by_mail(ctl, (struct msgblk *)NULL) == 0)
+ {
+ stuff_warning(ctl,
+ _("Subject: Fetchmail unreachable-server warning.\r\n"
+ "\r\n"
+ "Fetchmail could not reach the mail server %s:")
+ ctl->server.pollname);
+ stuff_warning(ctl, errbuf, ctl->server.pollname);
+ close_warning_by_mail(ctl, (struct msgblk *)NULL);
+ }
+#endif
+ }
+#endif /* INET6_ENABLE */
ok = PS_SOCKET;
set_timeout(0);
phase = oldphase;
set_timeout(0);
phase = oldphase;
+#ifdef SSL_ENABLE
+ /* perform initial SSL handshake on open connection */
+ /* Note: We pass the realhost name over for certificate
+ verification. We may want to make this configurable */
+ if (ctl->use_ssl && SSLOpen(mailserver_socket,ctl->sslkey,ctl->sslcert,realhost) == -1)
+ {
+ report(stderr, _("SSL connection failed.\n"));
+ goto closeUp;
+ }
+#endif
+
#ifdef KERBEROS_V4
if (ctl->server.preauthenticate == A_KERBEROS_V4)
{
goto cleanUp;
/* try to get authorized to fetch mail */
+ stage = STAGE_GETAUTH;
if (protocol->getauth)
{
if (protocol->password_canonify)
report(stderr, _("Lock-busy error on %s@%s\n"),
ctl->remotename,
ctl->server.truename);
- else
+ else if (ok == PS_AUTHFAIL)
{
- if (ok == PS_ERROR)
- ok = PS_AUTHFAIL;
report(stderr, _("Authorization failure on %s@%s\n"),
ctl->remotename,
ctl->server.truename);
&& !open_warning_by_mail(ctl, (struct msgblk *)NULL))
{
stuff_warning(ctl,
- _("Subject: fetchmail authentication failed\r\n"));
+ _("Subject: fetchmail authentication failed\r\n"));
stuff_warning(ctl,
- _("Fetchmail could not get mail from %s@%s."),
- ctl->remotename,
- ctl->server.truename);
+ _("Fetchmail could not get mail from %s@%s.\r\n"),
+ ctl->remotename,
+ ctl->server.truename);
stuff_warning(ctl,
- _("The attempt to get authorization failed."));
- stuff_warning(ctl,
- _("This probably means your password is invalid."));
+ _("The attempt to get authorization failed.\r\n" \
+ "This probably means your password is invalid, but POP3 servers have\r\n" \
+ "other failure modes that fetchmail cannot distinguish from this\r\n" \
+ "because they don't send useful error messages on login failure.\r\n"));
close_warning_by_mail(ctl, (struct msgblk *)NULL);
ctl->wedged = TRUE;
}
}
+ else
+ report(stderr, _("Unknown login or authentication error on %s@%s\n"),
+ ctl->remotename,
+ ctl->server.truename);
+
goto cleanUp;
}
}
dispatches = 0;
++pass;
+ /* reset timeout, in case we did an IDLE */
+ mytimeout = ctl->server.timeout;
+
if (outlevel >= O_DEBUG)
+ {
if (idp->id)
report(stdout, _("selecting or re-polling folder %s\n"), idp->id);
else
report(stdout, _("selecting or re-polling default folder\n"));
+ }
/* compute # of messages and number of new messages waiting */
+ stage = STAGE_GETRANGE;
ok = (protocol->getrange)(mailserver_socket, ctl, idp->id, &count, &new, &bytes);
if (ok != 0)
goto cleanUp;
(void) sprintf(buf, _("%s at %s"),
ctl->remotename, ctl->server.truename);
if (outlevel > O_SILENT)
+ {
if (count == -1) /* only used for ETRN */
report(stdout, _("Polling %s\n"), ctl->server.truename);
else if (count != 0)
if (pass == 1 && (run.poll_interval == 0 || outlevel >= O_VERBOSE))
report(stdout, _("No mail for %s\n"), buf);
}
+ }
/* very important, this is where we leave the do loop */
if (count == 0)
if (new == -1 || ctl->fetchall)
new = count;
fetches = new; /* set error status ccorrectly */
- goto no_error;
+ /*
+ * There used to be a `got noerror' here, but this
+ * prevneted checking of multiple folders. This
+ * comment is a reminder in case I introduced some
+ * subtle bug by removing it...
+ */
}
else if (count > 0)
{
for (i = 0; i < count; i++)
msgsizes[i] = -1;
+ stage = STAGE_GETSIZES;
ok = (proto->getsizes)(mailserver_socket, count, msgsizes);
if (ok != 0)
goto cleanUp;
}
/* read, forward, and delete messages */
+ stage = STAGE_FETCH;
for (num = 1; num <= count; num++)
{
flag toolarge = NUM_NONZERO(ctl->limit)
{
if (outlevel > O_SILENT)
{
- report_build(stdout, _("skipping message %d"), num);
+ report_build(stdout,
+ _("skipping message %d (%d octets)"),
+ num, msgsizes[num-1]);
if (toolarge && !check_only)
{
char size[32];
*/
if (protocol->fetch_body && !suppress_readbody)
{
- if (outlevel >= O_VERBOSE)
+ if (outlevel >= O_VERBOSE && !isafile(1))
{
fputc('\n', stdout);
fflush(stdout);
/* tell server we got it OK and resynchronize */
if (protocol->trail)
{
- if (outlevel >= O_VERBOSE)
+ if (outlevel >= O_VERBOSE && !isafile(1))
{
fputc('\n', stdout);
fflush(stdout);
struct idlist *sdp;
for (sdp = ctl->newsaved; sdp; sdp = sdp->next)
- if (sdp->val.status.num == num)
+ if ((sdp->val.status.num == num)
+ && (!toolarge || oldmsg))
sdp->val.status.mark = UID_SEEN;
}
}
} while
/*
- * Only re-poll if we had some actual forwards, allowed
- * deletions and had no errors.
+ * Only re-poll if we either had some actual forwards and
+ * either allowed deletions and had no errors.
* Otherwise it is far too easy to get into infinite loops.
*/
(dispatches && protocol->retry && !ctl->keep && !ctl->errcount);
}
- no_error:
+ /* no_error: */
/* ordinary termination with no errors -- officially log out */
ok = (protocol->logout_cmd)(mailserver_socket, ctl);
/*
cleanUp:
/* we only get here on error */
if (ok != 0 && ok != PS_SOCKET)
+ {
+ stage = STAGE_LOGOUT;
(protocol->logout_cmd)(mailserver_socket, ctl);
+ }
SockClose(mailserver_socket);
}