1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/BBA1.5_platform/apps/public/mailx-12.5/smtp.c
2024-07-22 01:58:46 -03:00

729 lines
17 KiB
C
Executable File

/*
* Heirloom mailx - a mail user agent derived from Berkeley Mail.
*
* Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
*/
/*
* Copyright (c) 2000
* Gunnar Ritter. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Gunnar Ritter
* and his contributors.
* 4. Neither the name of Gunnar Ritter nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#ifdef DOSCCS
static char sccsid[] = "@(#)smtp.c 2.43 (gritter) 8/4/07";
#endif
#endif /* not lint */
#include "rcv.h"
#include <sys/utsname.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif /* HAVE_ARPA_INET_H */
#include <unistd.h>
#include <setjmp.h>
#include "extern.h"
#include "md5.h"
#include <mail.h>
/*
* Do not change, you get incorrect base64 encodings else!
*/
#ifndef INFIX_BUF
#define INFIX_BUF 972
#endif
/*
* Mail -- a mail program
*
* SMTP client and other internet related functions.
*/
static int verbose;
static int _debug;
static int write_temp_file_to_socket(struct sock *sp, char *tempMail, FILE *nfo);
//static void smtp_data(char *tempMail);
/*
* Return our hostname.
*/
char *
nodename(int mayoverride)
{
static char *hostname;
char *hn;
struct utsname ut;
struct addrinfo hints, *res;
if (mayoverride && (hn = value("hostname")) != NULL && *hn) {
free(hostname);
hostname = sstrdup(hn);
}
if (hostname == NULL) {
uname(&ut);
hn = ut.nodename;
memset(&hints, 0, sizeof hints);
hints.ai_socktype = SOCK_DGRAM; /* dummy */
hints.ai_flags = AI_CANONNAME;
if (getaddrinfo(hn, "0", &hints, &res) == 0) {
if (res->ai_canonname) {
hn = salloc(strlen(res->ai_canonname) + 1);
strcpy(hn, res->ai_canonname);
}
freeaddrinfo(res);
}
hostname = smalloc(strlen(hn) + 1);
strcpy(hostname, hn);
}
//return hostname;
return "Archer_VR200v";
}
/*
* Return the user's From: address(es).
*/
char *
myaddrs(struct header *hp)
{
char *cp, *hn;
static char *addr;
size_t sz;
if (hp != NULL && hp->h_from != NULL) {
if (hp->h_from->n_fullname)
return savestr(hp->h_from->n_fullname);
if (hp->h_from->n_name)
return savestr(hp->h_from->n_name);
}
if ((cp = value("from")) != NULL)
return cp;
/*
* When invoking sendmail directly, it's its task
* to generate a From: address.
*/
if (value("smtp") == NULL)
return NULL;
if (addr == NULL) {
hn = nodename(1);
sz = strlen(myname) + strlen(hn) + 2;
addr = smalloc(sz);
snprintf(addr, sz, "%s@%s", myname, hn);
}
return addr;
}
char *
myorigin(struct header *hp)
{
char *cp;
struct name *np;
if ((cp = myaddrs(hp)) == NULL ||
(np = sextract(cp, GEXTRA|GFULL)) == NULL)
return NULL;
return np->n_flink != NULL ? value("sender") : cp;
}
static int read_smtp(struct sock *sp, int value, int ign_eof);
static int talk_smtp(struct name *to, FILE *fi, struct sock *sp,
char *server, char *uhp, struct header *hp,
const char *user, const char *password, const char *skinned);
char *
smtp_auth_var(const char *type, const char *addr)
{
char *var, *cp;
int len;
var = ac_alloc(len = strlen(type) + strlen(addr) + 7);
snprintf(var, len, "smtp-auth%s-%s", type, addr);
if ((cp = value(var)) != NULL){
cp = savestr(cp);
}
else {
snprintf(var, len, "smtp-auth%s", type);
if ((cp = value(var)) != NULL)
cp = savestr(cp);
}
ac_free(var);
return cp;
}
static char *smtpbuf;
static size_t smtpbufsize;
/*
* Get the SMTP server's answer, expecting value.
*/
static int
read_smtp(struct sock *sp, int value, int ign_eof)
{
int ret;
int len;
do {
if ((len = sgetline(&smtpbuf, &smtpbufsize, NULL, sp)) < 6) {
if (len >= 0 && !ign_eof)
DEBUG_ERROR( catgets(catd, CATSET, 241,
"Unexpected EOF on SMTP connection\n"));
return -1;
}
if (verbose || debug || _debug)
DEBUG_ERROR(smtpbuf);
switch (*smtpbuf) {
case '1': ret = 1; break;
case '2': ret = 2; break;
case '3': ret = 3; break;
case '4': ret = 4; break;
default: ret = 5;
}
if (value != ret)
DEBUG_ERROR( catgets(catd, CATSET, 191,
"smtp-server: %s"), smtpbuf);
} while (smtpbuf[3] == '-');
return ret;
}
/*
* Macros for talk_smtp.
*/
#define _SMTP_ANSWER(x, ign_eof, err) \
if (!debug && !_debug) { \
int y; \
if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
(!(ign_eof) || y != -1)) { \
if (b != NULL) \
free(b); \
if (err == 0) {\
mail_exit(MAIL_STATUS_ERR_RES);\
} else {\
mail_exit(err);\
}\
} \
}
#define SMTP_ANSWER(x, err) _SMTP_ANSWER(x, 0, err)
#define SMTP_OUT(x) if (verbose || debug || _debug) \
DEBUG_ERROR(">>> %s", x); \
if (!debug && !_debug) \
swrite(sp, x);
static int write_temp_file_to_socket(struct sock *sp, char *tempMail, FILE *nfo)
{
char buff[LINESIZE] = {0};
char *b = buff;
size_t blen;
size_t bsize = 0;
size_t count;
FILE *nfi;
fflush(nfo);
if (ferror(nfo)) {
perror(catgets(catd, CATSET, 180, "temporary mail file"));
fclose(nfo);
fclose(nfi);
return 1;
}
if ((nfi = fopen(tempMail, "r")) == NULL) {
perror(tempMail);
fclose(nfo);
return 1;
}
count = fsize(nfi);
while (1) {
memset(b, 0, LINESIZE);
if (NULL == fgets(b, LINESIZE, nfi)){
break;
}
blen = strlen(b);
if (*b == '.') {
swrite1(sp, ".", 1, 1);
}
b[blen-1] = '\r';
b[blen] = '\n';
swrite1(sp, b, blen+1, 1);
}
return 0;
}
/*
* Talk to a SMTP server.
*/
static int
talk_smtp(struct name *to, FILE *fi, struct sock *sp,
char *xserver, char *uhp, struct header *hp,
const char *user, const char *password, const char *skinned)
{
struct name *n;
char *b = NULL, o[LINESIZE];
size_t blen, bsize = 0, count;
char *b64, *authstr, *cp;
enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth;
char *tempMail;
int inhdr = 1, inbcc = 0;
int ret;
if ((authstr = smtp_auth_var("", skinned)) == NULL)
auth = user && password ? AUTH_LOGIN : AUTH_NONE;
else if (strcmp(authstr, "plain") == 0)
auth = AUTH_PLAIN;
else if (strcmp(authstr, "login") == 0)
auth = AUTH_LOGIN;
else if (strcmp(authstr, "cram-md5") == 0)
auth = AUTH_CRAM_MD5;
else {
DEBUG_ERROR("Unknown SMTP authentication "
"method: \"%s\"\n", authstr);
mail_exit(MAIL_STATUS_ERR_GENERAL);
}
if (auth != AUTH_NONE && (user == NULL || password == NULL)) {
DEBUG_ERROR("User and password are necessary "
"for SMTP authentication.\n");
mail_exit(MAIL_STATUS_ERR_USER);
}
SMTP_ANSWER(2, MAIL_STATUS_ERR_IO);
/* v11.0 compatibility */
if (value("smtp-use-starttls") ||
value("smtp-use-tls")) {
char *server;
if ((cp = strchr(xserver, ':')) != NULL) {
server = salloc(cp - xserver + 1);
memcpy(server, xserver, cp - xserver);
server[cp - xserver] = '\0';
} else
server = xserver;
snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
SMTP_OUT(o);
SMTP_ANSWER(2, 0);
SMTP_OUT("STARTTLS\r\n");
SMTP_ANSWER(2, 0);
if (!debug && !_debug && ssl_open(server, sp, uhp) != OKAY) {
mail_exit(MAIL_STATUS_ERR_STARTTLS);
}
}
if (auth != AUTH_NONE) {
snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
SMTP_OUT(o);
SMTP_ANSWER(2, 0);
switch (auth) {
default:
case AUTH_LOGIN:
SMTP_OUT("AUTH LOGIN\r\n");
SMTP_ANSWER(3, 0);
b64 = strtob64(user);
snprintf(o, sizeof o, "%s\r\n", b64);
free(b64);
SMTP_OUT(o);
SMTP_ANSWER(3, 0);
b64 = strtob64(password);
snprintf(o, sizeof o, "%s\r\n", b64);
free(b64);
SMTP_OUT(o);
SMTP_ANSWER(2, MAIL_STATUS_ERR_USER);
break;
case AUTH_PLAIN:
SMTP_OUT("AUTH PLAIN\r\n");
SMTP_ANSWER(3, 0);
snprintf(o, sizeof o, "%c%s%c%s", '\0', user, '\0',
password);
b64 = memtob64(o, strlen(user)+strlen(password)+2);
snprintf(o, sizeof o, "%s\r\n", b64);
SMTP_OUT(o);
SMTP_ANSWER(2, MAIL_STATUS_ERR_USER);
break;
case AUTH_CRAM_MD5:
SMTP_OUT("AUTH CRAM-MD5\r\n");
SMTP_ANSWER(3, 0);
for (cp = smtpbuf; digitchar(*cp&0377); cp++);
while (blankchar(*cp&0377)) cp++;
cp = cram_md5_string(user, password, cp);
SMTP_OUT(cp);
SMTP_ANSWER(2, MAIL_STATUS_ERR_USER);
break;
}
} else {
snprintf(o, sizeof o, "HELO %s\r\n", nodename(1));
SMTP_OUT(o);
SMTP_ANSWER(2, 0);
}
snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned);
SMTP_OUT(o);
SMTP_ANSWER(2, MAIL_STATUS_ERR_EMAIL);
for (n = to; n != NULL; n = n->n_flink) {
if ((n->n_type & GDEL) == 0) {
snprintf(o, sizeof o, "RCPT TO:<%s>\r\n",
skin(n->n_name));
SMTP_OUT(o);
SMTP_ANSWER(2, MAIL_STATUS_ERR_EMAIL);
}
}
if (0 == strcmp(value("mail-type"), "check-connection")){
if (b != NULL)
free(b);
SMTP_OUT("QUIT\r\n");
_SMTP_ANSWER(2, 1, 0);
rm(tempMail);
return 0;
}
SMTP_OUT("DATA\r\n");
SMTP_ANSWER(3, 0);
ret = smtp_data(tempMail, hp, fi, sp);
if (ret != 0)
return ret;
SMTP_OUT(".\r\n");
SMTP_ANSWER(2, 0);
SMTP_OUT("QUIT\r\n");
_SMTP_ANSWER(2, 1, 0);
if (b != NULL)
free(b);
return 0;
}
int smtp_data(char *tempMail, struct header *hp, FILE *fi, struct sock *sp)
{
FILE *nfo, *nfi;
enum mimeclean isclean;
enum conversion convert;
char *charset = NULL;
char *contenttype = NULL;
int lastc = EOF;
size_t count;
if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
perror(catgets(catd, CATSET, 178, "temporary mail file"));
return 1;
}
convert = get_mime_convert(fi, &contenttype, &charset,
&isclean);
contenttype = "text/html";
if (puthead(hp, nfo,
GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
|GMSGID|GIDENT|GREF|GDATE,
SEND_MBOX, convert, contenttype, charset)) {
fclose(nfo);
unlink(tempMail);
return 1;
}
char *send_boundary = get_send_boundary();
if (hp->h_attach != NULL) {
//=================================================================//
struct attachment *att;
fputs("This is a multi-part message in MIME format.\n", nfo);
if (fsize(fi) != 0) {
char *buf, c = '\n';
size_t sz, bufsize, count;
fprintf(nfo, "\n--%s\n", send_boundary);
fprintf(nfo, "Content-Type: %s", contenttype);
if (charset)
fprintf(nfo, "; charset=%s", charset);
fprintf(nfo, "\nContent-Transfer-Encoding: %s\n"
"Content-Disposition: inline\n\n",
getencoding(convert));
buf = smalloc(bufsize = INFIX_BUF);
for (;;) {
sz = fread(buf, sizeof *buf, bufsize, fi);
if (sz == 0)
break;
c = buf[sz - 1];
if (mime_write(buf, sz, nfo, convert,
TD_ICONV, NULL, (size_t)0,
NULL, NULL) == 0) {
free(buf);
fclose(nfo);
unlink(tempMail);
return 1;
}
}
free(buf);
if (ferror(fi))
{
fclose(nfo);
unlink(tempMail);
return 1;
}
if (c != '\n')
putc('\n', nfo);
if (charset != NULL)
put_signature(nfo, convert);
}
fflush(nfo);
if (write_temp_file_to_socket(sp, tempMail, nfo) != 0) {
fclose(nfo);
unlink(tempMail);
return 1;
}
for (att = hp->h_attach; att != NULL; att = att->a_flink) {
fclose(nfo);
nfo = fopen(tempMail, "w");
char *basename;
char *contenttype = NULL;
char *charset = NULL;
enum conversion convert = CONV_TOB64;
enum mimeclean isclean;
if ((basename = strrchr(att->a_name, '/')) == NULL)
basename = att->a_name;
else
basename++;
if (att->a_content_type)
contenttype = att->a_content_type;
else
contenttype = mime_filecontent(basename);
if (att->a_charset)
charset = att->a_charset;
fprintf(nfo,
"\n--%s\n"
"Content-Type: %s",
send_boundary, contenttype);
if (charset == NULL)
putc('\n', nfo);
else
fprintf(nfo, ";\n charset=%s\n", charset);
if ((fi = fopen(att->a_name, "r")) == NULL) {
perror(att->a_name);
fclose(nfo);
unlink(tempMail);
return -1;
}
convert = get_mime_convert(fi, &contenttype, &charset, &isclean);
if (att->a_content_disposition == NULL)
att->a_content_disposition = "attachment";
fprintf(nfo, "Content-Transfer-Encoding: %s\n"
"Content-Disposition: %s;\n"
" filename=\"",
getencoding(convert),
att->a_content_disposition);
mime_write(basename, strlen(basename), nfo,
CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL);
fwrite("\"\n", sizeof (char), 2, nfo);
if (att->a_content_id)
fprintf(nfo, "Content-ID: %s\n", att->a_content_id);
if (att->a_content_description)
fprintf(nfo, "Content-Description: %s\n",
att->a_content_description);
putc('\n', nfo);
if (write_temp_file_to_socket(sp, tempMail, nfo) != 0) {
fclose(nfo);
unlink(tempMail);
return 1;
}
while (1){
fclose(nfo);
nfo = fopen(tempMail, "w");
int status = attach_file(att, convert, isclean, fi, nfo);
if (status < 0) {
fclose(nfo);
unlink(tempMail);
return 1;
}
if (write_temp_file_to_socket(sp, tempMail, nfo) != 0) {
fclose(nfo);
unlink(tempMail);
return 1;
}
if (status > 0)
continue;
else {
break;
}
}
}
fclose(nfo);
nfo = fopen(tempMail, "w");
/* the final boundary with two attached dashes */
fprintf(nfo, "\n--%s--\n", send_boundary);
if (write_temp_file_to_socket(sp, tempMail, nfo) != 0){
fclose(nfo);
unlink(tempMail);
return 1;
}
fclose(fi);
fclose(nfo);
unlink(tempMail);
return 0;
//=================================================================//
} else {
size_t sz, bufsize, count;
char *buf;
buf = smalloc(bufsize = INFIX_BUF);
for (;;) {
sz = fread(buf, sizeof *buf, bufsize, fi);
if (sz == 0)
break;
lastc = buf[sz - 1];
if (mime_write(buf, sz, nfo, convert,
TD_ICONV, NULL, (size_t)0,
NULL, NULL) == 0) {
fclose(nfo);
free(buf);
unlink(tempMail);
return 1;
}
}
free(buf);
if (ferror(fi)) {
fclose(nfo);
unlink(tempMail);
return 1;
}
if (charset != NULL)
put_signature(nfo, convert);
if (write_temp_file_to_socket(sp, tempMail, nfo) != 0) {
fclose(nfo);
unlink(tempMail);
return 1;
}
fclose(nfo);
fclose(fi);
unlink(tempMail);
return 0;
}
}
static sigjmp_buf smtpjmp;
static void
onterm(int signo)
{
siglongjmp(smtpjmp, 1);
}
int infix_and_transfer(char *server, struct name *to, FILE *fi, struct header *hp,
const char *user, const char *password, const char *skinned)
{
struct sock so;
int use_ssl, ret;
sighandler_type saveterm;
int use_starttls = 0;
memset(&so, 0, sizeof so);
saveterm = safe_signal(SIGTERM, SIG_IGN);
if (sigsetjmp(smtpjmp, 1)) {
safe_signal(SIGTERM, saveterm);
mail_exit(MAIL_STATUS_ERR_GENERAL);
}
if (saveterm != SIG_IGN)
safe_signal(SIGTERM, onterm);
if ((NULL == value("smtp-use-ssl")) &&
(NULL == value("smtp-use-starttls"))){
if (sopen(server, &so, 0, server, "smtp") != OKAY) {
safe_signal(SIGTERM, saveterm);
mail_exit(MAIL_STATUS_ERR_CONNECT);
}
use_starttls = 0;
}
if (NULL != value("smtp-use-ssl")){
if (sopen(server, &so, 1, server, "smtps") != OKAY) {
safe_signal(SIGTERM, saveterm);
mail_exit(MAIL_STATUS_ERR_SSL);
}
use_starttls = 0;
}
if (NULL != value("smtp-use-starttls")){
if (sopen(server, &so, 0, server, "smtps") != OKAY) {
safe_signal(SIGTERM, saveterm);
mail_exit(MAIL_STATUS_ERR_STARTTLS);
}
use_starttls = 1;
}
so.s_desc = "SMTP";
if (use_starttls)
assign("smtp-use-starttls", "");
ret = talk_smtp(to, fi, &so, server, server, hp,
user, password, skinned);
sclose(&so);
if (smtpbuf) {
free(smtpbuf);
smtpbuf = NULL;
smtpbufsize = 0;
}
safe_signal(SIGTERM, saveterm);
return ret;
}