Email mit SMTP-Auth

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
rolaf
Beiträge: 3843
Registriert: 10.03.2005 14:01

Re: Email mit SMTP-Auth

Beitrag von rolaf »

NicTheQuick hat geschrieben:Sorry, hab das ganze mal komplett verbessert.
Was entschuldigst du dich für ne Verbesserung? :mrgreen:

Sieht interessant aus, funktioniert auch, nur ob ich da so schnell Durchblicke - hmmm mal sehen. :o
:::: WIN 10 :: PB 5.73 :: (x64) ::::
Benutzeravatar
HeX0R
Beiträge: 3040
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: Email mit SMTP-Auth

Beitrag von HeX0R »

Is ja lustig, ich hatte mich auch vorhin drangesetzt das zu verbessern.
Damit es nicht völlig umsonst war, hier meine Version:

Siehe hier
Zuletzt geändert von HeX0R am 15.07.2010 20:06, insgesamt 1-mal geändert.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8807
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Email mit SMTP-Auth

Beitrag von NicTheQuick »

Receive- und SendNetworkData() sind bei dir noch nicht korrekt. Es kann sein, dass nicht alles auf einmal empfangen oder gesendet wurde.

Ansonsten finde ich das mit den Phasen merkwürdig. Aber die Enumeration für die Fehlermeldungen gefällt mir dafür besser als bei mir.
Benutzeravatar
HeX0R
Beiträge: 3040
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: Email mit SMTP-Auth

Beitrag von HeX0R »

SendNetworkData() kann man aktuell ohne API nicht wirklich gut machen, daher schenke ich mir das für die kleinen zu sendenden Datenmengen.
Es ist in deinem Code genauso falsch einfach bei Rückgabewert von -1 von einem Verbindungsabbruch auszugehen, das ist aller Wahrscheinlichkeit nach eher ein WSAEWOULDBLOCK.

Und die Receivefunktion ist verbesserungswürdig, da gebe ich dir recht, aber für diese Kinkerlitzchen g'schissen gut ;)

[Nachtrag:]
Das mit den Phasen hat z.B. den Vorteil, dass man es auch sehr einfach ändern kann in z.B. SMTP ohne Auth (also je nach Rückgabewert die Phasen verändern)
Benutzeravatar
rolaf
Beiträge: 3843
Registriert: 10.03.2005 14:01

Re: Email mit SMTP-Auth

Beitrag von rolaf »

Zur Komplettverwirrung jetzt noch meine AnfängerFreundlicheIdiotenFassung die ich jetzt nutze: :lol:

Code: Alles auswählen

;PB-Mail (PB 4.41, PB 4.50)
;von Der-T, Kiffi, DrFalo, NicTheQuick, HeX0R, ...

InitNetwork()

Procedure.s Mail_ReceiveString(ConnectID)

  Protected Result.s = Space(4999)
  
  ReceiveNetworkData(ConnectID, @Result, 4999)
  ProcedureReturn Left(Result, 3)

EndProcedure

Procedure Mail_SendString(ConnectID, StringToSend.s)
  
  StringToSend + #CRLF$
  SendNetworkData(ConnectID, @StringToSend, Len(StringToSend))

EndProcedure

Procedure.i Mail_SendMail(Server.s, User.s, Pass.s, MailName.s, MailFrom.s, MailTo.s, Subject.s, Content.s)

  Protected TmpString.s
  Protected ConnectID = OpenNetworkConnection(Server, 25)

  If ConnectID

    If Mail_ReceiveString(ConnectID) = "220"
      Mail_SendString(ConnectID, "EHLO PB-Mail")
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf

    If Mail_ReceiveString(ConnectID) = "250"
      Mail_SendString(ConnectID, "AUTH LOGIN")
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf
  
    If Mail_ReceiveString(ConnectID) = "334"
      TmpString = Space(1024)
      Base64Encoder(@User, StringByteLength(User), @TmpString, 1024)
      Mail_SendString(ConnectID, TmpString)
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf

    If Mail_ReceiveString(ConnectID) = "334"
      TmpString = Space(1024)
      Base64Encoder(@Pass, StringByteLength(Pass), @TmpString, 1024)
      Mail_SendString(ConnectID, TmpString)
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf

    If Mail_ReceiveString(ConnectID) = "235"
      Mail_SendString(ConnectID, "MAIL FROM: <" + MailFrom + ">")
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf

    If Mail_ReceiveString(ConnectID) = "250"
      Mail_SendString(ConnectID, "RCPT TO: <" + MailTo + ">")
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf

    If Mail_ReceiveString(ConnectID) = "250"
      Mail_SendString(ConnectID, "DATA")
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf

    If Mail_ReceiveString(ConnectID) = "354"
      Mail_SendString(ConnectID, "From: " + MailName + " <" + MailFrom + ">")
      Mail_SendString(ConnectID, "To: <" + MailTo + ">")
      Mail_SendString(ConnectID, "Subject: " + Subject)
      Mail_SendString(ConnectID, "")
      Mail_SendString(ConnectID, Content)
      Mail_SendString(ConnectID, ".")
    Else
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 0
    EndIf

    If Mail_ReceiveString(ConnectID) = "250"
      Mail_SendString(ConnectID, "QUIT")
      CloseNetworkConnection(ConnectID)
      ProcedureReturn 1
    EndIf

  Else

    ProcedureReturn 0

  EndIf

