SMTP client functions (no authorization)

Miscellaneous Forums/Blitz Showcase/SMTP client functions (no authorization)

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

I've created some SMTP client functions. It works fine, but it doesn't support authorization at the moment and i don't have any SMTP servers either where i could authorize myself, so could anybody help me on that part.

Usage example:
SMTPStream% = SMTPConnect("smtp.some-server.com", "username", "password")
SMTPSendHeader(SMTPStream%, "mail@...", "rcpt@...", "Mail subject")
WriteLine SMTPStream%, "This is the message"
SMTPEndMail(SMTPStream%)


Functions:
Global SMTPError$ = ""

Function SMTPConnect%(server$, username$ = "", password$ = "", port% = 25)
	Local stream% = OpenTCPStream(server$, port%)
	Local AUTH_type% = 0 ; 1 - LOGIN, 2 - PLAIN, 3 - DIGEST-MD5, 4 - CRAM-MD5
	
	If stream%
		While Not Eof(stream%)
			If Not msg$ = ""
				DebugLog "SMTP>>>" + msg$
				WriteLine stream%, msg$
			EndIf
			
			If ReadAvail(stream%)
				txt$ = ReadLine$(stream%): DebugLog "SMTP<<<" + txt$
				code$ = Left$(txt$, 3)
				cmd$ = Mid(txt$, 5)
				value$ = Sector(cmd$, " ", 1, True)
				cmd$ = Sector(cmd$, " ", 0)
			Else
				txt$ = "": code$ = "": cmd$ = "": value$ = ""
			EndIf
			
			msg$ = ""
			Select code$
				Case "220"
					msg$ = "EHLO " + server$
				Case "250"
					; COMMAND
					Select cmd$
						Case "AUTH"
							; SET AUTH type
							If Instr(value$, "PLAIN") Then AUTH_type% = 2
							If Instr(value$, "DIGEST-MD5") Then AUTH_type% = 3
							If Instr(value$, "CRAM-MD5") Then AUTH_type% = 4
							If Instr(value$, "LOGIN") Then AUTH_type% = 1
							
							Select AUTH_type%
								Case 1
									msg$ = "AUTH LOGIN"
								Case 2
									msg$ = "AUTH PLAIN " + base64_enc$(username$ + Chr(0) + username$ + Chr(0) + password$)
								Case 3
									msg$ = "AUTH DIGEST-MD5"
								Case 4
									msg$ = "AUTH CRAM-MD5"
							End Select
						Case "8BITMIME" ; No AUTH type
							If AUTH_type% = 0 Then Return stream%
					End Select
				Case "334" ; AUTH type ok
					Select AUTH_type%
						Case 1 ; LOGIN
							Select cmd$
								Case "VXNlcm5hbWU6" ; Username
									msg$ = base64_enc$(username$)
								Case "UGFzc3dvcmQ6" ; Password
									msg$ = base64_enc$(password$)
							End Select
						Case 3 ; DIGEST-MD5
						Case 4 ; CRAM-MD5
					End Select
				Case "535", "501" ; Authorization failed
					SMTPError$ = Mid(txt$, 5)
					
					CloseTCPStream stream%
					Return False
				Case "235" ; Authorized
					If AUTH_type% = 0 Then Return stream%
			End Select
		Wend
		
		Return stream%
	EndIf
End Function

Function SMTPSendHeader(stream%, from$, rcpt$, subject$)
	Local index% = 0, msg$
	
	msg$ = "MAIL FROM:<" + from$ + ">"
	While Not Eof(stream%)
		If ReadAvail(stream%)
			txt$ = ReadLine$(stream%): DebugLog "SMTP<<<" + txt$
			code$ = Left$(txt$, 3)
			cmd$ = Mid(txt$, 5)
		Else
			code$ = "": cmd$ = ""
		EndIf
		
		If Not msg$ = ""
			DebugLog "SMTP>>>" + msg$
			WriteLine stream%, msg$
		EndIf
		
		msg$ = ""
		Select code$
			Case ""
			Case "250"
				index% = index% + 1
				Select index%
					Case 1
						msg$ = "RCPT TO:<" + rcpt$ + ">"
					Case 2
						msg$ = "DATA"
					Case 3
				End Select
			Case "354"
				msg$ = "Subject: " + subject$
				DebugLog "SMTP>>>" + msg$
				WriteLine stream%, msg$ + Chr(13) + Chr(10)
				Return True
			Default
				SMTPError$ = Mid(txt$, 5)
				Return False
		End Select
	Wend
End Function

Function SMTPEndMail(stream%)
	WriteLine stream%, "."
	WriteLine stream%, "QUIT"
	CloseTCPStream stream%
End Function

Function SMTPLastError$()
	Return SMTPError$
End Function

Function base64_enc$(inp$)
	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

Function Sector$(txt$, separator$, sector%, toend% = False)
	Local result$ = "", occ
	For i = 1 To Len(txt$)
		If Mid$(txt$, i, 1) = separator$
			occ = occ + 1
			If toend% And occ% > sector% Then result$ = result$ + Mid$(txt$, i, 1)
		Else
			If occ => sector Then result$ = result$ + Mid$(txt$, i, 1)
		EndIf
		If Not toend% Then If occ > sector Then Exit
	Next
	Return result$
End Function