0

I'm getting email with attachments by simple IMAP client:

class Program
{
    static StreamWriter sw = null;
    static TcpClient tcpc = null;
    static SslStream ssl = null;
    static string path;
    static StringBuilder sb = new StringBuilder();
    static byte[] dummy;
    string username = "user";
    string password = "pass";

    static void Main(string[] args)
    {
        try
        {
            path = Environment.CurrentDirectory + "\\emailresponse.txt";

            if (System.IO.File.Exists(path))
                System.IO.File.Delete(path);

            using (sw = new System.IO.StreamWriter(System.IO.File.Create(path)))
            using (tcpc = new System.Net.Sockets.TcpClient("imap.server.com", 993))
            using (ssl = new System.Net.Security.SslStream(tcpc.GetStream()))
            {
                ssl.AuthenticateAsClient("imap.server.com");
                receiveResponse("");                    
                receiveResponse("$ LOGIN " + username + " " + password + "\r\n");
                Console.WriteLine("enter the email number to fetch :");
                int number = int.Parse(Console.ReadLine());
                receiveResponse("$ FETCH " + number + " body[header]\r\n");
                receiveResponse("$ FETCH " + number + " body[text]\r\n");
                receiveResponse("$ LOGOUT\r\n");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    static void receiveResponse(string command)
    {
        try
        {
            if (command != "")
            {
                if (tcpc.Connected)
                {
                    dummy = Encoding.Default.GetBytes(command);
                    ssl.Write(dummy, 0, dummy.Length);
                }
                else
                {
                    throw new ApplicationException("TCP CONNECTION DISCONNECTED");
                }
            }
            ssl.Flush();
            byte[] bigBuffer = new byte[1024*18];
            int bites = ssl.Read(bigBuffer, 0, bigBuffer.Length);
            byte[] buffer = new byte[bites];
            Array.Copy(bigBuffer, 0, buffer, 0, bites);
            sb.Append(Encoding.Default.GetString(buffer));
            sw.WriteLine(sb.ToString());
            sb = new StringBuilder();
        }
        catch (Exception ex)
        {
            throw new ApplicationException(ex.ToString());
        }
    }
}

Everything is alright, but if email size is more than ~19kb, content is cropped regardless of buffer size. How to fix it?

Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
shmnff
  • 647
  • 2
  • 15
  • 31
  • Your code crops it to 1024*18 bytes. If you don't want to do that, stop doing it. – arnt Feb 06 '18 at 07:42
  • @arnt changed to 100*1024, same result – shmnff Feb 06 '18 at 07:45
  • here is a similar problems [link#1](https://social.msdn.microsoft.com/Forums/vstudio/en-US/4994c44b-4fe5-4086-afe0-1e4edf49e7ba/sslstream-read-size?forum=wcf) and [link#2](https://stackoverflow.com/questions/11159494/packet-fragmentation-when-sending-data-via-sslstream) – shmnff Feb 06 '18 at 12:41
  • Just found that the buffer size is 16384 bytes or less – shmnff Feb 06 '18 at 12:53
  • 2
    The classic way transmit or receive unlimited data using a limited buffer is to use a while(){...} loop. Try that. – arnt Feb 06 '18 at 13:11
  • @arnt I still can not find a suitable solution. Just info about `SSLStream._SslState.MaxDataSize` limitation – shmnff Feb 06 '18 at 13:19
  • while(stillHaveDataToRead && connectionIsStillOpen) { ....; message.Append(...buffer); ... } – arnt Feb 06 '18 at 16:01

2 Answers2

1

If you call Stream.Read(), it makes no guarantees that it will read the number of bytes you are asking for. It only makes the guarantee that it won't read more than the number of bytes you've requested and that it will, at a minimum, read 1 byte unless the stream has hit EOF (for network streams, if Stream.Read() returns 0, it means the socket has been disconnected).

As others have pointed out, you will need to write a loop which calls Stream.Read() until you have read the full response.

Be aware, however, that you cannot blindly write a loop that calls Stream.Read() over and over until Read() returns 0 because that will cause your code to hang. You will need to properly parse the response as you are reading it so that you can calculate how much data you actually need to read and to stop reading once you have read the full response.

In order to do that, you'll need to read rfc3501 and write a parser for it.

Since you won't easily be able to convert the byte[] data into a string until you have parsed the message contents (each text/* part in the message might be encoded in a different charset), you will need to write your parser such that it parses byte arrays instead of strings.

If that sounds like too much work for you, you may want to look into using a library such as MailKit.

Community
  • 1
  • 1
jstedfast
  • 35,744
  • 5
  • 97
  • 110
  • The minimum that needs to be parsed to read properly is newlines, and {n} literals (which is pretty much all python's imaplib does) – Max Feb 06 '18 at 23:02
  • MailKit actually does something similar, but in order to do that, you need to know how to properly buffer incremental reads and I figured that’d be too complicated to explain. – jstedfast Feb 06 '18 at 23:31
  • I cannot use external packages. It is the lack of my network. – shmnff Feb 07 '18 at 07:57
0

Solution is that I have to repeat request receiveResponse("$ FETCH " + number + " body[text]\r\n") till I get all data pieces. And problem was that I did loop inside receiveResponse instead of main block.

shmnff
  • 647
  • 2
  • 15
  • 31