1
\$\begingroup\$

In my .net core console application, I am reading a mail inbox and saving excel attachments from a specific sender that has a specific subject. It is working as is expected, I wonder if there are any improvements to make it better. So I would be glad if you can share your comments.

Thanks in advance.

class Program
    {
        static void Main(string[] args)
        {
            using var client = new ImapClient();
            client.Connect("imap.gmail.com", 993, true);

            client.Authenticate("[email protected]", "12345");

            var inbox = client.Inbox;
            inbox.Open(FolderAccess.ReadWrite);

            
            var query = SearchQuery.FromContains("[email protected]")
                .And(SearchQuery.SubjectContains("101")).And(SearchQuery.NotSeen);

            foreach (var uid in inbox.Search(query))
            {
                var message = inbox.GetMessage(uid);

                foreach (var attachment in message.Attachments)
                {
                    if (attachment is MessagePart)
                    {
                        var fileName = attachment.ContentDisposition?.FileName;
                        var rfc822 = (MessagePart)attachment;

                        if (string.IsNullOrEmpty(fileName))
                            fileName = "attached-message.eml";

                        using var stream = File.Create(fileName);
                        rfc822.Message.WriteTo(stream);
                    }
                    else
                    {
                        var part = (MimePart)attachment;
                        var fileName = part.FileName;
                        var file = Path.GetFileNameWithoutExtension(fileName);
                        var newPath = fileName.Replace(file, file + "-"+ uid);
                        Console.WriteLine(newPath);
                        Console.WriteLine(uid);
                        using var stream = File.Create(newPath);
                        part.Content.DecodeTo(stream);
                    }

                    inbox.AddFlags(uid, MessageFlags.Seen, true);
                    
                }

 
            }
            client.Disconnect(true);
        }
    }
\$\endgroup\$
4
  • \$\begingroup\$ Do you want your code to be compacted? \$\endgroup\$
    – user251264
    Nov 14, 2021 at 21:11
  • \$\begingroup\$ may be more maintainable :) \$\endgroup\$
    – raysefo
    Nov 15, 2021 at 7:31
  • \$\begingroup\$ Which version of C# are you using? \$\endgroup\$ Nov 15, 2021 at 11:24
  • \$\begingroup\$ I am using VS 2019, latest version \$\endgroup\$
    – raysefo
    Nov 15, 2021 at 11:29

1 Answer 1

3
\$\begingroup\$

Maintainability can mean different things for different developers. Here I will use this website's definition:

Maintainable code is basically
the amount of time it takes a developer to make a change and  
the amount of risk that the change could break something.

 So from this perspective a not maintainable code means:

  • either there is lots of effort to make changes
  • or there is a high probability to break something

What sort of changes can you expect?

  • Be able to connect to other user's mailbox
  • Be able read not just inbox, but other folders as well
  • Be able to retrieve other messages not just the not seen ones

Any of the above changes requires multiple adjustments on your code. That means more effort and higher probability to break something.

Oneway to lower risk and minimize modification efforts is to

  • Group those things together which are semantically tight to each other
  • Have some and well-focused reusable parts/components
  • Use optional parameters with default values to make your code more flexible

  If you look at your code from 1000 feet high then you can see the following:

  • Connect to a mailbox
  • Select a folder
  • Issue a search query
  • Iterate through the messages
  • Handle attachments based on their type

So, your main should follow this same level of abstraction:

static void Main()
{
    using var client = ConnectToMailbox();
    var folder = SelectFolder(client);
 
    foreach (var messageId in IssueSearch(folder))
    foreach (var attachment in folder.GetMessage(messageId).Attachments)
    {
        _ = attachment switch
        {
            MessagePart messagePart => HandleMessagePart(messagePart),
            MimePart mimePart => HandleMimePart(mimePart, messageId),
            _ => throw new NotSupportedException("Attachment is unknown")
        };
 
        folder.AddFlags(messageId, MessageFlags.Seen, true);
    }
}

Each of the methods are well focused and have small responsibilities. So, any of the above changes are localized (there is a single and easy spot place to do adjustments).

Connect to a mailbox 


ImapClient ConnectToMailbox(
    string host = "imap.gmail.com", int port = 993, bool useSsl = true,
    string username = "[email protected]", string pwd = "12345")
{
    var client = new ImapClient();
    client.Connect(host, port, useSsl);
    client.Authenticate(username, pwd);
    return client;
}

Select a folder

IMailFolder SelectFolder(ImapClient client, SpecialFolder? folder = null)
{
    var inbox = folder.HasValue ? client.GetFolder(folder.Value): client.Inbox;
    inbox.Open(FolderAccess.ReadWrite);
    return inbox;
}

Issue a search query

IList<UniqueId> IssueSearch(IMailFolder inbox,
    string from = "[email protected]",
    string subject = "101",
    SearchQuery? filter = null)
{
    var query = SearchQuery.FromContains(from)
        .And(SearchQuery.SubjectContains(subject))
        .And(filter ?? SearchQuery.NotSeen);
    return inbox.Search(query);
}

Handle attachments based on their type

MimeEntity HandleMessagePart(MessagePart attachment)
{
    var fileName = attachment.ContentDisposition?.FileName;
 
    if (string.IsNullOrEmpty(fileName))
        fileName = "attached-message.eml";
 
    using var stream = File.Create(fileName);
    attachment.Message.WriteTo(stream);
    return attachment;
}

MimeEntity HandleMimePart(MimePart attachment, UniqueId uid)
{
    var fileName = attachment.FileName;
    var file = Path.GetFileNameWithoutExtension(fileName);
    var newPath = fileName.Replace(file, $"{file}-{uid}");
    Console.WriteLine(newPath);
    Console.WriteLine(uid);
 
    using var stream = File.Create(newPath);
    attachment.Content.DecodeTo(stream);
    return attachment;
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ The double foreach and _ for the switch hacks are nice haha :-] \$\endgroup\$
    – t3chb0t
    Dec 31, 2021 at 22:25
  • \$\begingroup\$ @t3chb0t I do agree it is not the nicest code that I've ever written, but at least it is concise :D \$\endgroup\$ Jan 3, 2022 at 13:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.