POP3Client
Frank Dzaebel, erstellt am: 1.3.2006, zuletzt geändert: 28.6.2006
Kategorie: POP3, .NET-Version: 1.1, [Download]
Eine kleine Library zum Zugriff auf POP3. Andere Links:
A POP3 Client in C# .NET
Retrieve Mail From a POP3 Server Using C#
Google-Suche: POP3 C#
Versenden und Empfangen von E-Mail
namespace POP3Client
{
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Diagnostics;
using System;
using System.Windows.Forms;
using System.Text.RegularExpressions;
// Ein Delegat-Typ um Status-Änderungs-Ereignisse anzuhängen
public delegate void StatusÄnderungsHandler(string NeuerStatus);
// Ein Delegat-Typ um WarteStatus-Änderungs-Ereignisse anzuhängen
public delegate void WarteStatusÄnderungsHandler(WStatus NeuerWarteStatus);
#region VStatus, WStatus
/// <summary>Verbindungsstatus zum POP3-Server</summary>
public enum VStatus
{
/// <summary>Die Verbindung zum PO3Server ist 'getrennt'.
/// Die Reihenfolge ist GETRENNT, AUTHORISIERUNG, TRANSAKTION, MODIFIKATION. </summary>
GETRENNT = 0,
/// <summary>Die Verbindung zum PO3Server ist im Zustand 'Authorisierung des Benutzers'.
/// Die Reihenfolge ist GETRENNT, AUTHORISIERUNG, TRANSAKTION, MODIFIKATION. </summary>
AUTHORISIERUNG = 1,
/// <summary>Die Verbindung zum PO3Server ist im Zustand 'TRANSAKTION'.
/// Die Reihenfolge ist GETRENNT, AUTHORISIERUNG, TRANSAKTION, MODIFIKATION. </summary>
TRANSAKTION = 2,
/// <summary>Die Verbindung zum PO3Server ist im Zustand 'MODIFIKATION'.
/// Die Reihenfolge ist GETRENNT, AUTHORISIERUNG, TRANSAKTION, MODIFIKATION. </summary>
MODIFIKATION = 3
};
/// <summary>WarteStatus. WarteStatus der Verbindung zum POP3-Server (für einen Client-Progressbar). Bei Beginn eines Warte-Vorganges auf WStatus.BEGONNEN, bei Ende auf WStatus.BEENDET</summary>
public enum WStatus
{
/// <summary>Der Pop3Server wartet auf die Beendigung eine Anfrage</summary>
BEGONNEN = 0,
/// <summary>Das Warten auf die Beendigung eine Anfrage an den POP3Server ist beendet.</summary>
BEENDET = 1
};
#endregion
public class POP3client
{
#region Öffentliche globale Variablen
/// <summary>Port (Bei Mails standardmäßig = 110). Die Anschlussnummer des Remotehosts, mit dem eine Verbindung hergestellt werden soll. </summary>
public int SERVER_PORT = 110;
/// <summary>Globaler Benutzername</summary>
public string user;
/// <summary>Globales Passwort</summary>
public string pwd;
/// <summary>Globaler Hostname</summary>
public string pop;
/// <summary>Globaler Fehler-Status</summary>
public bool Fehler;
#endregion
#region Private globale Variablen
/// <summary>Membervariable zu [Status]. Status der Verbindung. Anfangs VStatus.GETRENNT</summary>
private VStatus m_Status = VStatus.GETRENNT;
/// <summary>Membervariable zu [WarteStatus]. WarteStatus der Verbindung (für einen Client-Progressbar). Bei Beginn eines Warte-Vorganges auf WStatus.BEGONNEN, bei Ende auf WStatus.BEENDET</summary>
private WStatus m_WarteStatus = WStatus.BEENDET;
private TcpClient Server;
private NetworkStream NetStrm;
private StreamReader RdStrm;
private string Data;
private byte[] szData;
private string CRLF = "\r\n";
private Regex RegX;
#endregion
public POP3client()
{
}
public POP3client(string pop_server,string user_name,string password)
{
pop = pop_server; user = user_name; pwd = password;
}
// Ein Ereignis, das der Client benutzen kann um Änderungen am Status der Verbindung zu behandeln.
public event StatusÄnderungsHandler StatusGeändert;
// Ein Ereignis, das der Client benutzen kann um Änderungen am WarteStatus einer mail-Funktion z.B. über eine ProgressBar darzustellen.
public event WarteStatusÄnderungsHandler WarteStatusGeändert;
#region Connect() - Methode
public string connect(string pop_server)
{
pop = pop_server; return (connect());
}
/// <summary>Verbindung zum 'pop_server' wird aufgebaut. </summary>
/// <returns>Beispiel: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>"</returns>
public string connect()
{
WarteStatus = WStatus.BEGONNEN;
try
{ // Initialisierung
Server = new TcpClient(pop,SERVER_PORT);
NetStrm = Server.GetStream();
RdStrm = new StreamReader(Server.GetStream());
//The pop session is now in the AUTHORISIERUNG Status
Status = VStatus.AUTHORISIERUNG;
return (RdStrm.ReadLine());
}
catch (Exception Ex)
{
return ("Fehler: " + Ex.ToString());
}
finally { WarteStatus = WStatus.BEENDET; }
}
#endregion
#region Private Methoden
/// <summary>Wenn (Status != stat) ist, wird einen Fehlerstring zurückgegeben, der besagt, dass der VStatus 'stat' im augenblicklichen Zustand nicht erlaubt ist </summary>
/// <param name="stat">Augenblicklicher VStatus</param>
/// <returns>Fehlerstring</returns>
private bool Ist_Status(VStatus stat,ref string Meldung)
{
if (Status != stat)
{
StackFrame CallStack = new StackFrame(1,true);
Meldung = String.Format(
"Bei Aufruf von Kommando \"{0}\" (Zeile {1}) ist der Zustand [{2}] nicht erlaubt!\r\nMomentaner Zustand: [{3}]",
CallStack.GetMethod().Name,CallStack.GetFileLineNumber(),Enum.GetName(typeof(VStatus),stat),Enum.GetName(typeof(VStatus),Status));
}
else Meldung = "";
return (Status == stat);
}
private string disconnect()
{
string temp = "Verbindung getrennt.";
if (Status != VStatus.GETRENNT)
{ //Verbindung trennen
NetStrm.Close(); RdStrm.Close();
Status = VStatus.GETRENNT;
}
else
{
temp = "Nicht verbunden.";
}
return (temp);
}
private void issue_command(string command)
{
//Sendet das Kommando zum POP Server.
Data = command + CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
}
private string read_single_line_response()
{
//read the response of the pop server.
string temp;
try
{
temp = RdStrm.ReadLine();
was_pop_error(temp);
return (temp);
}
catch (Exception Ex)
{
Fehler = true;
return ("Fehler in read_single_line_response(): " + Ex.ToString());
}
}
private string read_multi_line_response()
{
//read the response of the pop server.
string temp = "";
string szTemp;
try
{
szTemp = RdStrm.ReadLine();
was_pop_error(szTemp);
if (!Fehler)
{
while (szTemp != ".")
{
temp += szTemp + CRLF;
szTemp = RdStrm.ReadLine();
}
}
else
{
temp = szTemp;
}
return (temp);
}
catch (InvalidOperationException err)
{
return ("Fehler in read_multi_line_response(): " + err.ToString());
}
}
/// <summary>Setzt Fehler auf true, wenn der POP-Server einen Fehler während des letzten Client-Befehls entdeckt. </summary>
/// <param name="response"></param>
private void was_pop_error(string response)
{
if (response.StartsWith("-"))
{ ///<comment> Wenn der erste Buchstabe der Antwort ein "-" ist, dann hat der POP-Server einen Fehler während des letzten Client-Befehls entdeckt <comment>
Fehler = true;
}
else Fehler = false; //Erfolg
}
#endregion
#region POP commands
/// <summary>DELE löscht eine Meldung. Sie wird tatsächlich aber erst gelöscht, wenn das QUIT-Kommando aufgerufen wird. Deswegen kann man auch eine Meldung undeleten mit RSET (z.B. bei einem Fehler).</summary>
/// <param name="msg_number">MeldungsNr die zu löschen ist</param>
/// <returns>Beispiel1: "DELE 1" -> "S: +OK message 1 deleted"
/// Beispiel2: "DELE 2" -> "S: -ERR message 2 already deleted" </returns>
public string DELE(int msg_number)
{
string temp = "";
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
issue_command("DELE " + msg_number.ToString());
temp = read_single_line_response();
}
return (temp);
}
/// <summary>
/// LIST listet Meldungsnummer(n) mit deren Byte-Größe der Mailbox auf.
/// Das tut es, indem es das "Standard-POP3-Multiline-Antwortformat" nutzt.
/// Hier ein Beispiel:
/// LIST
/// +OK Mailbox contents follow
/// 1 7774
/// 2 513
/// 3 10493
/// .
/// Die letzte Zeile ist eine einzelne 'Punkt-Zeile', was Standard bei allgemeinen Mail-Protokollen ist.
/// Jede Zeile enthält die MeldungsNr gefolgt von der Größe in Bytes.
/// Wenn man eine MeldungsNr löscht (DELE), würde sie hier nicht erscheinen, deswegen tappen Sie nicht in die Falle,
/// dass Nummern sequentiell sind und deswegen ignoriert werden könnten!
/// </summary>
public string LIST()
{
string temp = "";
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
issue_command("LIST");
temp = read_multi_line_response();
}
return (temp);
}
/// <summary>
/// LIST listet Meldungsnummer(n) mit deren Byte-Größe der Mailbox auf.
/// Das tut es, indem es das "Standard-POP3-Multiline-Antwortformat" nutzt.
/// Hier ein Beispiel:
/// LIST
/// +OK Mailbox contents follow
/// 1 7774
/// 2 513
/// 3 10493
/// .
/// Die letzte Zeile ist eine einzelne 'Punkt-Zeile', was Standard bei allgemeinen Mail-Protokollen ist.
/// Jede Zeile enthält die MeldungsNr gefolgt von der Größe in Bytes.
/// Wenn man eine MeldungsNr löscht (DELE), würde sie hier nicht erscheinen, deswegen tappen Sie nicht in die Falle,
/// dass Nummern sequentiell sind und deswegen ignoriert werden könnten!
/// </summary>
public string LIST(int msg_number)
{
string temp = "";
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
issue_command("LIST " + msg_number.ToString());
temp = read_single_line_response(); //when the message number is supplied, expect a single line response
}
return (temp);
}
/// <summary>Der POP3Server tut nichts, außer, dass er eine positive Antwort ("+OK") ausgibt.</summary>
/// <returns></returns>
public string NOOP()
{
string temp = "";
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
issue_command("NOOP");
temp = read_single_line_response();
}
return (temp);
}
/// <summary>Dem Pop3Client wird das Passwort mitgeteilt. </summary>
/// <returns></returns>
public string PASS()
{
string temp = "";
if (Ist_Status(VStatus.AUTHORISIERUNG,ref temp))
{
if (pwd != null)
{
issue_command("PASS " + pwd);
temp = read_single_line_response();
if (!Fehler)
{
//Übergang zu VStatus.TRANSAKTION
Status = VStatus.TRANSAKTION;
}
}
else
{
temp = "Kein Passwort gesetzt.";
}
}
return (temp);
}
/// <summary>Dem Pop3Client wird das Passwort mitgeteilt.</summary>
/// <param name="password">Mail-Passwort</param>
/// <returns></returns>
public string PASS(string password)
{
pwd = password; //put the supplied password into the appropriate property
return (PASS()); //call PASS() with no arguement
}
/// <summary>QUIT terminiert die Session und löscht jede mit DELE markierte Meldung.</summary>
/// <returns>Beispiel: "+OK dewey POP3 server signing off (2 messages left)"</returns>
public string QUIT()
{
//QUIT is valid in all pop states
string temp;
if (Status != VStatus.GETRENNT)
{
issue_command("QUIT");
temp = read_single_line_response();
temp += CRLF + disconnect();
}
else
{
temp = "Not Connected.";
}
return (temp);
}
/// <summary>RETR ruft eine Meldung vom Pop-Server ab. Nutzen Sie die MeldungsNr vom List-Kommando. Sie erhalten den realen Text der Meldung, Header-Zeilen gefolgt von einer Leerzeile gefolgt von den 'body'-Zeilen, gefolgt von einem Punkt auf einer einzelnen neuen Zeile. Enthält die Nachricht schon so eine Zeile, wird sie dubliziert. Der Client muß sie dann zurück setzen.</summary>
public string RETR(int msg)
{
string temp = "";
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
// Retrieve: Abruf der Mail mit Nummer Mail Parameter
issue_command("RETR " + msg.ToString());
temp = read_multi_line_response();
}
return (temp);
}
/// <summary>Wenn Meldungen (durch DELE MedlungsNr) als gelöscht markiert wurden, werden diese Löschung(en) wieder aufgehoben. Der POP3 server antwortet z.B. mit "+OK maildrop has 2 messages (320 octets)". </summary>
/// <returns></returns>
public string RSET()
{
string temp = "";
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
issue_command("RSET");
temp = read_single_line_response();
}
return (temp);
}
/// <summary> STAT antwortet einfach mit einer einzelnen Zeile, die aus zwei Nummern besteht: Die Anzahl von Meldungen in der MailBox, und der Gesamt-Größe der Meldungen in Bytes. Ähnlich ist das LIST Kommando</summary>
/// <param name="Anzahl">Die Anzahl von Meldungen in der MailBox</param>
/// <param name="Bytes">Gesamt-Größe der Meldungungen in Bytes</param>
/// <returns>[string] Beispiel-Rückgabe: "+OK 23 1352"</returns>
public string STAT(ref int Anzahl,ref int Bytes)
{
string temp = ""; Anzahl = 0; Bytes = 0;
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
issue_command("STAT");
temp = read_single_line_response();
if (!Fehler)
{
try //ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1031/cpgenref/html/cpconregularexpressionslanguageelements.htm
{
RegX = new Regex(@"\+OK\s*(?'Anzahl'\d+)\s*(?'Bytes'\d+)");
Anzahl = int.Parse(RegX.Match(temp).Result("${Anzahl}"));
Bytes = int.Parse(RegX.Match(temp).Result("${Bytes}"));
}
catch (Exception Ex)
{
temp = Ex.Message;
}
}
}
return (temp);
}
/// <summary>TOP gibt die Kopfzeilen der Meldung 'Msg' und 'Zeilen' Meldungszeilen des body's der Meldung zurück. Wenn 'Zeilen'=0 ist, erhält man natürlich nur die Kopfzeilen, was für für Filter-Zwecke benutzt wird, ohne gleich die ganze Meldung downzuloaden. TOP ist zwar ein optionales Kommando, aber die meisten POP-Server unterstützen es. </summary>
/// <param name="Msg">MeldungsNr, deren Kopfzeilen erhalten werden sollen</param>
/// <param name="Zeilen">Anzahl der Zeilen des body's der Meldung, die zusätzlich angefügt werden sollen.</param>
/// <returns></returns>
public string TOP(int Msg,int Zeilen)
{
string temp = "";
if (Ist_Status(VStatus.TRANSAKTION,ref temp))
{
issue_command(String.Format("TOP {0} {1}",Msg.ToString(),Zeilen.ToString()));
temp = read_multi_line_response();
}
return (temp);
}
/// <summary>Dem Pop3Client wird der Benutzername mitgeteilt. </summary>
/// <returns></returns>
public string USER()
{
string temp = "";
if (Ist_Status(VStatus.AUTHORISIERUNG,ref temp))
{
if (user != null)
{
issue_command("USER " + user);
temp = read_single_line_response();
}
else
{
temp = "Es wurde kein Benutzer definiert.";
}
}
return (temp);
}
/// <summary>Dem Pop3Client wird der Benutzername mitgeteilt. </summary>
/// <returns></returns>
public string USER(string user_name)
{
user = user_name;
return (USER()); //Aufruf: USER ohne Argumente
}
#endregion
/// <summary>Globaler Verbindungs-Status </summary>
public VStatus Status
{
get { return m_Status; }
set
{
m_Status = value;
if (StatusGeändert != null)
StatusGeändert(Enum.GetName(typeof(VStatus),m_Status)); //Ereignis auslösen
}
}
/// <summary>Globaler Warte-Status [WStatus.BEGONNEN, BEENDET, KEIN_WARTEN] </summary>
public WStatus WarteStatus
{
get { return m_WarteStatus; }
set
{
m_WarteStatus = value;
if (WarteStatusGeändert != null)
WarteStatusGeändert(m_WarteStatus); //Ereignis auslösen
}
}
}
}