EndProcedure

If Mail_SendMail("server" ,"user" ,"pass" ,"mailname", "mailfrom", "mailto" ,"subject" ,"content")
  MessageRequester("PB-Mail", "Mail wurde erfolgreich versandt.")
Else
  MessageRequester("PB-Mail", "Mail konnte nicht versandt werden.")
EndIf
:::: WIN 10 :: PB 5.73 :: (x64) ::::
Benutzeravatar
HeX0R
Beiträge: 3040
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: Email mit SMTP-Auth

Beitrag von HeX0R »

NicTheQuick hat geschrieben:Receive- und SendNetworkData() sind bei dir noch nicht korrekt. Es kann sein, dass nicht alles auf einmal empfangen oder gesendet wurde.
War gerade beim Schuh-Shopping mit Frauchen, da hat man viel Zeit über allen möglichen Unsinn nachzudenken, z.B. ist mir aufgefallen, dass meine Receive-Funktion absolut korrekt ist, wie sie ist.
Mein Puffer ist 65536 Bytes groß, es kommen aber immer nur maximal 100 oder 200 Bytes als Antwort zurück.
Und das wird mit Sicherheit IMMER als ganzes empfangen.
So dämlich ist TCP/IP nun auch wieder nicht, dass es Bytes einzeln hin und her schickt, und da bei diesem ganzen Handshake Zeugs ohnehin nahezu kein Traffic aufläuft, würde sogar Der-T's Methode nahezu niemals Fehlfunktionen aufweisen.

Interessant wird das ganze erst, wenn man viele und große Dateien hin- und her verschickt, wie bei meinem NeT0R z.B., da läuft der Puffer ständig voll und man kommt nicht mehr ohne API-Kniffe aus.

Z.B. sendet die Gegenstelle ein #FD_WRITE sobald sie wieder weitere Daten aufnehmen kann, darauf kann man dann gezielt reagieren und muss nicht stur einfach mit Gewalt ein Paket zum Empfänger "quetschen".

Schade übrigens, dass das Dev-Team keine Anstalten macht sich mal ein wenig um die Netzwerklibrary zu kümmern.
Feature-Requests werden i.d.R. ignoriert.
Dark
Beiträge: 93
Registriert: 24.08.2007 20:36
Kontaktdaten:

Re: Email mit SMTP-Auth

Beitrag von Dark »

Hallo,
HeX0R hat geschrieben: Mein Puffer ist 65536 Bytes groß, es kommen aber immer nur maximal 100 oder 200 Bytes als Antwort zurück.
Und das wird mit Sicherheit IMMER als ganzes empfangen.
So dämlich ist TCP/IP nun auch wieder nicht, dass es Bytes einzeln hin und her schickt
Erstmal hast du recht, das TCP/IP normalerweise keine einzelnen Bytes verschickt, jedoch ist das Betriebsystem hierfür zuständig und dessen Implementierungen können unterschiedlich sein. Desweiteren können auch Nachrichten fragmentiert verschickt werden, was dazu führen könnte, das du die Nachricht auch wieder als einzelne Fragmente erhälst. Diese Fragemente können z.B. dadurch entstehen das der Server bei SMTP eine lange Willkommensnachricht oder ähnliches versendet, welches die MTU der aktuellen Verbindung übersteigt. Desweiteren könnte es auch einfach sein, das ein Router auf dem Weg einen sehr geringen MTU Wert hat und bereits kleine Pakete fragmentiert.

mfg,
Dark
Benutzeravatar
HeX0R
Beiträge: 3040
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: Email mit SMTP-Auth

Beitrag von HeX0R »

Gut, nehmen wir also mal an, die Pakete würden bei diesem sehr geringen Datenaustausch auch fragmentiert ankommen können, durch irgendeinen worst case.
Dann sind aber ALLE hier vorgestellten ReceiveFunktionen untauglich.
Man müsste dann das Paket auf das Endezeichen, bzw. die beiden Endezeichen, nämlich #CRLF$ hin untersuchen.

