Commit c90ba307 authored by Rob Swindell's avatar Rob Swindell 💬
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 f039b2aa
Pipeline #1170 passed with stage
in 17 minutes and 37 seconds
...@@ -2715,44 +2715,6 @@ static void parse_mail_address(char* p ...@@ -2715,44 +2715,6 @@ static void parse_mail_address(char* p
strip_char(name, name, '\\'); 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) static BOOL checktag(scfg_t *scfg, char *tag, uint usernum)
{ {
char fname[MAX_PATH+1]; char fname[MAX_PATH+1];
...@@ -2950,12 +2912,6 @@ static void smtp_thread(void* arg) ...@@ -2950,12 +2912,6 @@ static void smtp_thread(void* arg)
} cmd = SMTP_CMD_NONE; } cmd = SMTP_CMD_NONE;
enum {
ENCODING_NONE
,ENCODING_BASE64
,ENCODING_QUOTED_PRINTABLE
} content_encoding = ENCODING_NONE;
SetThreadName("sbbs/smtp"); SetThreadName("sbbs/smtp");
thread_up(TRUE /* setuid */); thread_up(TRUE /* setuid */);
...@@ -4059,24 +4015,7 @@ static void smtp_thread(void* arg) ...@@ -4059,24 +4015,7 @@ static void smtp_thread(void* arg)
p=buf; p=buf;
if(*p=='.') p++; /* Transparency (RFC821 4.5.2) */ if(*p=='.') p++; /* Transparency (RFC821 4.5.2) */
if(msgtxt!=NULL) { if(msgtxt!=NULL) {
switch(content_encoding) { fputs(p, msgtxt);
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;
}
} }
lines++; lines++;
/* release time-slices every x lines */ /* release time-slices every x lines */
...@@ -4102,20 +4041,6 @@ static void smtp_thread(void* arg) ...@@ -4102,20 +4041,6 @@ static void smtp_thread(void* arg)
,sender, sizeof(sender)-1 ,sender, sizeof(sender)-1
,sender_addr, sizeof(sender_addr)-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) ...@@ -4410,7 +4335,6 @@ static void smtp_thread(void* arg)
break; break;
} }
rcpt_count=0; rcpt_count=0;
content_encoding=ENCODING_NONE;
memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count); memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
...@@ -4468,7 +4392,6 @@ static void smtp_thread(void* arg) ...@@ -4468,7 +4392,6 @@ static void smtp_thread(void* arg)
break; break;
} }
rcpt_count=0; rcpt_count=0;
content_encoding=ENCODING_NONE;
memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count); memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
sockprintf(socket,client.protocol,session,ok_rsp); sockprintf(socket,client.protocol,session,ok_rsp);
badcmds=0; badcmds=0;
......
...@@ -558,6 +558,7 @@ typedef struct { /* Message */ ...@@ -558,6 +558,7 @@ typedef struct { /* Message */
char* editor; /* Message editor (if known) */ char* editor; /* Message editor (if known) */
char* mime_version; /* MIME Version (if applicable) */ char* mime_version; /* MIME Version (if applicable) */
char* content_type; /* MIME Content-Type (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_charset; /* MIME text <charset> (if applicable) - malloc'd */
char* text_subtype; /* MIME text/<sub-type> (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 */ 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 ...@@ -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)); smb_parse_content_type(p, &(msg->text_subtype), &(msg->text_charset));
break; break;
} }
if(strnicmp(p, "Content-Transfer-Encoding:", 26) == 0) {
p += 26;
SKIP_WHITESPACE(p);
msg->content_encoding = p;
break;
}
break; break;
} }
} }
...@@ -897,6 +903,7 @@ static void clear_convenience_ptrs(smbmsg_t* msg) ...@@ -897,6 +903,7 @@ static void clear_convenience_ptrs(smbmsg_t* msg)
msg->newsgroups=NULL; msg->newsgroups=NULL;
msg->mime_version=NULL; msg->mime_version=NULL;
msg->content_type=NULL; msg->content_type=NULL;
msg->content_encoding=NULL;
msg->text_subtype=NULL; msg->text_subtype=NULL;
msg->text_charset=NULL; msg->text_charset=NULL;
......
/* Synchronet message base (SMB) message text library routines */ /* 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.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
...@@ -15,21 +13,9 @@ ...@@ -15,21 +13,9 @@
* See the GNU Lesser General Public License for more details: lgpl.txt or * * See the GNU Lesser General Public License for more details: lgpl.txt or *
* http://www.fsf.org/copyleft/lesser.html * * 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 * * For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html * * 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. * * Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/ ****************************************************************************/
...@@ -42,7 +28,7 @@ ...@@ -42,7 +28,7 @@
#include "base64.h" #include "base64.h"
#include "lzh.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* buf;
char* preamble; char* preamble;
...@@ -217,7 +203,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) ...@@ -217,7 +203,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode)
return(buf); return(buf);
} }
void SMBCALL smb_freemsgtxt(char* buf) void smb_freemsgtxt(char* buf)
{ {
if(buf!=NULL) if(buf!=NULL)
free(buf); free(buf);
...@@ -275,6 +261,22 @@ static size_t strStartsWith_i(const char* buf, const char* match) ...@@ -275,6 +261,22 @@ static size_t strStartsWith_i(const char* buf, const char* match)
return 0; 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) static enum content_transfer_encoding mime_getxferencoding(const char* beg, const char* end)
{ {
const char* p = beg; const char* p = beg;
...@@ -286,15 +288,7 @@ static enum content_transfer_encoding mime_getxferencoding(const char* beg, cons ...@@ -286,15 +288,7 @@ static enum content_transfer_encoding mime_getxferencoding(const char* beg, cons
FIND_CHAR(p, '\n'); FIND_CHAR(p, '\n');
continue; continue;
} }
p += len; return mime_encoding(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 CONTENT_TRANFER_ENCODING_NONE; return CONTENT_TRANFER_ENCODING_NONE;
...@@ -354,7 +348,7 @@ static BOOL mime_getattachment(const char* beg, const char* end, char* attachmen ...@@ -354,7 +348,7 @@ static BOOL mime_getattachment(const char* beg, const char* end, char* attachmen
} }
// Parses a MIME text/* content-type header field // 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) { if(subtype != NULL) {
FREE_AND_NULL(*subtype); FREE_AND_NULL(*subtype);
...@@ -405,8 +399,8 @@ void SMBCALL smb_parse_content_type(const char* content_type, char** subtype, ch ...@@ -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 */ /* Find the specified content-type in a multi-part MIME-encoded message body, recursively */
static const char* mime_getcontent(const char* buf, const char* content_type, const char* content_match 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) ,int depth, enum content_transfer_encoding* encoding, char** charset, char* attachment, size_t attachment_len, int index)
{ {
const char* txt; const char* txt;
...@@ -465,12 +459,12 @@ static const char* mime_getcontent(const char* buf, const char* content_type, co ...@@ -465,12 +459,12 @@ static const char* mime_getcontent(const char* buf, const char* content_type, co
const char* cp; const char* cp;
if((match_len && strnicmp(content_type, match1, match_len) && strnicmp(content_type, match2, match_len)) if((match_len && strnicmp(content_type, match1, match_len) && strnicmp(content_type, match2, match_len))
|| (attachment != NULL && !mime_getattachment(txt, p, attachment, attachment_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; return cp;
continue; continue;
} }
if(found++ != index) { 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; return cp;
continue; continue;
} }
...@@ -490,29 +484,41 @@ static const char* mime_getcontent(const char* buf, const char* content_type, co ...@@ -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 */ /* 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 */ /* 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; const char* txt;
enum content_transfer_encoding xfer_encoding = CONTENT_TRANFER_ENCODING_NONE; enum content_transfer_encoding xfer_encoding = CONTENT_TRANFER_ENCODING_NONE;
if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */ if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */
return NULL; return NULL;
txt = mime_getcontent(buf, msg->content_type, "text/plain", 0, &xfer_encoding, &msg->text_charset if(strStartsWith_i(msg->content_type, "multipart/") > 0) {
,/* attachment: */NULL, /* attachment_len: */0, /* index: */0); txt = mime_getpart(buf, msg->content_type, "text/plain", 0, &xfer_encoding, &msg->text_charset
if(txt == NULL) {
txt = mime_getcontent(buf, msg->content_type, "text/html", 0, &xfer_encoding, &msg->text_charset
,/* attachment: */NULL, /* attachment_len: */0, /* index: */0); ,/* attachment: */NULL, /* attachment_len: */0, /* index: */0);
if(txt == NULL) if(txt == NULL) {
return NULL; txt = mime_getpart(buf, msg->content_type, "text/html", 0, &xfer_encoding, &msg->text_charset
free(msg->text_subtype); ,/* attachment: */NULL, /* attachment_len: */0, /* index: */0);
msg->text_subtype = strdup("html"); 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 { } else {
free(msg->text_subtype); /* Single-part MIME */
msg->text_subtype = strdup("plain"); if(strStartsWith_i(msg->content_type, "text/") < 1) {
*buf = '\0';
return buf;
}
xfer_encoding = mime_encoding(msg->content_encoding);
len = strlen(buf);
} }
if(len == 0) /* No decoding necessary */
memmove(buf, txt, strlen(txt)+1);
if(*buf == 0) /* No decoding necessary */
return buf; return buf;
if(xfer_encoding == CONTENT_TRANFER_ENCODING_QUOTED_PRINTABLE) if(xfer_encoding == CONTENT_TRANFER_ENCODING_QUOTED_PRINTABLE)
qp_decode(buf); qp_decode(buf);
...@@ -520,41 +526,75 @@ char* SMBCALL smb_getplaintext(smbmsg_t* msg, char* buf) ...@@ -520,41 +526,75 @@ char* SMBCALL smb_getplaintext(smbmsg_t* msg, char* buf)
char* decoded = strdup(buf); char* decoded = strdup(buf);
if(decoded == NULL) if(decoded == NULL)
return NULL; return NULL;
if(b64_decode(decoded, strlen(decoded), buf, strlen(buf)) > 0) if(b64_decode(decoded, len, buf, len) > 0)
strcpy(buf, decoded); strcpy(buf, decoded);
free(decoded); free(decoded);
} }
return buf; return buf;
} }
/* Get just a base64-encoded attachment (just one) from MIME-encoded message body */ /* Get an attachment (just one) from single-part or multi-part MIME-encoded message body */
/* This function is destructive (over-writes 'buf' with decoded attachment)! */ /* This function can be destructive (over-write '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) uint8_t* smb_getattachment(smbmsg_t* msg, char* buf, char* filename, size_t filename_len, uint32_t* filelen, int index)
{ {
const char* txt; const char* txt;
enum content_transfer_encoding xfer_encoding = CONTENT_TRANFER_ENCODING_NONE; enum content_transfer_encoding xfer_encoding = CONTENT_TRANFER_ENCODING_NONE;
if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */ if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */
return NULL; return NULL;
txt = mime_getcontent(buf, msg->content_type, /* match-type: */NULL, 0, &xfer_encoding, /* charset: */NULL if(strStartsWith_i(msg->content_type, "multipart/") > 0) {
,/* attachment: */filename, filename_len, index); txt = mime_getpart(buf, msg->content_type, /* match-type: */NULL, 0, &xfer_encoding, /* charset: */NULL
if(txt != NULL && xfer_encoding == CONTENT_TRANFER_ENCODING_BASE64) { ,/* attachment: */filename, filename_len, index);
memmove(buf, txt, strlen(txt)+1); if(txt != NULL && xfer_encoding == CONTENT_TRANFER_ENCODING_BASE64) {
int result = b64_decode(buf, strlen(buf), buf, strlen(buf)); 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) if(result < 1)
return NULL; return NULL;
if(filelen != NULL) if(filelen != NULL)
*filelen = result; *filelen = result;
return (uint8_t*)buf; } else {
if(filelen != NULL)
*filelen = strlen(buf);
} }
return buf;
return NULL; /* No attachment */
} }
/* Return number of file attachments contained in MIME-encoded message body */ /* Return number of file attachments contained in MIME-encoded message body */
/* 'body' may be NULL if the body text is not already read/available */ /* '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 */ if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */
return 0; 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