Seite 1 von 7

Email mit SMTP-Auth

Verfasst: 06.07.2010 19:26
von Der-T
Hallo,

auch wenn ichnicht davon ausgehe, dass der nachfolgende Code besonders effizient, elegant und beispielhaft oder einfach nur cool ist, so wurde er nachgefragt und mir fällt kein besser Ort als dieses Sub-Forum ein, um der Anfrage nachzukommen.

Folgender Code entstammt meinem Projekt rLOOKvUPs. Die Besonderheit daran ist eben, dass hier als Absender und Empfänger Email-Adresse die gleiche Adresse verwendet wird. Ferner wir lediglich ein übergebener String versender, die Authentifizierung am Mail-Server erfolgt wie der Topic schon sagt mit SMTP-Auth. Erfolgreich getestet habe ich es mit 1&1, GMX und web.de. Den Code habe ich selbst geschrieben, mit ein klein wenig "Inspiration" durch das Code Archive und der Beschreibung einer SMTP-Auth-Sitzung bei Wikipedia.

Viel Spaß damit!

Code: Alles auswählen

InitNetwork() 

Enumeration    
  #NotificationMail  
EndEnumeration

Declare.s ReceiveData(ConnectionID.i)
Declare SendString(ConnectionID, StringToSend.s)

Procedure.i SendMyMail(Server.s,User.s,Pass.s,Mail.s,Betreff.s,Inhalt.s)
  Protected ReturnValue.i = 0      
  Protected ConnectionID
  ConnectionID = OpenNetworkConnection(Server,25)        
  If ConnectionID
    If ReceiveData(ConnectionID) = "220"
      SendString(ConnectionID,"EHLO rvsLOOKUP.localhost")      
      If ReceiveData(ConnectionID) = "250"          
        If ReceiveData(ConnectionID) = "250"
          SendString(ConnectionID,"AUTH LOGIN")          
          If ReceiveData(ConnectionID) = "334"
            Protected tmpString.s = Space(1024) 
            Base64Encoder(@User,StringByteLength(User),@tmpString,1024)
            SendString(ConnectionID,tmpString)            
            If ReceiveData(ConnectionID) = "334"
              tmpString = Space(1024) 
              Base64Encoder(@Pass,StringByteLength(Pass),@tmpString,1024)
              SendString(ConnectionID,tmpString)              
              If ReceiveData(ConnectionID) = "235"
                SendString(ConnectionID,"MAIL FROM: <"+Mail+">")                
                If ReceiveData(ConnectionID) = "250"
                  SendString(ConnectionID,"RCPT TO: <"+Mail+">")                  
                  If ReceiveData(ConnectionID) = "250"
                    SendString(ConnectionID,"DATA")                    
                    If ReceiveData(ConnectionID) = "354"
                      SendString(ConnectionID,"From: <"+Mail+">")                      
                      SendString(ConnectionID,"To: <"+Mail+">")                      
                      SendString(ConnectionID,"Subject: "+Betreff)                      
                      SendString(ConnectionID,"")                      
                      SendString(ConnectionID,Inhalt)                      
                      SendString(ConnectionID,".")                      
                      If ReceiveData(ConnectionID) = "250"
                        SendString(ConnectionID,"QUIT")   
                        CloseNetworkConnection(ConnectionID)
                        ReturnValue = 1                      
                      EndIf                  
                    EndIf                      
                  EndIf                    
                EndIf                  
              EndIf                
            EndIf              
          EndIf            
        EndIf          
      EndIf
    EndIf      
  EndIf 
  ProcedureReturn ReturnValue
EndProcedure

Procedure.s ReceiveData(ConnectionID)
  Define DataResult.s
  DataResult = Space(4999)
  ReceiveNetworkData(ConnectionID,@DataResult,4999)   
  DataResult = Left(DataResult,3)
  ProcedureReturn DataResult
EndProcedure

Procedure SendString(ConnectionID, StringToSend.s)
  StringToSend + #CRLF$
  SendNetworkData(ConnectionID, @StringToSend, Len(StringToSend))
EndProcedure

Re: Email mit SMTP-Auth

Verfasst: 06.07.2010 21:00
von walker
Hi,

das kann aber nur funktionieren, wenn im Datenverkehr keine "Panne" passiert oder sonst eine Verzögerung auftritt. Die Daten müssen immer sofort vorhanden sein ... oder?
Imho ist es besser, diese Routine in eine Repeat/Until Schleife zu packen und die Ereignisse mit NetworkClientEvent() abfragen... und dann in einer Select/Endselect Schleife die einzelnen Schritte abarbeiten .... Angenehmer Nebeneffekt... der Code wird lesbarer ....

Re: Email mit SMTP-Auth

Verfasst: 07.07.2010 17:48
von Der-T
Hallo Walker,

ja, wenn es während der Übertragung zu einem Fehler kommt, kann die Mail nicht gesendet werden. In der Praxis ist das bei mir allerding noch nicht vorgekommen und ich bekomme von meinem rvsLOOKUP täglich im Schnitt 2 bis 3 Mails. Ok, ist nicht viel, aber es dennoch auch, dass das senden bei den wenigen Mails bisher immer tadellos funktioniert hat. Wie du das mit dem Select-Block meinst, ist mir hingegen nicht ganz klar. Klar weiss ich, wie man mit Select arbeitet, aber da der SMTP-Server an unterschiedlichen Stellen die gleichen Meldungen liefert, müsste man innerhalb eines Case-Blocks auch wieder verzweigen, entweder mit nem weiteren Select oder mit if. Ob dies wirklich übersichtlicher wird, sei mal dahingestellt.

Re: Email mit SMTP-Auth

Verfasst: 07.07.2010 20:32
von Blackskyliner
walker hat geschrieben:Hi,