Sodele, jeder hat mal am Code rumgepfuscht, jetzt bist du dran Dark ;) ...
Der-T
Beiträge: 65
Registriert: 06.05.2009 20:01

Re: Email mit SMTP-Auth

Beitrag von Der-T »

Wow, ich bin begeistert, was sich hier so alles tut. Daher erst einmal ein ganz großes Danke an alle, die hier ihren Beitrag zur Verbesserung des Codes geleistet und/oder eigene Varianten veröffentlicht haben. Auch ich arbeite noch daran, denn zum einen will ich mal schauen wie gut oder schlecht ich die von Dark geschilderten Probleme mit den PB-Mitteln (WinAPI ist im Moment noch nicht so sehr mein Fall) beheben kann, zum anderen bereitet mir das von Andreas21 beschriebne Problem noch etwas Kopfzerbrechen....

Leider muss ich aber auch gestehen, dass es im Moment sehr langsam vorwärts geht, denn von gestern Abend mal abgesehen, ist zur Zeit das Wetter einfach zu schön um all zu viel Zeit vorm Rechner zu verbringen...
Gruß
Der-T
PB 4.50 RC2
Benutzeravatar
HeX0R
Beiträge: 3040
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win11 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2 + 3
Kontaktdaten:

Re: Email mit SMTP-Auth

Beitrag von HeX0R »

Gut, ich habe mal bei mir einige Verbesserungen eingefügt:

Code: Alles auswählen

;Email (PB 4.41, PB 4.50)
;von Der-T, Kiffi, DrFalo, HeX0R, ...

InitNetwork()
EnableExplicit

Global SMTP_LAST_ERROR

Structure _SMTP_QUESTION_ANSWER_
	Question.s
	Answer.s
EndStructure

Enumeration
	#SMTP_NO_ERROR
	#SMTP_ERROR_SERVER_NOT_AVAILABLE
	#SMTP_ERROR_TIMED_OUT
	#SMTP_ERROR_NO_MEMORY_AVAILABLE
	#SMTP_ERROR_UNKNOWN_DISCONNECT
	#SMTP_ERROR_PHASE_0
	#SMTP_ERROR_PHASE_1
	#SMTP_ERROR_PHASE_2
	#SMTP_ERROR_PHASE_3
	#SMTP_ERROR_PHASE_4
	#SMTP_ERROR_PHASE_5
	#SMTP_ERROR_PHASE_6
	#SMTP_ERROR_PHASE_7
	#SMTP_ERROR_PHASE_8
EndEnumeration

