--- /dev/null
+/*
+ * Copyright (C) 2004, 2003, 2004 Mads Martin Joergensen <mmj at mmj.dk>
+ * Copyright (C) 2022 Baptiste Daroussin <bapt@FreeBSD.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#pragma once
+
+int initsmtp(int *sockfd, const char *relayhost, unsigned short port, const char *heloname);
+int endsmtp(int *sockfd);
unistr.c gethdrline.c send_digest.c \
writen.c getlistaddr.c strgen.c statctrl.c \
ctrlvalue.c readn.c getlistdelim.c ctrlvalues.c \
- utils.c mlmmj.c
+ utils.c mlmmj.c send_mail.c
mlmmj_send_SOURCES = mlmmj-send.c
mlmmj_send_LDADD= libmlmmj.a
#include "stdbool.h"
#include "getaddrsfromfd.h"
#include "utils.h"
+#include "send_mail.h"
static int addtohdr = false;
static int prepmailinmem = 0;
return 0;
}
-int initsmtp(int *sockfd, const char *relayhost, unsigned short port, const char *heloname)
-{
- int retval = 0;
- int try_ehlo = 1;
- char *reply = NULL;
-
- do {
- init_sockfd(sockfd, relayhost, port);
-
- if(*sockfd == -1) {
- retval = EBADF;
- break;
- }
-
- if((reply = checkwait_smtpreply(*sockfd, MLMMJ_CONNECT)) != NULL) {
- log_error(LOG_ARGS, "No proper greeting to our connect"
- "Reply: [%s]", reply);
- free(reply);
- retval = MLMMJ_CONNECT;
- /* FIXME: Queue etc. */
- break;
- }
-
- if (try_ehlo) {
- write_ehlo(*sockfd, heloname);
- if((reply = checkwait_smtpreply(*sockfd, MLMMJ_EHLO))
- == NULL) {
- /* EHLO successful don't try more */
- break;
- }
-
- /* RFC 1869 - 4.5. - In the case of any error response,
- * the client SMTP should issue either the HELO or QUIT
- * command.
- * RFC 1869 - 4.5. - If the server SMTP recognizes the
- * EHLO command, but the command argument is
- * unacceptable, it will return code 501.
- */
- if (strncmp(reply, "501", 3) == 0) {
- free(reply);
- /* Commmand unacceptable; we choose to QUIT but
- * ignore any QUIT errors; return that EHLO was
- * the error.
- */
- endsmtp(sockfd);
- retval = MLMMJ_EHLO;
- break;
- }
-
- /* RFC 1869 - 4.6. - A server SMTP that conforms to RFC
- * 821 but does not support the extensions specified
- * here will not recognize the EHLO command and will
- * consequently return code 500, as specified in RFC
- * 821. The server SMTP should stay in the same state
- * after returning this code (see section 4.1.1 of RFC
- * 821). The client SMTP may then issue either a HELO
- * or a QUIT command.
- */
-
- if (reply[0] != '5') {
- free(reply);
- /* Server doesn't understand EHLO, but gives a
- * broken response. Try with new connection.
- */
- endsmtp(sockfd);
- try_ehlo = 0;
- continue;
- }
-
- free(reply);
-
- /* RFC 1869 - 4.7. - Other improperly-implemented
- * servers will not accept a HELO command after EHLO has
- * been sent and rejected. In some cases, this problem
- * can be worked around by sending a RSET after the
- * failure response to EHLO, then sending the HELO.
- */
- write_rset(*sockfd);
- reply = checkwait_smtpreply(*sockfd, MLMMJ_RSET);
-
- /* RFC 1869 - 4.7. - Clients that do this should be
- * aware that many implementations will return a failure
- * code (e.g., 503 Bad sequence of commands) in response
- * to the RSET. This code can be safely ignored.
- */
- free(reply);
-
- /* Try HELO on the same connection
- */
- }
-
- write_helo(*sockfd, heloname);
- if((reply = checkwait_smtpreply(*sockfd, MLMMJ_HELO))
- == NULL) {
- /* EHLO successful don't try more */
- break;
- }
- if (try_ehlo) {
- free(reply);
- /* We reused a connection we tried EHLO on. Maybe
- * that's why it failed. Try with new connection.
- */
- endsmtp(sockfd);
- try_ehlo = 0;
- continue;
- }
-
- log_error(LOG_ARGS, "Error with HELO. Reply: "
- "[%s]", reply);
- free(reply);
- /* FIXME: quit and tell admin to configure
- * correctly */
- retval = MLMMJ_HELO;
- break;
-
- } while (1);
-
- return retval;
-}
-
-int endsmtp(int *sockfd)
-{
- int retval = 0;
- char *reply = NULL;
-
- if(*sockfd == -1)
- return retval;
-
- write_quit(*sockfd);
- reply = checkwait_smtpreply(*sockfd, MLMMJ_QUIT);
- if(reply) {
- printf("reply from quit: %s\n", reply);
- log_error(LOG_ARGS, "Mailserver would not let us QUIT. "
- "We close the socket anyway though. "
- "Mailserver reply = [%s]", reply);
- free(reply);
- retval = MLMMJ_QUIT;
- }
-
- close(*sockfd);
- *sockfd = -1;
-
- return retval;
-}
-
int send_mail_verp(int sockfd, struct strlist *addrs, char *mailmap,
size_t mailsize, const char *from,
const char *hdrs, const char *body, const char *verpextra)
--- /dev/null
+/*
+ * Copyright (C) 2004, 2003, 2004 Mads Martin Joergensen <mmj at mmj.dk>
+ * Copyright (C) 2022 Baptiste Daroussin <bapt@FreeBSD.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "checkwait_smtpreply.h"
+#include "mail-functions.h"
+#include "send_mail.h"
+#include "log_error.h"
+#include "init_sockfd.h"
+#include "mlmmj.h"
+#include "xmalloc.h"
+
+int
+initsmtp(int *sockfd, const char *relayhost, unsigned short port, const char *heloname)
+{
+ int retval = 0;
+ int try_ehlo = 1;
+ char *reply = NULL;
+
+ do {
+ init_sockfd(sockfd, relayhost, port);
+
+ if(*sockfd == -1) {
+ retval = EBADF;
+ break;
+ }
+
+ if((reply = checkwait_smtpreply(*sockfd, MLMMJ_CONNECT)) != NULL) {
+ log_error(LOG_ARGS, "No proper greeting to our connect"
+ "Reply: [%s]", reply);
+ free(reply);
+ retval = MLMMJ_CONNECT;
+ /* FIXME: Queue etc. */
+ break;
+ }
+
+ if (try_ehlo) {
+ write_ehlo(*sockfd, heloname);
+ if((reply = checkwait_smtpreply(*sockfd, MLMMJ_EHLO))
+ == NULL) {
+ /* EHLO successful don't try more */
+ break;
+ }
+
+ /* RFC 1869 - 4.5. - In the case of any error response,
+ * the client SMTP should issue either the HELO or QUIT
+ * command.
+ * RFC 1869 - 4.5. - If the server SMTP recognizes the
+ * EHLO command, but the command argument is
+ * unacceptable, it will return code 501.
+ */
+ if (strncmp(reply, "501", 3) == 0) {
+ free(reply);
+ /* Commmand unacceptable; we choose to QUIT but
+ * ignore any QUIT errors; return that EHLO was
+ * the error.
+ */
+ endsmtp(sockfd);
+ retval = MLMMJ_EHLO;
+ break;
+ }
+
+ /* RFC 1869 - 4.6. - A server SMTP that conforms to RFC
+ * 821 but does not support the extensions specified
+ * here will not recognize the EHLO command and will
+ * consequently return code 500, as specified in RFC
+ * 821. The server SMTP should stay in the same state
+ * after returning this code (see section 4.1.1 of RFC
+ * 821). The client SMTP may then issue either a HELO
+ * or a QUIT command.
+ */
+
+ if (reply[0] != '5') {
+ free(reply);
+ /* Server doesn't understand EHLO, but gives a
+ * broken response. Try with new connection.
+ */
+ endsmtp(sockfd);
+ try_ehlo = 0;
+ continue;
+ }
+
+ free(reply);
+
+ /* RFC 1869 - 4.7. - Other improperly-implemented
+ * servers will not accept a HELO command after EHLO has
+ * been sent and rejected. In some cases, this problem
+ * can be worked around by sending a RSET after the
+ * failure response to EHLO, then sending the HELO.
+ */
+ write_rset(*sockfd);
+ reply = checkwait_smtpreply(*sockfd, MLMMJ_RSET);
+
+ /* RFC 1869 - 4.7. - Clients that do this should be
+ * aware that many implementations will return a failure
+ * code (e.g., 503 Bad sequence of commands) in response
+ * to the RSET. This code can be safely ignored.
+ */
+ free(reply);
+
+ /* Try HELO on the same connection
+ */
+ }
+
+ write_helo(*sockfd, heloname);
+ if((reply = checkwait_smtpreply(*sockfd, MLMMJ_HELO))
+ == NULL) {
+ /* EHLO successful don't try more */
+ break;
+ }
+ if (try_ehlo) {
+ free(reply);
+ /* We reused a connection we tried EHLO on. Maybe
+ * that's why it failed. Try with new connection.
+ */
+ endsmtp(sockfd);
+ try_ehlo = 0;
+ continue;
+ }
+
+ log_error(LOG_ARGS, "Error with HELO. Reply: "
+ "[%s]", reply);
+ free(reply);
+ /* FIXME: quit and tell admin to configure
+ * correctly */
+ retval = MLMMJ_HELO;
+ break;
+
+ } while (1);
+
+ return retval;
+}
+
+int
+endsmtp(int *sockfd)
+{
+ int retval = 0;
+ char *reply = NULL;
+
+ if(*sockfd == -1)
+ return retval;
+
+ write_quit(*sockfd);
+ reply = checkwait_smtpreply(*sockfd, MLMMJ_QUIT);
+ if(reply) {
+ printf("reply from quit: %s\n", reply);
+ log_error(LOG_ARGS, "Mailserver would not let us QUIT. "
+ "We close the socket anyway though. "
+ "Mailserver reply = [%s]", reply);
+ free(reply);
+ retval = MLMMJ_QUIT;
+ }
+
+ close(*sockfd);
+ *sockfd = -1;
+
+ return retval;
+}
+
#include "init_sockfd.h"
#include "checkwait_smtpreply.h"
#include "find_email_adr.h"
+#include "send_mail.h"
ATF_TC_WITHOUT_HEAD(random_int);
ATF_TC_WITHOUT_HEAD(chomp);
ATF_TC_WITHOUT_HEAD(unsubscribe);
ATF_TC_WITHOUT_HEAD(genlistname);
ATF_TC_WITHOUT_HEAD(genlistfqdn);
+ATF_TC_WITHOUT_HEAD(smtp);
#ifndef NELEM
#define NELEM(array) (sizeof(array) / sizeof((array)[0]))
ATF_REQUIRE_STREQ(ret, "bla@meh");
}
+ATF_TC_BODY(smtp, tc)
+{
+ int smtppipe[2];
+ char *reply;
+ ATF_REQUIRE(pipe(smtppipe) >= 0);
+ pid_t p = atf_utils_fork();
+ if (p == 0) {
+ dprintf(smtppipe[0], "220 me fake smtp\n");
+ /* EHLO */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "250-hostname.net\n"
+ "250-PIPELINEING\n"
+ "250-SIZE 20480000\n"
+ "250-ETRN\n"
+ "250-STARTTLS\n"
+ "250-ENHANCEDSTATUSCODES\n"
+ "250-8BITMIME\n"
+ "250-DSN\n"
+ "250-SMTPUTF8\n"
+ "250 CHUNKING\n");
+ /* HELO */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "250-hostname.net\n");
+ /* MAIL FROM */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "250 2.1.0 Ok\n");
+ /* RCPT TO */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "250 2.1.0 Ok\n");
+ /* DATA */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "354 Send message content; end with <CRLF>.<CRLF>\n");
+ /* DATA */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "250 2.1.0 Ok\n");
+ /* QUIT */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "221 2.0.0 Bye\n");
+ /* RSET */
+ reply = mygetline(smtppipe[0]);
+ dprintf(smtppipe[0],
+ "250 2.0.0 Ok\n");
+ exit (0);
+ }
+ close(smtppipe[0]);
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_CONNECT);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_ehlo(smtppipe[1], "plop");
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_EHLO);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_helo(smtppipe[1], "plop");
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_HELO);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_mail_from(smtppipe[1], "plop", NULL);
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_FROM);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_rcpt_to(smtppipe[1], "plop");
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_RCPTTO);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_data(smtppipe[1]);
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_DATA);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_dot(smtppipe[1]);
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_DOT);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_quit(smtppipe[1]);
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_QUIT);
+ ATF_REQUIRE_EQ(reply, NULL);
+ write_rset(smtppipe[1]);
+ reply = checkwait_smtpreply(smtppipe[1], MLMMJ_RSET);
+ ATF_REQUIRE_EQ(reply, NULL);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, random_int);
ATF_TP_ADD_TC(tp, unsubscribe);
ATF_TP_ADD_TC(tp, genlistname);
ATF_TP_ADD_TC(tp, genlistfqdn);
+ ATF_TP_ADD_TC(tp, smtp);
return (atf_no_error());
}