Seite 3 von 7

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 11:22
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

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 11:54
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

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 12:00
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.

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 12:03
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)

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 13:32
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

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 18:45
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.

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 19:24
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

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 19:38
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 ;) ...

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 19:46
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...

Re: Email mit SMTP-Auth

Verfasst: 15.07.2010 19:48
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")