Procedure SendAsciiString(ConnectionID, StringToSend.s, TimeOUT = 1000)
	Protected *Buffer, Result, Pos, R, Size, Error, TimedOUT

	StringToSend + #CRLF$
	*Buffer = AllocateMemory(StringByteLength(StringToSend, #PB_Ascii) + 1)
	If *Buffer
		PokeS(*Buffer, StringToSend, -1, #PB_Ascii)
		Pos      = 0
		Size     = MemorySize(*Buffer) - 1
		TimedOUT = ElapsedMilliseconds() + TimeOUT
		Repeat
			R = SendNetworkData(ConnectionID, *Buffer + Pos, Size - Pos)
			If R = -1
				CompilerIf #PB_Compiler_OS = #PB_OS_Windows
				Error = WSAGetLastError_()
				If Error = #WSAEWOULDBLOCK
					Delay(10)
				Else
					Break
				EndIf
				CompilerElse
				Break
				CompilerEndIf
			Else
				Pos + R
			EndIf
			If TimedOUT < ElapsedMilliseconds()
				Break
			EndIf
		Until Pos = Size
		FreeMemory(*Buffer)
		If Pos = Size
			Result = #True
		EndIf
	EndIf

	ProcedureReturn Result
EndProcedure

Procedure.s MyBase64(String.s)
	Protected Result.s, *Buffer, *TmpString, L

	*TmpString = AllocateMemory(2048)
	
	If *TmpString
		*Buffer = AllocateMemory(StringByteLength(String, #PB_Ascii) + 1)
		If *Buffer
			PokeS(*Buffer, String, -1, #PB_Ascii)
			L = Base64Encoder(*Buffer, MemorySize(*Buffer) - 1, *TmpString, 2048)
			FreeMemory(*Buffer)
			Result = PeekS(*TmpString, L, #PB_Ascii)
		EndIf
		FreeMemory(*TmpString)
	EndIf

	ProcedureReturn Result
EndProcedure

Procedure SendMyMail(Server.s, User.s, Pass.s, MailFrom.s, MailTo.s, Subject.s, Body.s, Port = 25, TimeOUT = 1000)
	Protected Pos, i, L, R, Result, ConnectionID, OptionalParameters.s
	Protected Phase, TimedOUT, *Buffer, a$, b$, c$

	SMTP_LAST_ERROR = #SMTP_NO_ERROR
	
	*Buffer = AllocateMemory($10000)
	If *Buffer = #Null
		SMTP_LAST_ERROR = #SMTP_ERROR_NO_MEMORY_AVAILABLE
		ProcedureReturn #False
	EndIf

	ConnectionID = OpenNetworkConnection(Server, Port)
	If ConnectionID = #Null
		SMTP_LAST_ERROR = #SMTP_ERROR_SERVER_NOT_AVAILABLE
		FreeMemory(*Buffer)
		ProcedureReturn #False
	EndIf
	
	Dim Answers._SMTP_QUESTION_ANSWER_(10)
	Answers(0)\Answer   = "220"
	Answers(0)\Question = "EHLO localhost"
	Answers(1)\Answer   = "250"
	Answers(1)\Question = "AUTH LOGIN"
	Answers(2)\Answer   = "334"
	Answers(2)\Question = MyBase64(User)
	Answers(3)\Answer   = "334"
	Answers(3)\Question = MyBase64(Pass)
	Answers(4)\Answer   = "235"
	Answers(4)\Question = "MAIL FROM: <" + MailFrom + ">"
	Answers(5)\Answer   = "250"
	Answers(5)\Question = "RCPT TO: <" + MailTo + ">"
	Answers(6)\Answer   = "250"
	Answers(6)\Question = "DATA"
	Answers(7)\Answer   = "354"
	Answers(7)\Question = "From: <" + MailFrom + ">" + #CRLF$
	Answers(7)\Question + "To: <" + MailTo + ">"     + #CRLF$
	Answers(7)\Question + "Subject: " + Subject      + #CRLF$ + #CRLF$
	Answers(7)\Question + Body                       + #CRLF$
	Answers(7)\Question + "."
	Answers(8)\Answer   = "250"
	Answers(8)\Question = "QUIT"

	Phase    = 0
	TimedOUT = ElapsedMilliseconds() + TimeOUT

	Repeat
		If NetworkClientEvent(ConnectionID) = #PB_NetworkEvent_Data
			TimedOUT = ElapsedMilliseconds() + TimeOUT
			Pos      = 0
			Repeat
				L = ReceiveNetworkData(ConnectionID, *Buffer + Pos, $10000 - Pos)
				If L = -1
					SMTP_LAST_ERROR = #SMTP_ERROR_UNKNOWN_DISCONNECT
					Break 2
				Else
					Pos + L
				EndIf
				If TimedOUT < ElapsedMilliseconds()
					SMTP_LAST_ERROR = #SMTP_ERROR_TIMED_OUT
					Break 2
				EndIf
			Until Pos > 3 And PeekW(*Buffer + Pos - 2) = $0A0D
			
			TimedOUT = ElapsedMilliseconds() + TimeOUT
			b$       = PeekS(*Buffer, L, #PB_Ascii)
			a$       = Left(b$, 3)
			R        = 0
				
			Debug b$
				
			If a$ = Answers(Phase)\Answer
				R = SendAsciiString(ConnectionID, Answers(Phase)\Question)
			EndIf
			If R
				If Phase = 1
					OptionalParameters = ""
					For i = 1 To CountString(b$, #CRLF$) - 1
						c$ = StringField(b$, i + 1, #LF$)
						c$ = RemoveString(c$, #CR$)
						OptionalParameters + Mid(c$, 5) + #LF$
					Next i
					Debug "Accepted Parameters:" + OptionalParameters
				EndIf
				Phase + 1
				If Phase = 9
					Result = #True
					Phase = 100
				EndIf
			Else
						
				SMTP_LAST_ERROR = #SMTP_ERROR_PHASE_0 + Phase
				Phase = 100
			EndIf

		Else
			Delay(5)
		EndIf

		If TimedOUT < ElapsedMilliseconds()
			SMTP_LAST_ERROR = #SMTP_ERROR_TIMED_OUT
			Break
		EndIf

	Until Phase = 100

	CloseNetworkConnection(ConnectionID)
	FreeMemory(*Buffer)

	ProcedureReturn Result
EndProcedure
	

SendMyMail("mailserver.dein", "user", "passwort", "mailaddy_von", "mailaddy_an", "Betreff", "Inhalt")
Zuletzt geändert von HeX0R am 15.07.2010 22:18, insgesamt 2-mal geändert.
Antworten