das kann aber nur funktionieren, wenn im Datenverkehr keine "Panne" passiert oder sonst eine Verzögerung auftritt. Die Daten müssen immer sofort vorhanden sein ... oder?
Imho ist es besser, diese Routine in eine Repeat/Until Schleife zu packen und die Ereignisse mit NetworkClientEvent() abfragen... und dann in einer Select/Endselect Schleife die einzelnen Schritte abarbeiten .... Angenehmer Nebeneffekt... der Code wird lesbarer ....
Ich weiß es ist getrollt...

Select/Endselect Schleife gibt es nicht, genau so wenig wie eine if-schleife...
Am Ende sind beides Ausdrücke, die eine Bedingung signalisieren/darstellen/evaluieren...

Ansonsten ist der Code doch für kleinere Projekte ganz i.O. finde ich zumindest :allright:

Re: Email mit SMTP-Auth

Verfasst: 07.07.2010 22:59
von Dark
Hallo,

ich finde erstmal gut das du anderen Leuten den Code zur Verfügung stellst. Jedoch möchte ich auch etwas Kritik hevorbringen, da Anfänger sonst davon ausgehen sie könnten diesen Code einfach benutzen ohne die Gefahren zu kennen.
Es wird nicht überprüft ob Daten vorhanden sind, sondern einfach ReceiveNetworkData() aufgerufen, dies funktioniert nur, da dieser Befehl in PureBasic blocking ist. Dies bedeutet, dass die API von dem jeweiligen Betriebsystem den Thread so lange anhält, bis Daten vorhanden sind.
Dies führt auch direkt zu dem nächsten Problem: Du überprüfst nicht ob überhaupt Daten empfangen wurden (Rückgabewert von ReceiveNetworkData()). Dies könnte zum Beispiel bei einem Disconnect seitens des Serves passieren. Dieser Fehler fällt bei dir nicht so schwer auf, da du eine eigene Prozedur zum Empfangen geschrieben hast, sonst könnte in dem Empfangsbuffer noch der Wert von vorher drinne stehen. Zugleich entsteht hierdurch jedoch ein weiterer sehr wichtiger und gerne ingorierter Fehler: SMTP basiert auf TCP, einem streambasieren (und nicht paketbasierten!) Protokoll. Dies bedeutet das die Daten in beliebig vielen Aufrufen von ReceiveNetworkData auftauchen könne. Es könnte also sein das du einmal nur ein Byte und dann die ganzen anderen Bytes erhälst. In diesem Fall würde die Zahl nicht mehr richtig ausgelesen.
Das gleiche trifft nebenbei auch auf das Senden zu: Bei dem Senden besteht keine Garantie, das alle Daten gesendet werden, sondern es kann vorkommen, das nur die Hälfte oder gar nur ein Byte übermittelt wurde (Rückgabewert!). Die weiteren Daten müssten also nochmal gesendet werden. In der Hilfe steht auch drinne das die größe bei SendNetworkData und TCP nicht 65536 bytes überschreiten darf. Dies könnte jedoch beim Inhalt ggf. überschritten werden.
So wie deine Senden routinen geschrieben sind, sind sie außerdem auch Unicode kompatibel, was sie mit SendNetworkString wären. Jedoch steht in der PureBasic Hilfe nichts zu dem Rückgabewert der Funktion drinne. Es wäre also vll, ratsam eine Umkonvertierungsroutine mithilfe eines CompilerIf zu implementieren.

Zum schluß noch nen kleiner tipp: Initialisiere den Wert von ReturnValue auf #False/0 dann musst du ihn nicht jedes mal ändern, sondern nur im Erfolgsfall auf #True/1 setzen.

mfg,
Dark

Re: Email mit SMTP-Auth

Verfasst: 08.07.2010 19:24
von Der-T
Blackskyliner hat geschrieben:Ansonsten ist der Code doch für kleinere Projekte ganz i.O. finde ich zumindest :allright:
Danke, aber ich habe gerade noch etwas unschönes gefunden. Da stehen immer noch relikte von der PB-Mail-Lib drin (CreateMail etc.), welche überhaupt nicht mehr gebraucht werden, die werde ich noch rausnehmen ....
Dark hat geschrieben:Jedoch möchte ich auch etwas Kritik hevorbringen, da Anfänger sonst davon ausgehen sie könnten diesen Code einfach benutzen ohne die Gefahren zu kennen.
Hallo Dark. In der Tat waren mir die von dir geschilderten Probleme so auch nicht wirklich bewusst. Daher ein ganz großes Danke für deine konstruktive Kritik, werde versuchen, diese in den Code einfliessen zu lassen.

Re: Email mit SMTP-Auth

Verfasst: 10.07.2010 11:43
von Der-T
Ok, die Relikte der PB-Mail-Lib sind nun vollständig entfernt und die wirklich mehr als unnötigen Else-Zweige wurden ebenfalls entfernt. Alle anderen Überarbeitungen dauern noch ein wenig.

Re: Email mit SMTP-Auth

Verfasst: 10.07.2010 13:50
von rolaf
Sehr schön, weiter so... :allright:

Re: Email mit SMTP-Auth

Verfasst: 11.07.2010 21:38
von X360 Andy
Vielen dank für das bereitstellen :)

Re: Email mit SMTP-Auth

Verfasst: 12.07.2010 14:23
von Andreas21
In Zeile 19 und 20 wertest du die Antwort vom Server aus.
Der Server sendet aber diese nur 1 mal. Da bleibt der Code stehen.
Der-T hat geschrieben:

Code: Alles auswählen

19 If ReceiveData(ConnectionID) = "250"          
20   If ReceiveData(ConnectionID) = "250"
Das müste geändert werden.

Und ein InitNetwork() mit rein nehmen damit der Code auch lauffähig ist.