Skip to content
Snippets Groups Projects
Commit e9f56e5d authored by Rob Swindell's avatar Rob Swindell :speech_balloon:
Browse files

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 08ae8875
No related branches found
No related tags found
No related merge requests found
......@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment