e-mail sending?

Blitz3D Forums/Blitz3D Programming/e-mail sending?

Just for a bit of variety (it's very old and therefore not well written)...

;********************************************************************************************
;***								Blitz Mail Library
;***					       Yan 2001 - yan@...
;********************************************************************************************

;Updated (well, hacked at) 03/12/04 - 	Fixed multi Line response bug.
;										Added Base64 decoder and Login stuff.

SeedRnd MilliSecs()

Type html_image
	Field filename$, cid$, idata$
End Type

Type inc_data
	Field filename$, idata$
End Type

; Sends the processed mail.
; mailfrom$ = A string holding the senders mail address
; fromname$ = A string holding the senders name
; mailto$	= A string holding the recipients mail address
; toname$	= A string holding the recipients name - optional
; subject$	= A string holding the subject of the mail
; body$		= A string holding the processed MIME mail
; mailhost$ = A string holding the servers IP address or DNS (SMTP / ESMTP)
; username$ = A string holding the account username
; userpass$ = A string holding the account password
; This function returns a string containing 'success' if successful or an error message if not
Function send_mail$ (mailfrom$, fromname$, mailto$, toname$ = "", subject$, body$, mailhost$, username$="", userpass$="")
	Local host, code$, resp$, err$ = "Success", qt$ = Chr$(34)
	If toname$ = "" Then toname$ = mailto$
	
	host = OpenTCPStream(mailhost$, 25)

	If host
		resp$ = ReadLine(host)
		code$ = Left$(resp$, 3)
		If code$ = "220"
			While Left$(resp$, 4) = "220-"
				resp$ = ReadLine(host)
			Wend 
		
			WriteLine(host, "EHLO Blitz Mail Library")
			resp$ = ReadLine(host)
			
			If Left$(resp$, 3) = "250"			
				While Left$(resp$, 4) = "250-"
					resp$ = ReadLine(host)
				Wend 
				
				If Left$(resp$, 3) = "250"
					WriteLine(host, "AUTH LOGIN")
					resp$ = ReadLine(host)

					While Left$(resp$, 3) = "334"
						If Instr(Lower$(base64_dec$(Right$(resp$, Len(resp$) - 4))), "username:") > 0 
							WriteLine(host, base64_enc$(username$, 0))
							resp$ = ReadLine(host)
						
						ElseIf Instr(Lower$(base64_dec$(Right$(resp$, Len(resp$) - 4))), "password:") > 0 
							WriteLine(host, base64_enc$(userpass$, 0))
							resp$ = ReadLine(host)
						EndIf
					Wend
				Else
					err$ = resp$
				EndIf
			Else
				WriteLine(host, "HELO Blitz Mail Library")
				resp$ = ReadLine(host)
			EndIf
						
			If (Left$(resp$, 3) = "250") Or (Left$(resp$, 3) = "235")
				WriteLine(host, "MAIL FROM: <" + mailfrom$ + ">")
				resp$ = ReadLine(host)
				code$ = Left$(resp$, 3)
				If code$ = "250"	
					WriteLine(host, "RCPT TO: <" + mailto$ + ">")
					resp$ = ReadLine(host)
					code$ = Left$(resp$, 3)
					If code$ = "250"
						WriteLine(host, "DATA")
						resp$ = ReadLine(host)
						If Left$(resp$, 3) = "354"
							WriteLine(host, "From: " + qt$ + mailname$ + qt$ + " <" + mailfrom$ + ">")
							WriteLine(host, "To: " + qt$ + toname$ + qt$ + " <" + mailto$ + ">")
							WriteLine(host, "Subject: " + subject$)
							WriteLine(host, "X-Mailer: Blitz Mail Library v1.01")
							WriteLine(host, body$)
							WriteLine(host, ".")
							resp$ = ReadLine(host)
							If Left$(resp$, 3) = "250"
								WriteLine(host, "QUIT")
								resp$ = ReadLine(host)
								If Left$(resp$, 3) <> "221"
									err$ = resp$
								EndIf
							Else
								err$ = resp$
							EndIf
						Else
							err$ = resp$
						EndIf
					Else
						If code$ = "501"
							err$ = "Email recipient not specified (or invalid address)"
						Else
							err$ = resp$
						EndIf
					EndIf
				Else
					If code$ = "501"
						err$ = "Email sender not specified (or invalid address)"
					Else
						err$ = resp$
					EndIf
				EndIf
			Else
				err$ = resp$
			EndIf
		Else
			If code$ = "421"
				err$ = "Service not available"
			Else
				err$ = resp$
			EndIf 
		EndIf
		CloseTCPStream host
		If err$ = "" Then err$ = "Timeout error"
		Return err$
	Else
		Return "Failed to connect to server " + "'" + mailhost$ + "'"
	EndIf
	Return err$
End Function

; Encodes the processed HTML text and/or plain text into MIME format
; html$	= A string holding the processed HTML text - optional
; plain$= A string holding the plain text - optional
; This function returns a string containing the MIME mail body text
;
; You're probably wondering why BOTH of the parameters are optional?
; If you wanted to send a blank mail with just an attachment.
; You still need to run this function to MIME encode the attachment - build_mime$()
Function build_mime$(html$="", plain$="")
	Local out$, nl$ = Chr$(13) + Chr$(10), qt$ = Chr$(34)
	Local bound$ = "=_PART_00_" + unique()
	Local bound1$ = "=_PART_01_" + unique()
	Local bound2$ = "=_PART_02_" + unique()
	If plain$ = "" And html$ <> ""
		plain$ = "This is a HTML mail. Your mail app may not be able to display the main body of this mail."
	EndIf
	

	out$ = out$ + "MIME-Version: 1.0" + nl$
	out$ = out$ + "Content-Type: multipart/mixed;" + nl$ + Chr$(9)
	out$ = out$ + "boundary=" + qt$ + bound$ + qt$ + nl$ + nl$
	out$ = out$ + "This is a MIME encoded message" + nl$ + nl$
	out$ = out$ + "--" + bound$ + nl$
	If html$ <> ""
		If First html_image = Null
			out$ = out$ + "Content-Type: multipart/alternative;" + nl$ + Chr$(9)
			out$ = out$ + "boundary=" + qt$ + bound1$ + qt$ + nl$ + nl$ + nl$
			out$ = out$ + "--" + bound1$ + nl$
			out$ = out$ + "Content-Type: text/plain; charset=" + qt$ + "iso-8859-1" + qt$ + nl$
			out$ = out$ + "Content-Transfer-Encoding: quoted-printable" + nl$ + nl$
			out$ = out$ + qp_enc$(plain$) + nl$
			out$ = out$ + "--" + bound1$ + nl$ 
			out$ = out$ + "Content-Type: text/html; charset=" + qt$ + "iso-8859-1" + qt$ + nl$
			out$ = out$ + "Content-Transfer-Encoding: quoted-printable" + nl$ + nl$
			out$ = out$ + html$ + nl$
		Else
			out$ = out$ + "Content-Type: multipart/related;" + nl$ + Chr$(9)
			out$ = out$ + "boundary=" + qt$ + bound1$ + qt$ + nl$ + nl$ + nl$
			out$ = out$ + "--" + bound1$ + nl$
			out$ = out$ + "Content-Type: multipart/alternative;" + nl$ + Chr$(9)
			out$ = out$ + "boundary=" + qt$ + bound2$ + qt$ + nl$ + nl$ + nl$
			out$ = out$ + "--" + bound2$ + nl$
			out$ = out$ + "Content-Type: text/plain; charset=" + qt$ + "iso-8859-1" + qt$ + nl$
			out$ = out$ + "Content-Transfer-Encoding: quoted-printable" + nl$ + nl$
			out$ = out$ + qp_enc$(plain$) + nl$
			out$ = out$ + "--" + bound2$ + nl$
			out$ = out$ + "Content-Type: text/html; charset=" + qt$ + "iso-8859-1" + qt$ + nl$
			out$ = out$ + "Content-Transfer-Encoding: quoted-printable" + nl$ + nl$
			out$ = out$ + html$ + nl$
			out$ = out$ + "--" + bound2$ + "--" + nl$ + nl$
			For image.html_image=Each html_image
				out$ = out$ + "--" + bound1$ + nl$
				out$ = out$ + "Content-Type: image/" + Right$(image.html_image\filename$, 3)
				out$ = out$ + "; name=" + qt$ + image.html_image\filename$ + qt$ + nl$
				out$ = out$ + "Content-Transfer-Encoding: base64" + nl$
				out$ = out$ + "Content-ID: <" + image.html_image\cid$ + ">" + nl$ + nl$
				out$ = out$ + image.html_image\idata$ + nl$
			Next
		EndIf
		out$ = out$ + "--" + bound1$ + "--" + nl$ + nl$
	Else
		out$ = out$ + "Content-Type: text/plain;" + nl$ + nl$
		out$ = out$ + "Content-Transfer-Encoding: quoted-printable" + nl$
		out$ = out$ + qp_enc$(plain$) + nl$
	EndIf
		
	If First inc_data <> Null
		For inc.inc_data=Each inc_data
			out$ = out$ + "--" + bound$ + nl$
			out$ = out$ + "Content-Type: application/octete-stream"
			out$ = out$ + "; name=" + qt$ + inc.inc_data\filename$ + qt$ + nl$
			out$ = out$ + "Content-Transfer-Encoding: base64" + nl$
			out$ = out$ + "Content-Disposition: attachment; filename=" + qt$ + inc.inc_data\filename$ + qt$ + nl$ + nl$
			out$ = out$ + inc.inc_data\idata$ + nl$
		Next
	EndIf
	
	out$ = out$ + "--" + bound$ + "--" + nl$
	
	Delete Each html_image
	Delete Each inc_data
	
	Return out$
End Function

; Converts an external file into a string
; inp$ = A string containing the filepath to the required file
; This function returns a string containing the files data
Function file_to_string$(inp$)
	Local file, out$
	
	file = ReadFile(inp$)
	If (Not file) Then RuntimeError "Couldn't open '" + inp$ + "' for encoding."
	
	Repeat
		out$ = out$ + Chr$(ReadByte(file))
	Until Eof(file)
	
	CloseFile(file)
	
	Return out$
End Function

; Encodes files as attachments
; file$ = A string containing the filepath to the required file
Function process_include$(file$)
	inc.inc_data = New inc_data
	inc.inc_data\filename$ = file$
	inc.inc_data\idata$ = base64_enc$(file_to_string$(file$))
End Function

; Scans the HTML string loads and encodes any images and replaces their filename with a ContentIDentifier
; inp$	= A string containing the HTML text
; dir$	= A string containing the path to the original HTML file - optional (if the HTML file is in the same directory
;																			as your code)
; This function returns a string containing the processed HTML text
Function process_html$(inp$, dir$="")
	Local out$ = inp$, img$, ifrom, ito = 1, fnd
	If dir$ <> "" And Right$(dir$, 1) <> "" Then dir$ = dir$ + ""
	
	Repeat
		ifrom = Instr(Lower$(inp$), "<img", ito)
		If ifrom
			ifrom = Instr(Lower$(inp$), "src", ifrom + 4) + 3
			ifrom = Instr(Lower$(inp$), Chr$(34), ifrom) + 1
			ito = Instr(Lower$(inp$), Chr$(34), ifrom)
			fnd = False
			For image.html_image=Each html_image
				If image.html_image\filename$ = Mid$(inp$, ifrom, ito - ifrom) Then fnd = True
			Next
			If (Not fnd)
				image.html_image = New html_image
				image.html_image\filename$ = Mid$(inp$, ifrom, ito - ifrom)		 
				image.html_image\cid$ = unique$()
				image.html_image\idata$ = base64_enc$(file_to_string$(dir$ + image.html_image\filename$))
				out$ = Replace$(out$, image.html_image\filename$, "cid:" + image.html_image\cid$)
			EndIf 
		EndIf
	Until (Not ifrom)
	
	Return qp_enc$(out$)
End Function

; Encodes a string using the base64 algorithm
; inp$ = A string containing the data to be encoded
; add_nl = 1 - adds a newline seguence at the end of the encoded string, 0 - no newline (if string is < 76 chars)
; This function returns a string containing the encoded data
; You shouldn't need to call this directly. But feel free.
Function base64_enc$(inp$, add_nl=1)
	Local b64_enc$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
	Local nl$ = Chr$(13) + Chr$(10)
	Local out$, trp$, char, i = 1
		
	Repeat
		trp$ = Mid$(inp$, i, 3)
	
		Select Len(trp$)
			Case 3
				out$ = out$ + Mid$(b64_enc$, (Asc(Mid$(trp$, 1, 1)) Shr 2) + 1, 1)
				out$ = out$ + Mid$(b64_enc$, (((Asc(Mid$(trp$, 1, 1)) Shl 4) Or (Asc(Mid$(trp$, 2, 1)) Shr 4)) And $3f) + 1, 1)
				out$ = out$ + Mid$(b64_enc$, (((Asc(Mid$(trp$, 2, 1)) Shl 2) Or (Asc(Mid$(trp$, 3, 1)) Shr 6)) And $3f) + 1, 1)
				out$ = out$ + Mid$(b64_enc$, (Asc(Mid$(trp$, 3, 1)) And $3f) + 1, 1)
			Case 2
				out$ = out$ + Mid$(b64_enc$, (Asc(Mid$(trp$, 1, 1)) Shr 2) + 1, 1)
				out$ = out$ + Mid$(b64_enc$, (((Asc(Mid$(trp$, 1, 1)) Shl 4) Or (Asc(Mid$(trp$, 2, 1)) Shr 4)) And $3f) + 1, 1)
				out$ = out$ + Mid$(b64_enc$, ((Asc(Mid$(trp$, 2, 1)) Shl 2) And $3f) + 1, 1)
				out$ = out$ + "="
			Case 1
				out$ = out$ + Mid$(b64_enc$, (Asc(Mid$(trp$, 1, 1)) Shr 2) + 1, 1)
				out$ = out$ + Mid$(b64_enc$, ((Asc(Mid$(trp$, 1, 1)) Shl 4) And $3f) + 1, 1)
				out$ = out$ + "=="
		End Select
	
		i = i + 3
		char = char + 4
		If char = 76
			out$ = out$ + nl$
			char = 0
		EndIf
	Until i > Len(inp$)
	If char And add_nl Then out$ = out$ + nl$
	
	Return out$
End Function

; Decodes a string that's been encoded with the base64 algorithm
; inp$ = A string containing the encoded data
; This function returns a string containing the decoded data
; You shouldn't need to call this directly. But feel free.
Function base64_dec$(inp$)
	Local b64_enc$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
	Local out$, oct, i = 1, qc, char 

	Repeat
		char = Instr(b64_enc$, Mid$(inp$, i, 1))
		If char > 0
			If char = 65 Then char = 1
			oct = (oct Shl 6) Or ((char - 1) And $3f)
			qc = qc + 1
		EndIf
		
		If qc = 4
			out$ = out$ + Chr$((oct Shr 16) And $ff) + Chr$((oct Shr 8) And $ff) + Chr$(oct And $ff)
			
			oct = 0
			qc = 0
		EndIf
	
		i = i + 1
	Until i > Len(inp$)
	
	Return out$
End Function
		
; Encodes a string using the quoted printable algorithm
; inp$ = A string containing the text to be encoded
; This function returns a string containing the encoded text
; You shouldn't need to call this directly. But feel free.
Function qp_enc$(inp$)
	Local qp_asc$ = Chr$(9) + " !#$%&'()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
	Local nl$ = Chr$(13) + Chr$(10)
	Local out$, cnt, char$, i
	
	Repeat
		i = i + 1
		char$ = Mid$(inp$, i, 1)
		If Instr(qp_asc$, char$)
			out$ = out$ + char$
			cnt = cnt + 1
		Else
			If Mid$(inp$, i, 2) <> nl$
				If cnt > 72
					out$ = out$ + "=" + nl$
					cnt = 0
				EndIf
				out$ = out$ + "=" + Right$(Hex$(Asc(char$)), 2)
				cnt = cnt + 3
			Else
				out$ = out$ + nl$
				i = i + 1
				cnt = 0
			EndIf
		EndIf
		If cnt = 75
			out$ = out$ + "=" + nl$
			cnt = 0
		EndIf
	Until i >= Len(inp$)
	
	If cnt Then out$ = out$ + nl$
	
	Return out$
End Function

; Spits out a 24 digit (96 bit) random(ish) hexadecimal string
Function unique$()
	Local out$, i
	
	For i= 1 To 12
		out$ = out$ + Right$(Hex$(Rand(1, 255)), 2)
	Next
	
	Return out$
End Function

Usage example (you'll need to supply your own HTML message and attachment ZIP)...

;************************************************************************
;***					Blitz Mail Library Demo
;***		          Yan 2001 - yan@...
;************************************************************************

Graphics 640, 480, 0, 2

; Add the MIME library
Include "BlitzMailInc.bb"

; Get the required parameters
name_from$ = Input$("Enter your name : ")

Repeat
	mail_from$ = Input$("Enter your e-mail address : ")
Until mail_from$ <> "" And Instr(mail_from$, "@") > 0

Print

to_name$ = Input$("Enter the recipients name : ")

Repeat
	to_mail$ = Input$("Enter the recipients e-mail address : ")
Until to_mail$ <> "" And Instr(to_mail$, "@") > 0

Print

subject$ = Input$("Enter subject text : ")

Print

server$ = Input$("Enter SMTP server name (default - 'mail.crosswinds.net') : ")
If server$ = "" Then server$ = "mail.crosswinds.net"

; Add 'text.zip' as an include
process_include$("text.zip")

; We're using an external file for the HTML. So, lets convert it to a string
tmp_html$ = file_to_string$("demomail2.htm")


; Lets scan the HTML string for images, load and encode them. Then insert their CID into the relevant image tags
; If the HTML was in a different directory to this code - tmp_html$ = process_html$(tmp_html$, "HTML directory")
tmp_html$ = process_html$(tmp_html$)

; Now assemble all of the data into one MIME mail complete with relevant encoding.
html$ = build_mime$(tmp_html$, "Some Plain Text incase the receiving mail client can't cope with HTML.")

; The above could have been accomplished with...
;html$ = build_mime$(process_html$(file_to_string$("demomail2.htm")), "Some Demo Text")

Print
Print "Sending Mail (for dial up networks you may have to connect manually first)"
Print
Delay 100 ; Hmmm...You need this delay to get the above text to print ???
; Now send the mail
Print send_mail$(mail_from$, name_from$, to_mail$, to_name$, subject$, html$, server$)
Print
Print "Any key to exit"
WaitKey()

End