Encapsulating the Protocols

Now that you've got some of the protocol basics down, you're ready for the source code for this channel. Okay, here goes. First you have to encapsulate the SMTP and POP3 protocols that are later used in the client-side and server-side transport channel sinks.

The first part of this is shown in Listing 10-1. This source file encapsulates the lower-level SMTP protocol. It provides a public constructor that needs the hostname of your SMTP-server as a parameter. Its SendMessage() method takes the sender's and recipient's e-mail address and the full e-mail's text (including the headers) as parameters. It then connects to the SMTP server and sends the specified mail.

Listing 10-1: Encapsulating the Lower-Level SMTP Protocol

start example
 using System; using System.Net.Sockets; using System.Net; using System.IO; using System.Text; namespace SMTPChannel {    public class SMTPConnection    {       private String _hostname;       private TcpClient _smtpConnection;       private NetworkStream _smtpStream;       private StreamReader _smtpResponse;       public SMTPConnection(String hostname) {          _hostname = hostname;       }       private void Connect() {          _smtpConnection = new TcpClient(_hostname,25);          _smtpStream = _smtpConnection.GetStream();          _smtpResponse = new StreamReader(_smtpStream);       }       private void Disconnect() {          _smtpStream.Close();          _smtpResponse.Close();          _smtpConnection.Close();       }       private void SendCommand(String command, int expectedResponseClass) {          // command: the SMTP command to send          // expectedResponseClass: first digit of the expected smtp response          // Throws an exception if the server's responsecode's first          // digit (resonse class) is > the expectedResponseClass.          // If expectedResponseClass == 0, it will be ignored          command = command + "\r\n";          byte[] cmd = Encoding.ASCII.GetBytes(command);          _smtpStream.Write(cmd,0,cmd.Length);          String response = _smtpResponse.ReadLine();          if (expectedResponseClass != 0) {             int resp = Convert.ToInt32(response.Substring(0,1));             if (resp > expectedResponseClass) {                throw new Exception("SMTP Server returned unexpected " +                   "response:\n"' + response + "‘");             }          }       }       public void SendMessage(String from, String to, String text) {          try          {             Connect();             SendCommand("HELO localhost",2);             SendCommand("MAIL FROM: <" + from + ">",2);             SendCommand("RCPT TO: <" + to + ">",2);             SendCommand("DATA",3);             byte[] bodybytes = Encoding.ASCII.GetBytes(text + "\r\n");             _smtpStream.Write(bodybytes,0,bodybytes.Length);             SendCommand(".",3);             SendCommand("QUIT",0);          } finally {             try {                Disconnect();             } catch (Exception e) {/*ignore*/};          }       }    } } 
end example

To encapsulate the POP3 protocol, you first have to add a class that will hold the parsed message and its headers. This is shown in Listing 10-2.

Listing 10-2: A Retrieved and Parsed Message

start example
 using System; using System.Collections; namespace SMTPChannel {    public class POP3Msg    {       public String From;       public String To;       public String Body;       public String Headers;       public String MessageId;       public String InReplyTo;    } } 
end example

The helper class POP3Connection encapsulates the lower-level details of the POP3 protocol. After construction, it connects to the server, authenticates the user, and issues a LIST command to retrieve the list of messages waiting.

 using System; using System.Net.Sockets; using System.Net; using System.IO; using System.Collections; using System.Text; namespace SMTPChannel {    public class POP3Connection    {    private class MessageIndex    {       // will be used to store the result of the LIST command       internal int Number;       internal int Bytes;       internal MessageIndex(int num, int msgbytes)       {          Number = num;          Bytes = msgbytes;       }    }    private String _hostname;    private String _username;    private String _password;    private TcpClient _pop3Connection;    private NetworkStream _pop3Stream;    private StreamReader _pop3Response;    private IDictionary _msgs;    public POP3Connection(String hostname, String username, String password)    {       // try to connect to the server with the supplied username       // and password.       _hostname = hostname;       _username = username;       _password = password;       try       {          Connect();       }       catch (Exception e)       {          try          {             Disconnect();          }          catch (Exception ex) {/* ignore */}          throw e;      } } private void Connect() {    // initialize the list of messages    _msgs = new Hashtable();    // open the connection    _pop3Connection = new TcpClient(_hostname,110);    _pop3Stream = _pop3Connection.GetStream();    _pop3Response = new StreamReader(_pop3Stream);    // ignore first line (server's greeting)    String response = _pop3Response.ReadLine();    // authenticate    SendCommand("USER " + _username,true);    SendCommand("PASS " + _password,true);    // retrieve the list of messages    SendCommand("LIST",true);    response = _pop3Response.ReadLine();    while (response != ".")    {       // add entries to _msgs dictionary       int pos = response.IndexOf(" ");       String msgnumStr = response.Substring(0,pos);       String bytesStr = response.Substring(pos);       int msgnum = Convert.ToInt32(msgnumStr);       int bytes = Convert.ToInt32(bytesStr);       MessageIndex msgidx = new MessageIndex(msgnum,bytes);       _msgs.Add (msgidx,msgnum);       response = _pop3Response.ReadLine();    } } 

These methods make use of the SendCommand() method, which sends a specified POP3 command to the server and checks the response if indicated:

 private void SendCommand(String command,bool needOK) {     // sends a single command.     // if needOK is set it will check the response to begin     // with "+OK" and will throw an exception if it doesn't.     command = command + "\r\n";     byte[] cmd = Encoding.ASCII.GetBytes(command);     // send the command     _pop3Stream.Write(cmd,0,cmd.Length);     String response = _pop3Response.ReadLine();     // check the response     if (needOK)     {        if (!response.Substring(0,3).ToUpper().Equals("+OK"))        {           throw new Exception("POP3 Server returned unexpected " +             "response:\n"' + response + "‘");        }     } } 

The MessageCount property returns the number of messages available at the server:

 public int MessageCount {    get    {       // returns the message count after connecting and       // issuing the LIST command       return _msgs.Count;    } } 

GetMessage() returns a POP3Msg object, which is filled by retrieving the specified message from the server. It does this by sending the RETR command to the POP3 server and by checking for special e-mail headers. It then populates the POP3Msg object's properties with those headers and the e-mail body:

 public POP3Msg GetMessage(int msgnum) {    // create the resulting object    POP3Msg tmpmsg = new POP3Msg();    // retrieve a single message    SendCommand("RETR " + msgnum,true);    String response = _pop3Response.ReadLine();    // read the response line by line and populate the    // correct properties of the POP3Msg object    StringBuilder headers = new StringBuilder();    StringBuilder body = new StringBuilder();    bool headersDone=false;    while ((response!= null) && (response != "." ))    {       // check if all headers have been read       if (!headersDone)       {          if (response.Length >0)          {             // this will only parse the headers which are relevant             // for .NET Remoting             if (response.ToUpper().StartsWith("IN-REPLY-TO:"))             {                tmpmsg.InReplyTo = response.Substring(12).Trim();             }             else if (response.ToUpper().StartsWith("MESSAGE-ID:"))             {                tmpmsg.MessageId = response.Substring(11).Trim();             }             else if (response.ToUpper().StartsWith("FROM:"))             {                tmpmsg.From = response.Substring(5).Trim();             }             else if (response.ToUpper().StartsWith("TO:"))             {                tmpmsg.To = response.Substring(3).Trim();             }                headers.Append(response).Append("\n");          }          else          {             headersDone = true;       }       else       {          // all headers have been read, add the rest to          // the body.          // For .NET Remoting, we need the body in a single          // line to decode Base64 therefore no <CR><LF>s will          // be appended!          body.Append(response);       }       // read next line       response = _pop3Response.ReadLine();    }    // set the complete header and body Strings of POP3Msg    tmpmsg.Body = body.ToString();    tmpmsg.Headers = headers.ToString();    return tmpmsg; } 

What's still needed is DeleteMessage(), which flags a message for deletion by sending the DELE command to the server:

 public void DeleteMessage(int msgnum) {    // issue the DELE command to delete the specified message    SendCommand("DELE " + msgnum,true); } 

And finally, you need a method to send the QUIT command and disconnect from the server. This Disconnect() method is shown here:

 public void Disconnect() {    // sends QUIT and disconnects    try    {       // send QUIT to commit the DELEs       SendCommand("QUIT",false);    }    finally    {       // close the connection       _pop3Stream.Close();       _pop3Response.Close();       _pop3Connection.Close();    } } 

With those two helper classes presented previously, you'll be able to access SMTP and POP3 servers without having to deal further with the details of those protocols. Nevertheless, what's still missing is a mapping between the .NET Remoting Framework and those e-mail messages.

Checking for New Mail

As POP3 will not notify the client whenever a message has been received, it is necessary to continuously log on to the server to check for new e-mails. This is done by the POP3Polling class. Each instance of this class contacts a given server in regular intervals. If a message is available, it will fetch it and invoke a delegate on another helper class that will then process this message.

The POP3Polling class can be run in two modes: client or server mode. Whenever it's in client mode, it only starts polling after the client has sent a request to the server. As soon as the server's reply is handled, this class stops polling to save network bandwidth. In server mode, it checks the server regularly to handle any remoting requests.

As you can see in the following part of this class, it provides a custom constructor that accepts the server's hostname, username, password, the polling interval, and a flag indicating whether it should run in server or client mode:

 using System; using System.Collections; using System.Threading; namespace SMTPChannel {    public class POP3Polling    {       delegate void HandleMessageDelegate(POP3Msg msg);       // if it's a remoting server, we will poll forever       internal bool _isServer;       // if this is not a server, this property has to be set to       // start polling       internal bool _needsPolling;       // is currently polling       internal bool _isPolling;       // polling interval in seconds       internal int _pollInterval;       // logon data       private String _hostname;       private String _username;       private String _password;       internal POP3Polling(String hostname, String username, String password,                int pollInterval, bool isServer)       {          _hostname = hostname;          _username = username;          _password = password;          _pollInterval = pollInterval;          _isServer = isServer;          if (!_isServer) { _needsPolling = false; }       } 

The following Poll() method does the real work. It is started in a background thread and checks for new e-mails as long as either _isServer or _needsPolling (which indicates the need for client-side polling) is true. While it polls the server, it also sets a flag to prevent reentrancy.

SMTPHelper.MessageReceived() is a static method that is called by using an asynchronous delegate in a fire-and-forget way. This method will handle the e-mail and forward it to the remoting framework. Have a look at it here:

 private void Poll() {    if (_isPolling) return;    _isPolling = true;    do    {       Thread.Sleep(_pollInterval * 1000);       POP3Connection pop = new POP3Connection(_hostname,_username,_password);       for (int i =1;i<=pop.MessageCount;i++)       {          POP3Msg msg = pop.GetMessage(i);          HandleMessageDelegate del = new HandleMessageDelegate(                  SMTPHelper.MessageReceived);          del.BeginInvoke(msg,null,null);          pop.DeleteMessage(i);       }       pop.Disconnect();       pop = null;    } while (_isServer || _needsPolling);    _isPolling = false; } 

The last method of this class, CheckAndStartPolling(), can be called externally to start the background thread as soon as _needsPolling has been changed. It does the necessary checks to ensure that only one background thread is running per instance.

 internal void CheckAndStartPolling() {    if (_isPolling) return;    if (_isServer || _needsPolling)    }       Thread thr = new Thread(new ThreadStart(this.Poll));       thr.Start();       thr.IsBackground = true;    } } 

Registering a POP3 Server

When creating a custom channel like this, you might want to be able to have one application that can be client and server at the same time. To optimize the polling strategy in this case, you want to have only one instance of the POP3Polling class for a specified e-mail address, and not two separate ones for the client channel and the server channel.

For client-only use of this channel, you also want to have a central point that can be notified as soon as a request has been sent to the server so it can start the background thread to check for new e-mails. Additionally, this class needs a counter of responses that have been sent and for which no reply has yet been received. As soon as all open replies have been handled, it should again notify the POP3Polling object to stop checking for those e-mails.

This class provides a method, RegisterPolling(), that has to be called for each registered channel to create the POP3Polling object. This method first checks whether the same username/hostname combination has already been registered. If this is not the case, it creates the new object and stores a reference to it in a Hashtable.

If the same combination has been registered before, this method first checks whether the new registration request is for a server-side channel and in this case switches the already known POP3Polling object into server mode. It also checks the polling intervals of the old and the new object and takes the lower value. Finally, it calls the POP3Polling object's CheckAndStartPolling() method to enable the background thread if it is in server mode.

 using System; using System.Collections; using System.Threading; namespace SMTPChannel {    public class POP3PollManager    {       // dictionary of polling instances       static IDictionary _listeners = Hashtable.Synchronized(new Hashtable());       // number of sent messages for which no response has been received       private static int _pendingResponses;       private static int _lastPendingResponses; public static void RegisterPolling(String hostname, String username,    String password, int pollInterval, bool isServer) {    String key = username + "|" + hostname;    // check if this combination has already been registered    POP3Polling pop3 = (POP3Polling) _listeners[key];    if (pop3 == null)    {       // create a new listener       pop3 = new POP3Polling(hostname,username,password,          pollInterval,isServer);       _listeners[key]= pop3;    }    else    {       // change to server-mode if needed       if (!pop3._isServer && isServer)       {          pop3._isServer = true;       }       // check for pollInterval => lowest interval will be taken       if (! (pop3._pollInterval > pollInterval))       {          pop3._pollInterval = pollInterval;       }    }    pop3.CheckAndStartPolling(); } 

Two other methods that are provided in this class have to be called to notify all registered POP3Polling objects that the remoting framework is waiting for areply or that it has received a reply:

 internal static void RequestSent() {    _pendingResponses++;    if (_lastPendingResponses<=0 && _pendingResponses > 0)    {       IEnumerator enmr = _listeners.GetEnumerator();       while (enmr.MoveNext())       {          DictionaryEntry entr = (DictionaryEntry) enmr.Current;          POP3Polling pop3 = (POP3Polling) entr.Value;          pop3._needsPolling = true;          pop3.CheckAndStartPolling();       }    }    _lastPendingResponses = _pendingResponses; } internal static void ResponseReceived() {    _pendingResponses--;    if (_pendingResponses <=0)    {       IEnumerator enmr = _listeners.GetEnumerator();       while (enmr.MoveNext())       {          DictionaryEntry entr = (DictionaryEntry) enmr.Current;          POP3Polling pop3 = (POP3Polling) entr.Value;          pop3._needsPolling = false;       }    }    _lastPendingResponses = _pendingResponses; } 

Connecting to .NET Remoting

What you've seen until now has been quite protocol specific, because I haven't yet covered any connections between the underlying protocol to .NET Remoting. This task is handled by the SMTPHelper class. This class holds three synchronized Hashtables containing the following data:

  • Objects that are waiting for a response to a given SMTP message id. These can be either Thread objects or SMTPChannel.AsyncResponseHandler objects, both of which are shown later. These are stored in _waitingFor.

  • The server-side transport sink for any e-mail address that has been registered with a SMTPServerChannel in _servers.

  • The received responses that will be cached while waking up the thread that has been blocked is stored in _responses. This is a short-term storage that is only used for the fractions of a second it takes for the thread to wake up and fetch and remove the response.

This class also starts with a method that transforms the .NET Remotingspecific information in the form of a stream and an ITransportHeaders object into an e-mail message. This method writes the standard e-mail headers, adds the remoting-specific headers from the ITransportHeaders object, encodes the stream's contents to Base64, and ensures a maximum line length of 73 characters. Finally, it sends the e-mail using the SMTPConnection helper class:

 using System; using System.Text; using System.IO; using System.Collections; using System.Runtime.Remoting.Channels; using System.Threading; namespace SMTPChannel {    public class SMTPHelper    {       // threads waiting for response       private static IDictionary _waitingFor =          Hashtable.Synchronized(new Hashtable());       // known servers       private static IDictionary _servers =          Hashtable.Synchronized(new Hashtable());       // responses received       private static IDictionary _responses =          Hashtable.Synchronized(new Hashtable());       // sending messages       private static void SendMessage(String ID,String replyToId,          String mailfrom, String mailto, String smtpServer,          ITransportHeaders headers,    Stream stream, String objectURI)       {          StringBuilder msg = new StringBuilder();       if (ID != null)       {          msg.Append("Message-Id: ").Append(ID).Append("\r\n");       }       if (replyToId != null)       {          msg.Append("In-Reply-To: ").Append(replyToId).Append("\r\n");       }       msg.Append("From: ").Append(mailfrom).Append("\r\n");       msg.Append("To: ").Append(mailto).Append("\r\n");       msg.Append("MIME-Version: 1.0").Append("\r\n");       msg.Append("Content-Type: text/xml; charset=utf-8").Append("\r\n");       msg.Append("Content-Transfer-Encoding: BASE64").Append("\r\n");       // write the remoting headers       IEnumerator headerenum = headers.GetEnumerator();       while (headerenum.MoveNext())       {          DictionaryEntry entry = (DictionaryEntry) headerenum.Current;          String key = entry.Key as String;          if (key == null || key.StartsWith("__"))          {             continue;          }          msg.Append("X-REMOTING-").Append(key).Append(": ");          msg.Append(entry.Value.ToString()).Append("\r\n");       }       if (objectURI != null)       {          msg.Append("X-REMOTING-URI: ").Append(objectURI).Append("\r\n");       }       msg.Append("\r\n");       MemoryStream fs = new MemoryStream();       byte[] buf = new Byte[1000];       int cnt = stream.Read(buf,0,1000);       int bytecount = 0;       while (cnt>0)       {          fs.Write(buf,0,cnt);          bytecount+=cnt;       cnt = stream.Read(buf,0,1000);    }    // convert the whole string to Base64 encoding    String body = Convert.ToBase64String(fs.GetBuffer(),0,bytecount);    // and ensure the maximum line length of 73 characters    int linesNeeded = (int) Math.Ceiling(body.Length / 73);    for (int i = 0;i<=linesNeeded;i++)    {       if (i != linesNeeded)       {          String line = body.Substring(i*73,73);          msg.Append(line).Append("\r\n");       }       else       {          String line = body.Substring(i*73);          msg.Append(line).Append("\r\n");       }    }    // send the resulting message    SMTPConnection con = new SMTPConnection (smtpServer);    con.SendMessage(mailfrom,mailto,msg.ToString()); } 

This method is not called directly by the framework, but instead the class provides two other methods that are made for this purpose. The first one, named SendRequestMessage(), generates a message id that is later returned using an out parameter and then calls SendMessage() to send the e-mail via SMTP. It next calls the POP3PollManager's RequestSent() method so that the background thread will start checking for incoming reply mails.

The second method, SendReplyMessage(), takes a given message id and sends areply:

 internal static void SendRequestMessage(String mailfrom, String mailto,    String smtpServer, ITransportHeaders headers, Stream request,    String objectURI, out String ID) {    ID = "<" + Guid.NewGuid().ToString().Replace("-","") + "@REMOTING>";    SendMessage(ID,null,mailfrom,mailto,smtpServer,headers,request,objectURI);    POP3PollManager.RequestSent(); } internal static void SendResponseMessage(String mailfrom, String mailto,    String smtpServer, ITransportHeaders headers, Stream response,    String ID) {    SendMessage(null,ID,mailfrom,mailto,smtpServer,headers,response,null); } 

The more complex part of mapping the underlying protocol to the .NET Remoting Framework is the handling of responses. As the combination of SMTP and POP3 is asynchronous in its nature, whereas most .NET calls are executed synchronously, you have to provide a means for blocking the underlying thread until the matching response has been received. This is accomplished by the following method that adds the waiting thread to the _waitingFor Hashtable and suspends it afterwards. Whenever a response is received (which is handled by another function running in a different thread), the response is stored in the _responses Hashtable and the thread awakened again. It then fetches the value from _responses and return it to the caller:

 internal static POP3Msg WaitAndGetResponseMessage(String ID) {    // suspend the thread until the message returns    _waitingFor[ID] = Thread.CurrentThread;    Thread.CurrentThread.Suspend();    // waiting for resume    POP3Msg pop3msg = (POP3Msg) _responses[ID];    _responses.Remove(ID);    return pop3msg; } 

The previous method showed you the handling of synchronous .NET Remoting messages, but you might also want to support asynchronous delegates. In this case, a callback object is placed in _waitingFor and the method that handles incoming POP3 messages simply invokes the corresponding method on this callback object.

 internal static void RegisterAsyncResponseHandler(String ID,    AsyncResponseHandler ar) {    _waitingFor[ID] = ar; } 

One of the most important methods is MessageReceived(), which is called by POP3Polling as soon as an incoming message has been collected from the server. This method attempts to map all incoming e-mails to .NET Remoting calls.

There are two quite different types in e-mails: requests that are sent from a client to a server and reply messages that are sent back from the server. The distinction between these is that a reply e-mail includes an In-Reply-To header, whereas the request message only contains a Message-Id header.

If the incoming message is a request message, the MessageReceived() method checks the _servers Hashtable to retrieve the server-side sink chain for the destination e-mail address. It will then call HandleIncomingMessage() on this SMTPServerTransportSink object.

For reply messages, on the other hand, the method checks whether any objects are waiting for an answer to the contained In-Reply-To header. If the waiting object is a thread, the POP3Msg object will be stored in the _responses Hashtable and the thread will be awakened. For asynchronous calls, the waiting object will be of type AsyncResponseHandler. In this case the framework will simply call its HandleAsyncResponsePop3Msg() method. Regardless of whether the reply has been received for a synchronous or an asynchronous message, POP3PollManager.ResponseReceived() will be called to decrement the count of unanswered messages and to eventually stop polling if all replies have been received.

 internal static void MessageReceived(POP3Msg pop3msg) {    // whenever a pop3 message has been received, it    // will be forwarded to this method    // check if it's a request or a reply    if ((pop3msg.InReplyTo == null) && (pop3msg.MessageId != null))    {       // it's a request       String requestID = pop3msg.MessageId;       // Request received       // check for a registered server       SMTPServerTransportSink snk = (SMTPServerTransportSink)          _servers[GetCleanAddress(pop3msg.To)];       if (snk==null)       {          // No server side sink found for address          return;       }       // Dispatch the message to serversink       snk.HandleIncomingMessage(pop3msg);    }    else if (pop3msg.InReplyTo != null)    {       // a response must contain the in-reply-to header       String responseID = pop3msg.InReplyTo.Trim();       // check who's waiting for it       Object notify = _waitingFor[responseID];       if (notify as Thread != null)       {          _responses[responseID] = pop3msg;          // Waiting thread found. Will wake it up          _waitingFor.Remove(responseID );          ((Thread) notify).Resume();          POP3PollManager.ResponseReceived();       }       else if (notify as AsyncResponseHandler != null)       {          _waitingFor.Remove(responseID);          POP3PollManager.ResponseReceived();          ((AsyncResponseHandler)notify).HandleAsyncResponsePop3Msg(             pop3msg);       }       else       {          // No one is waiting for this reply. Ignore.       }    } } 

Another method is employed to map a POP3Msg object onto the objects that .NET Remoting needs: Stream and ITransportHeader. ProcessMessage() does this by taking all custom remoting headers starting with X-REMOTING-from the e-mail and putting them into a new ITransportHeader object. It then converts back the Base64-encoded payload into a byte array and creates a new MemoryStream that allows the remoting framework to read the byte array. It also returns the message id as an out parameter.

 internal static void ProcessMessage(POP3Msg pop3msg,    out ITransportHeaders headers, out Stream stream, out String ID) {    // this method will split it into a TransportHeaders and    // a Stream object and will return the "remoting ID"    headers = new TransportHeaders();    // first all remoting headers (which start with "X-REMOTING-")    // will be extracted and stored in the TransportHeaders object    String tmp = pop3msg.Headers;    int pos = tmp.IndexOf("\nX-REMOTING-");    while (pos >= 0)    {       int pos2 = tmp.IndexOf("\n",pos+1);       String oneline = tmp.Substring(pos+1,pos2-pos-1);       int poscolon = oneline.IndexOf(":");       String key = oneline.Substring(11,poscolon-11).Trim();       String headervalue = oneline.Substring(poscolon+1).Trim();       if (key.ToUpper() != "URI")       {          headers[key] = headervalue;       }       else       {          headers["__RequestUri"] = headervalue;       }       pos = tmp.IndexOf("\nX-REMOTING-",pos2);    }    String fulltext = pop3msg.Body ;    fulltext = fulltext.Trim();    byte[] buffer = Convert.FromBase64String(fulltext);    stream=new MemoryStream(buffer);    ID = pop3msg.MessageId; } 

There's also a method called RegisterServer() in SMTPHelper that stores a server-side sink chain in the _servers Hashtable using the e-mail address as a key:

 public static void RegisterServer(SMTPServerTransportSink snk,    String address) {    // Registering sink for a specified e-mail address    _servers[address] = snk; } 

The last two methods in the helper class are of a more generic nature. The first parses a URL in the form smtp:someone@somedomain.com/URL/to/object and returns the e-mail address separated from the object's URI (which is /URL/to/object in this case) as out parameters:

 internal static void parseURL(String url, out String email,    out String objectURI) {    // format: "smtp:user@host.domain/URL/to/object"    // is splitted to:    //      email = user@host.domain    //      objectURI = /URL/to/object    int pos = url.IndexOf("/");    if (pos > 0)    {       email = url.Substring(5,pos-5);       objectURI = url.Substring(pos);    }    else if (pos ==-1)    {       email = url.Substring(5);       objectURI ="";    }    else    {       email = null;       objectURI = url;    } } 

The second method is used to parse an e-mail address for an incoming request. It accepts addresses in a variety of formats and returns the generic form of user@domain.com:

 public static String GetCleanAddress(String address) {    // changes any kind of address like "someone@host"    // "<someone@host>" "<someone@host> someone@host"    // to a generic format of "someone@host"    address = address.Trim();    int posAt = address.IndexOf("@");    int posSpaceAfter = address.IndexOf(" ",posAt);    if (posSpaceAfter != -1) address = address.Substring(0,posSpaceAfter);    int posSpaceBefore = address.LastIndexOf(" ");    if (posSpaceBefore != -1 && posSpaceBefore < posAt)    {       address = address.Substring(posSpaceBefore+1);    }    int posLt = address.IndexOf("<");    if (posLt != -1)    {       address = address.Substring(posLt+1);    }    int posGt = address.IndexOf(">");    if (posGt != -1)    {       address = address.Substring(0,posGt);    }    return address; } 

Advanced  .NET Remoting C# Edition
Advanced .NET Remoting (C# Edition)
ISBN: 1590590252
EAN: 2147483647
Year: 2002
Pages: 91
Authors: Ingo Rammer

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net