I am using C# and Microsoft .Net Compact Framework 1.0. I tried to implement the POP3 protocol using the System.Net.Sockets.NetworkStream
and TcpClient
classes. I am able to log in and receive email and save the attachments with some email servers. But for some, I keep running into problems.
I read the size of the email message by sending the List <Index>
command. In some situations I get a size significantly smaller than the actual value. For example, for the same EMail:
Actual size: 577860, Returned size: 421096 using Exchange 2007
Actual size: 561005, Returned size: 560997 using Exchange 2003
Why don't I ever get the right size? The following the code I am using.
The size of the email never matches the size of the StringBuilder
at the end of PopRead
procedure. I am not able to read the email reliably because the Email size is not reliable and the DataAvailable
property of the NetworkStream
is sometimes false, even if there is more data that can be read.
I observed that the DataAvailable
property is false more often when I am trying to connect to the email server over the air (using data-plan) than when using the internet connection of the computer through activesync.
If it helps, the email servers are Exchange 2003 and Exchange 2007.
private bool POPRead(StringBuilder strBuffer, long lngFetchMailSize)
{
const int bufferSize = 1024;
byte[] inb;
if (enc == null)
{
enc = new ASCIIEncoding();
}
try
{
if (lngFetchMailSize > 0 && lngFetchMailSize < (32 * bufferSize))
{
// limit the size of the buffer as the amount of memory
// on Pocket PC is limited.
inb = new byte[lngFetchMailSize];
}
else
{
inb = new byte[bufferSize];
}
Array.Clear(inb, 0, inb.Length);
bool bMoreData = true;
long iBytesRead = 0L;
int bytesReadInThisRound = 0;
int numberOfTimesZeroBytesRead = 0;
while (bMoreData)
{
bytesReadInThisRound = this.nsPOP.Read(inb, 0, inb.Length);
iBytesRead += bytesReadInThisRound;
if (bytesReadInThisRound == 0)
{
numberOfTimesZeroBytesRead++;
}
else
{//If on a retry the data read is not empty, reset the counter.
numberOfTimesZeroBytesRead = 0;
}
strBuffer.Append(enc.GetString(inb, 0, bytesReadInThisRound));
Array.Clear(inb, 0, bytesReadInThisRound);
// DataAvailable sometimes gives false even though there is
// more to be read.
bMoreData = this.nsPOP.DataAvailable;
// Use this number (5), since some servers sometimes give the size
// of the email bigger than the actual size.
if ((lngFetchMailSize != 0 && !bMoreData)
&& (iBytesRead < lngFetchMailSize)
&& numberOfTimesZeroBytesRead < 5)
{
bMoreData = true;
}
}
}
catch (Exception ex)
{
string errmessage = "Reading email Expected Size: " + lngFetchMailSize;
LogException.LogError(ex, errmessage, false, "oePPop.POPRead");
Error = ex.Message + " " + errmessage;
return false;
}
finally
{
GC.Collect();
}
return true;
}
The following procedure is used to get the size of the email message:
private long GetMailSize(int index)
{
StringBuilder strBuffer = new StringBuilder();
const string LISTError = "Unable to read server's reply for LIST command";
if ((this.POPServer != null) && (this.nsPOP != null))
{
if (!this.POPWrite("LIST " + index))
{
return -1L;
}
if (!this.POPRead(strBuffer))
{
this.Error = LISTError;
return -1L;
}
if (!this.IsOK(strBuffer))
{
return -1L;
}
string strReturned = strBuffer.ToString();
int pos1 = strReturned.IndexOf(" ", 3);
if (pos1 == -1)
{
this.Error = LISTError;
return -1L;
}
int pos2 = strReturned.IndexOf(" ", (int)(pos1 + 1));
if (pos2 == -1)
{
this.Error = LISTError;
return -1L;
}
int pos3 = strReturned.IndexOf("\r\n", (int)(pos2 + 1));
if (pos3 == -1)
{
this.Error = LISTError;
return -1L;
}
long mailSize = 0;
Int64.TryParse(strBuffer.ToString(pos2 + 1, pos3 - (pos2 + 1)).Trim(), out mailSize);
return mailSize;
}
this.Error = NotConnectedError;
return -1L;
}
Hope I gave all the information needed for solving the issue. Any help or pointer in the right direction would be a lot of help.
Thanks,
Chandra.