Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

Commit c90ba307 authored by Rob Swindell's avatar Rob Swindell 💬

Support single-part MIME-encoded messages in SMBLIB

This involved the removal of the content-transfer-decoding feature of the SMTP mail server since we need single-part MIME-attached file contents to be stored in their original encoded form (e.g. base64-encoded) and not in binary, for the message body text (where NULs aren't allowed, CR/LF is appended, etc).

I accidentally made this change to smbtxt.c in the new_file_base branch and then copied over here, so there's some unrelated innocuous changes (comment header, removal of SMBCALL) that hopefully won't cause a merge conflict later.

I don't actually receive single-part MIME attached files, so depending on others (e.g. Nelgin and Dream Master) to test for me.
parent f039b2aa
Pipeline #1170 passed with stage
in 17 minutes and 37 seconds
......@@ -2715,44 +2715,6 @@ static void parse_mail_address(char* p
strip_char(name, name, '\\');
}
/* Decode quoted-printable content-transfer-encoded text */
/* Ignores (strips) unsupported ctrl chars and non-ASCII chars */
/* Does not enforce 76 char line length limit */
static char* qp_decode(char* buf)
{
uchar* p=(uchar*)buf;
uchar* dest=p;
for(;;p++) {
if(*p==0) {
*dest++='\r';
*dest++='\n';
break;
}
if(*p==' ' || (*p>='!' && *p<='~' && *p!='=') || *p=='\t')
*dest++=*p;
else if(*p=='=') {
p++;
if(*p==0) /* soft link break */
break;
if(IS_HEXDIGIT(*p) && IS_HEXDIGIT(*(p+1))) {
char hex[3];
hex[0]=*p;
hex[1]=*(p+1);
hex[2]=0;
/* ToDo: what about encoded NULs and the like? */
*dest++=(uchar)strtoul(hex,NULL,16);
p++;
} else { /* bad encoding */
*dest++='=';
*dest++=*p;
}
}
}
*dest=0;
return buf;
}
static BOOL checktag(scfg_t *scfg, char *tag, uint usernum)
{
char fname[MAX_PATH+1];
......@@ -2950,12 +2912,6 @@ static void smtp_thread(void* arg)
} cmd = SMTP_CMD_NONE;
enum {
ENCODING_NONE
,ENCODING_BASE64
,ENCODING_QUOTED_PRINTABLE
} content_encoding = ENCODING_NONE;
SetThreadName("sbbs/smtp");
thread_up(TRUE /* setuid */);
......@@ -4059,24 +4015,7 @@ static void smtp_thread(void* arg)
p=buf;
if(*p=='.') p++; /* Transparency (RFC821 4.5.2) */
if(msgtxt!=NULL) {
switch(content_encoding) {
case ENCODING_BASE64:
{
char decode_buf[sizeof(buf)];
if(b64_decode(decode_buf, sizeof(decode_buf), p, strlen(p))<0)
fprintf(msgtxt,"\r\n!Base64 decode error: %s\r\n", p);
else
fputs(decode_buf, msgtxt);
}
break;
case ENCODING_QUOTED_PRINTABLE:
fputs(qp_decode(p), msgtxt);
break;
default:
fprintf(msgtxt, "%s\r\n", p);
break;
}
fputs(p, msgtxt);
}
lines++;
/* release time-slices every x lines */
......@@ -4102,20 +4041,6 @@ static void smtp_thread(void* arg)
,sender, sizeof(sender)-1
,sender_addr, sizeof(sender_addr)-1);
}
else if(stricmp(field,"CONTENT-TRANSFER-ENCODING")==0) {
lprintf(LOG_INFO,"%04d %s %s %s = %s", socket, client.protocol, client_id, field, p);
if(stricmp(p,"base64")==0)
content_encoding=ENCODING_BASE64;
else if(stricmp(p,"quoted-printable")==0)
content_encoding=ENCODING_QUOTED_PRINTABLE;
else { /* Other (e.g. 7bit, 8bit, binary) */
content_encoding=ENCODING_NONE;
if(msgtxt!=NULL)
fprintf(msgtxt, "%s\r\n", buf);
}
hdr_lines++;
continue;
}
}
}
......@@ -4410,7 +4335,6 @@ static void smtp_thread(void* arg)
break;
}
rcpt_count=0;
content_encoding=ENCODING_NONE;
memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
......@@ -4468,7 +4392,6 @@ static void smtp_thread(void* arg)
break;
}
rcpt_count=0;
content_encoding=ENCODING_NONE;
memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
sockprintf(socket,client.protocol,session,ok_rsp);
badcmds=0;
......
......@@ -558,6 +558,7 @@ typedef struct { /* Message */
char* editor; /* Message editor (if known) */
char* mime_version; /* MIME Version (if applicable) */
char* content_type; /* MIME Content-Type (if applicable) */
char* content_encoding; /* MIME Content-Transfer-Encoding (if applicable) */
char* text_charset; /* MIME text <charset> (if applicable) - malloc'd */
char* text_subtype; /* MIME text/<sub-type> (if applicable) - malloc'd */
uint16_t to_agent, /* Type of agent message is to */
......
......@@ -859,6 +859,12 @@ static void set_convenience_ptr(smbmsg_t* msg, uint16_t hfield_type, void* hfiel
smb_parse_content_type(p, &(msg->text_subtype), &(msg->text_charset));
break;
}
if(strnicmp(p, "Content-Transfer-Encoding:", 26) == 0) {
p += 26;
SKIP_WHITESPACE(p);
msg->content_encoding = p;
break;
}
break;
}
}
......@@ -897,6 +903,7 @@ static void clear_convenience_ptrs(smbmsg_t* msg)
msg->newsgroups=NULL;
msg->mime_version=NULL;
msg->content_type=NULL;
msg->content_encoding=NULL;
msg->text_subtype=NULL;
msg->text_charset=NULL;
......
/* Synchronet message base (SMB) message text library routines */
/* $Id: smbtxt.c,v 1.49 2019/11/19 22:04:55 rswindell Exp $ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
......@@ -15,21 +13,9 @@
* See the GNU Lesser General Public License for more details: lgpl.txt or *
* http://www.fsf.org/copyleft/lesser.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
......@@ -42,7 +28,7 @@
#include "base64.h"
#include "lzh.h"
char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode)
char* smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode)
{
char* buf;
char* preamble;
......@@ -217,7 +203,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode)
return(buf);
}
void SMBCALL smb_freemsgtxt(char* buf)
void smb_freemsgtxt(char* buf)
{
if(buf!=NULL)
free(buf);
......@@ -275,6 +261,22 @@ static size_t strStartsWith_i(const char* buf, const char* match)
return 0;
}
static enum content_transfer_encoding mime_encoding(const char* str)
{
const char* p = str;
if(str == NULL)
return CONTENT_TRANFER_ENCODING_NONE;
SKIP_WHITESPACE(p);
if(strnicmp(p, "base64", 6) == 0)
return CONTENT_TRANFER_ENCODING_BASE64;
if(strnicmp(p, "quoted-printable", 16) == 0)
return CONTENT_TRANFER_ENCODING_QUOTED_PRINTABLE;
if(strnicmp(p, "7bit", 4) == 0 || strnicmp(p, "8bit", 4) == 0 || strnicmp(p, "binary", 6) == 0)
return CONTENT_TRANFER_ENCODING_NONE;
return CONTENT_TRANFER_ENCODING_OTHER;
}
static enum content_transfer_encoding mime_getxferencoding(const char* beg, const char* end)
{
const char* p = beg;
......@@ -286,15 +288,7 @@ static enum content_transfer_encoding mime_getxferencoding(const char* beg, cons
FIND_CHAR(p, '\n');
continue;
}
p += len;
SKIP_WHITESPACE(p);
if(strnicmp(p, "base64", 6) == 0)
return CONTENT_TRANFER_ENCODING_BASE64;
if(strnicmp(p, "quoted-printable", 16) == 0)
return CONTENT_TRANFER_ENCODING_QUOTED_PRINTABLE;
if(strnicmp(p, "7bit", 4) == 0 || strnicmp(p, "8bit", 4) == 0 || strnicmp(p, "binary", 6) == 0)
return CONTENT_TRANFER_ENCODING_NONE;
return CONTENT_TRANFER_ENCODING_OTHER;
return mime_encoding(p + len);
}
return CONTENT_TRANFER_ENCODING_NONE;
......@@ -354,7 +348,7 @@ static BOOL mime_getattachment(const char* beg, const char* end, char* attachmen
}
// Parses a MIME text/* content-type header field
void SMBCALL smb_parse_content_type(const char* content_type, char** subtype, char** charset)
void smb_parse_content_type(const char* content_type, char** subtype, char** charset)
{
if(subtype != NULL) {
FREE_AND_NULL(*subtype);
......@@ -405,8 +399,8 @@ void SMBCALL smb_parse_content_type(const char* content_type, char** subtype, ch
}
}
/* Find the specified content-type in a multi-pat MIME-encoded message body, recursively */
static const char* mime_getcontent(const char* buf, const char* content_type, const char* content_match
/* Find the specified content-type in a multi-part MIME-encoded message body, recursively */
static const char* mime_getpart(const char* buf, const char* content_type, const char* content_match
,int depth, enum content_transfer_encoding* encoding, char** charset, char* attachment, size_t attachment_len, int index)
{
const char* txt;
......@@ -465,12 +459,12 @@ static const char* mime_getcontent(const char* buf, const char* content_type, co
const char* cp;
if((match_len && strnicmp(content_type, match1, match_len) && strnicmp(content_type, match2, match_len))
|| (attachment != NULL && !mime_getattachment(txt, p, attachment, attachment_len))) {
if((cp = mime_getcontent(p, content_type, content_match, depth + 1, encoding, charset, attachment, attachment_len, index)) != NULL)
if((cp = mime_getpart(p, content_type, content_match, depth + 1, encoding, charset, attachment, attachment_len, index)) != NULL)
return cp;
continue;
}
if(found++ != index) {
if((cp = mime_getcontent(p, content_type, content_match, depth + 1, encoding, charset, attachment, attachment_len, index)) != NULL)
if((cp = mime_getpart(p, content_type, content_match, depth + 1, encoding, charset, attachment, attachment_len, index)) != NULL)
return cp;
continue;
}
......@@ -490,29 +484,41 @@ static const char* mime_getcontent(const char* buf, const char* content_type, co
/* Get just the (first) plain-text or HTML portion of a MIME-encoded multi-part message body */
/* Returns NULL if there is no MIME-encoded plain-text/html portion of the message */
char* SMBCALL smb_getplaintext(smbmsg_t* msg, char* buf)
char* smb_getplaintext(smbmsg_t* msg, char* buf)
{
size_t len;
const char* txt;
enum content_transfer_encoding xfer_encoding = CONTENT_TRANFER_ENCODING_NONE;
if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */
return NULL;
txt = mime_getcontent(buf, msg->content_type, "text/plain", 0, &xfer_encoding, &msg->text_charset
,/* attachment: */NULL, /* attachment_len: */0, /* index: */0);
if(txt == NULL) {
txt = mime_getcontent(buf, msg->content_type, "text/html", 0, &xfer_encoding, &msg->text_charset
if(strStartsWith_i(msg->content_type, "multipart/") > 0) {
txt = mime_getpart(buf, msg->content_type, "text/plain", 0, &xfer_encoding, &msg->text_charset
,/* attachment: */NULL, /* attachment_len: */0, /* index: */0);
if(txt == NULL)
return NULL;
free(msg->text_subtype);
msg->text_subtype = strdup("html");
if(txt == NULL) {
txt = mime_getpart(buf, msg->content_type, "text/html", 0, &xfer_encoding, &msg->text_charset
,/* attachment: */NULL, /* attachment_len: */0, /* index: */0);
if(txt == NULL)
return NULL;
free(msg->text_subtype);
msg->text_subtype = strdup("html");
} else {
free(msg->text_subtype);
msg->text_subtype = strdup("plain");
}
len = strlen(txt);
memmove(buf, txt, len + 1);
} else {
free(msg->text_subtype);
msg->text_subtype = strdup("plain");
/* Single-part MIME */
if(strStartsWith_i(msg->content_type, "text/") < 1) {
*buf = '\0';
return buf;
}
xfer_encoding = mime_encoding(msg->content_encoding);
len = strlen(buf);
}
memmove(buf, txt, strlen(txt)+1);
if(*buf == 0) /* No decoding necessary */
if(len == 0) /* No decoding necessary */
return buf;
if(xfer_encoding == CONTENT_TRANFER_ENCODING_QUOTED_PRINTABLE)
qp_decode(buf);
......@@ -520,41 +526,75 @@ char* SMBCALL smb_getplaintext(smbmsg_t* msg, char* buf)
char* decoded = strdup(buf);
if(decoded == NULL)
return NULL;
if(b64_decode(decoded, strlen(decoded), buf, strlen(buf)) > 0)
if(b64_decode(decoded, len, buf, len) > 0)
strcpy(buf, decoded);
free(decoded);
}
return buf;
}
/* Get just a base64-encoded attachment (just one) from MIME-encoded message body */
/* This function is destructive (over-writes 'buf' with decoded attachment)! */
uint8_t* SMBCALL smb_getattachment(smbmsg_t* msg, char* buf, char* filename, size_t filename_len, uint32_t* filelen, int index)
/* Get an attachment (just one) from single-part or multi-part MIME-encoded message body */
/* This function can be destructive (over-write 'buf' with decoded attachment)! */
uint8_t* smb_getattachment(smbmsg_t* msg, char* buf, char* filename, size_t filename_len, uint32_t* filelen, int index)
{
const char* txt;
enum content_transfer_encoding xfer_encoding = CONTENT_TRANFER_ENCODING_NONE;
if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */
return NULL;
txt = mime_getcontent(buf, msg->content_type, /* match-type: */NULL, 0, &xfer_encoding, /* charset: */NULL
,/* attachment: */filename, filename_len, index);
if(txt != NULL && xfer_encoding == CONTENT_TRANFER_ENCODING_BASE64) {
memmove(buf, txt, strlen(txt)+1);
int result = b64_decode(buf, strlen(buf), buf, strlen(buf));
if(strStartsWith_i(msg->content_type, "multipart/") > 0) {
txt = mime_getpart(buf, msg->content_type, /* match-type: */NULL, 0, &xfer_encoding, /* charset: */NULL
,/* attachment: */filename, filename_len, index);
if(txt != NULL && xfer_encoding == CONTENT_TRANFER_ENCODING_BASE64) {
size_t len = strlen(txt);
memmove(buf, txt, len + 1);
int result = b64_decode(buf, len, buf, len);
if(result < 1)
return NULL;
if(filelen != NULL)
*filelen = result;
return (uint8_t*)buf;
}
return NULL; /* No attachment */
}
/* Single-part MIME */
if(index > 0)
return NULL;
if(strStartsWith_i(msg->content_type, "text/") > 0)
return NULL;
if(filename != NULL) {
char* fname = strstr(msg->content_type, "name=");
if(fname == NULL)
return NULL;
fname += 5;
char* term = NULL;
if(*fname == '"') {
fname++;
term = strchr(fname, '"');
} else
term = strchr(fname, ';');
if(term != NULL)
*term = '\0';
strncpy(filename, fname, filename_len);
filename[filename_len - 1] = '\0';
}
if(mime_encoding(msg->content_encoding) == CONTENT_TRANFER_ENCODING_BASE64) {
size_t len = strlen(buf);
int result = b64_decode(buf, len, buf, len);
if(result < 1)
return NULL;
if(filelen != NULL)
*filelen = result;
return (uint8_t*)buf;
} else {
if(filelen != NULL)
*filelen = strlen(buf);
}
return NULL; /* No attachment */
return buf;
}
/* Return number of file attachments contained in MIME-encoded message body */
/* 'body' may be NULL if the body text is not already read/available */
ulong SMBCALL smb_countattachments(smb_t* smb, smbmsg_t* msg, const char* body)
ulong smb_countattachments(smb_t* smb, smbmsg_t* msg, const char* body)
{
if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */
return 0;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment