Sending an HTML email with optional attachments
by Richard Russell, April 2014, corrected August 2015
In the article Sending an email with attachments the procedure PROCsendmailattach() automatically sends an email using the SOCKLIB library. Listed below is a procedure which performs the same function except that you can send the body of the email as HTML instead of (or preferably as well as) plain text. The procedure might be called as follows:
INSTALL @lib$+"SOCKLIB" SMTP$ = "smtp.gmail.com" From$ = "sender@somewhere" To$ = "recipient@elsewhere" Subject$ = "Test of sending attachments" ReplyTo$ = "reallyme@myaddress" Plain$ = "This is a test of sending an HTML email." HTML$ = "<html><head></head><body>" + \ \ "This is a test of sending an <b><i>HTML</i></b> email." + \ \ "</body></html>" Attach$ = """C:\Program Files\BBC BASIC for Windows\readme.txt""" PROCsendmailhtmlattach(SMTP$,From$,To$,"","",Subject$,ReplyTo$,Plain$,HTML$,Attach$)
The calling program should first check that the attached file(s) actually exist, because if not PROCattach() will abort with an error and not terminate the SMTP transaction; this could leave the server 'in limbo'. Note that if an attached file path contains spaces or punctuation characters you must enclose it in quotes.
You should not assume that the receiving email client can necessarily render HTML, therefore ideally you should always include a plain-text message body as well as an HTML message body.
Here is PROCsendmailhtmlattach(); like PROCsendmail() it cannot be used if authentication is required so in practice that probably means that your ISP's SMTP server must be used:
DEF PROCsendmailhtmlattach(smtp$,from$,rcpt$,cc$,bcc$,subject$,replyto$,plain$,html$,attach$) LOCAL D%, S%, skt%, comma%, reply$, file$ DIM D% LOCAL 31, S% LOCAL 15 SYS "GetLocalTime", S% SYS "GetDateFormat", 0, 0, S%, "ddd, dd MMM yyyy ", D%, 18 SYS "GetTimeFormat", 0, 0, S%, "HH:mm:ss +0000", D%+17, 15 D%?31 = 13 PROC_initsockets skt% = FN_tcpconnect(smtp$,"mail") IF skt% <= 0 skt% = FN_tcpconnect(smtp$,"25") IF skt% <= 0 ERROR 100, "Failed to connect to mail server" IF FN_readlinesocket(skt%, 1000, reply$) WHILE FN_readlinesocket(skt%, 10, reply$) > 0 : ENDWHILE PROCsend(skt%,"HELO "+FN_gethostname) PROCmail(skt%,"MAIL FROM: ",from$) IF rcpt$<>"" PROClist(skt%,rcpt$) IF cc$<>"" PROClist(skt%,cc$) IF bcc$<>"" PROClist(skt%,bcc$) PROCsend(skt%, "DATA") IF FN_writelinesocket(skt%, "Date: "+$D%) IF FN_writelinesocket(skt%, "From: "+from$) IF FN_writelinesocket(skt%, "To: "+rcpt$) IF cc$<>"" IF FN_writelinesocket(skt%, "Cc: "+cc$) IF subject$<>"" IF FN_writelinesocket(skt%, "Subject: "+subject$) IF replyto$<>"" IF FN_writelinesocket(skt%, "Reply-To: "+replyto$) IF FN_writelinesocket(skt%, "MIME-Version: 1.0") IF attach$<>"" THEN IF FN_writelinesocket(skt%, "Content-Type: multipart/mixed; boundary=""BB4Wsep""") IF FN_writelinesocket(skt%, "") IF FN_writelinesocket(skt%, "--BB4Wsep") ENDIF IF plain$<>"" IF html$<>"" THEN IF FN_writelinesocket(skt%, "Content-Type: multipart/alternative; boundary=""BB4Wsep2""") IF FN_writelinesocket(skt%, "") IF FN_writelinesocket(skt%, "--BB4Wsep2") ENDIF IF plain$<>"" THEN IF FN_writelinesocket(skt%, "Content-Type: text/plain") IF FN_writelinesocket(skt%, "Content-Transfer-Encoding: 7bit") IF FN_writelinesocket(skt%, "") IF FN_writelinesocket(skt%, plain$) ENDIF IF plain$<>"" IF html$<>"" THEN IF FN_writelinesocket(skt%, "--BB4Wsep2") ENDIF IF html$<>"" THEN IF FN_writelinesocket(skt%, "Content-Type: text/html") IF FN_writelinesocket(skt%, "Content-Transfer-Encoding: 7bit") IF FN_writelinesocket(skt%, "") IF FN_writelinesocket(skt%, html$) ENDIF IF plain$<>"" IF html$<>"" THEN IF FN_writelinesocket(skt%, "--BB4Wsep2--") ENDIF IF attach$<>"" THEN REPEAT WHILE ASCattach$=32 attach$ = MID$(attach$,2) : ENDWHILE IF ASCattach$=34 THEN file$ = EVAL(attach$) comma% = INSTR(attach$, ",", LENfile$) ELSE comma% = INSTR(attach$, ",") IF comma% file$ = LEFT$(attach$, comma%-1) ELSE file$ = attach$ ENDIF PROCattach(skt%, file$) attach$ = MID$(attach$, comma%+1) UNTIL comma%=0 IF FN_writelinesocket(skt%, "--BB4Wsep--") ENDIF IF FN_writelinesocket(skt%, ".") PROCsend(skt%,"QUIT") PROC_exitsockets ENDPROC DEF PROClist(skt%,list$) LOCAL comma% REPEAT WHILE ASClist$=32 list$=MID$(list$,2):ENDWHILE comma% = INSTR(list$,",") IF comma% THEN PROCmail(skt%,"RCPT TO: ",LEFT$(list$,comma%-1)) list$ = MID$(list$,comma%+1) ELSE PROCmail(skt%,"RCPT TO: ",list$) ENDIF UNTIL comma% = 0 ENDPROC DEF PROCmail(skt%,cmd$,mail$) LOCAL I%,J% I% = INSTR(mail$,"<") J% = INSTR(mail$,">",I%) IF I% IF J% THEN PROCsend(skt%, cmd$+MID$(mail$,I%,J%-I%+1)) ELSE PROCsend(skt%, cmd$+"<"+mail$+">") ENDIF ENDPROC DEF PROCsend(skt%,cmd$) LOCAL reply$ IF FN_writelinesocket(skt%,cmd$) < 0 THEN ERROR 100, "Send failed" ENDIF IF FN_readlinesocket(skt%, 200, reply$) WHILE FN_readlinesocket(skt%, 10, reply$) > 0 : ENDWHILE ENDPROC DEF PROCattach(skt%,file$) LOCAL D%, F%, I%, L%, N%, A$, B$ B$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" F% = OPENIN(file$) IF F%=0 ERROR 100, "Failed to open attached file " + file$ REPEAT D% = INSTR(file$, "\") IF D%=0 D% = INSTR(file$, "/") IF D% file$ = MID$(file$, D%+1) UNTIL D%=0 IF FN_writelinesocket(skt%, "") IF FN_writelinesocket(skt%, "--BB4Wsep") IF FN_writelinesocket(skt%, "Content-Type: application/octet-stream") IF FN_writelinesocket(skt%, "Content-Disposition: attachment; filename="+file$) IF FN_writelinesocket(skt%, "Content-Transfer-Encoding: base64") IF FN_writelinesocket(skt%, "") L% = EXT#F% WHILE NOT EOF#F% N% = L% - PTR#F% D% = BGET#F% << 16 OR BGET#F% << 8 OR BGET#F% FOR I% = 1 TO 4 : A$ += MID$(B$,(D% >>> 18)+1,1) : D% = D% << 6 AND &FFFFFF : NEXT IF N%=1 RIGHT$(A$,2) = "==" ELSE IF N%=2 RIGHT$(A$) = "=" IF LENA$>=72 IF FN_writelinesocket(skt%,A$)=0 A$ = "" ENDWHILE IF A$<>"" IF FN_writelinesocket(skt%, A$) CLOSE #F% ENDPROC