- Worth: 10%
- 📅 Due: February 21, 2024 @ 23:59.
- 🕑 Late Submissions: Deductions for late submissions is 10%/day. To a maximum of 3 days. A a grade of 0% will be given after 3 days.
- 📥Submission: Submit through GitHub classroom.
- ✅ The use of generative AI is allowed to help improve your solution. But it should not write all your assignment.
In this first assignment, you are tasked with creating a simple Email Client Console App with essential email functionalities. This is the first milestone, where you'll create a service class that interfaces with a mail clients to retrieve and send emails asynchronously.
The Email app must:
-
Authenticate the user
-
Connect to a mail client
-
Retrieve all emails in the inbox.
-
Send an email with a body, title, sender and sender email
-
Delete a given email.
You do NOT have to implement:
- Detect when an email is sent (no event handlers for now)
- Detect when a new email is received (no monitoring the server for now)
- User interface (this will be done in the next assignment)
MailKit
MailKit developed by Jeffrey Stedfast and is a cross-platform mail client library which uses MimeKit. It offers authentication functionality as well as emailing functionality using POP3 protocol, Imap and Smtp. It is relatively easy to use for emailing but should be adapted for the needs of your application.
This Nuget package offers a variety of clients for retrieving or sending emails. In this assignment we will be using two: theImapClient which uses the imap protocol to retrieve emails, and the SmtpClient which uses the smtp protocol to send emails. This package uses MimeMessage class to hold email information such as the recipients, title, subject, etc.
To configure the clients, the following values are needed and should be encapsulated in a MailConfig class.
- Create an interface
IMailConfigwith the public properties below. - Create a
MailConfigclass which implements the interface
Required for authentication
string EmailAddressstring Password
Retrieving service
string ReceiveHostSecureSocketOptions RecieveSocketOptionsint ReceivePort
Sending service
string SendHostint SendPortSecureSocketOptions SendSocketOptions
Optional properties for OAuth 2.0
-
stringOAuth2ClientId -
stringOAuth2ClientSecret -
publicstringOAuth2RefreshToken -
In the main program, create an instance of the config while setting the values based on the tested mailing host setup in the next section.
-
Create a dummy email address on outlook:
-
Click Create free personal account
-
Create a new email address
-
Create a new password that you can easily remember
-
Fill in the first name, last name
-
Generate a password for the App
-
Visit the Google Account settings.
-
Select the “Security” tab.
-
Under the “Signing in to Google” section, click on “2-Step Verification” and follow the prompts to enable it.
-
After enabling two-step verification, search for “App Passwords”. You may be asked to re-enter your password.
-
Create a new app, call it "EmailConsoleApp"
-
Copy the 16-character password that is generated into your code. This is the password you should use to test the email client instead of the Google password created earlier.
-
-
Based on Google's settings for IMAP and SMTP Protocols, here are the config values:
Config value Value ImapHost"imap.gmail.com" ImapSocketSslOnConnectImapPort993 SmptHost"smtp-mail.outlook.com" SmtpSocketStartTlsorSslOnConnectSmtpPort587 (TLS), 465(SSL) EmailAddressdummy email created for this Passworddummy password
In this part of the assignment, you must implement a class called EmailService which provide the basic functionalities described earlier: connecting and authenticating, sending, retrieving all emails, deleting an email. Here is the interface it must implement:
public interface IEmailService
{
// Connection and authentication
Task StartSendClientAsync();
Task DisconnectSendClientAsync();
Task StartRetreiveClientAsync();
Task DisconnectRetreiveClientAsync();
// Emailing functionality
Task SendMessageAsync(MimeMessage message);
Task<IEnumerable<MimeMessage>> DownloadAllEmailsAsync();
Task DeleteMessageAsync(int uid);
}Your implementation should be flexible enough to allow OAuth 2.0 to be integrated in future iterations of this project as well as using other protocols. Here are a few guidelines to help you with the implementation.
Most mailing services now require OAuth 2.0 to email access, which involves registering the app with a developer account. However, for simplicity and testing purposes, we will only use basic authentication with the generated password.
Your design should be flexible enough to allow OAuth 2.0 to be integrated in future iterations of this project.
Requirements:
-
Before implementing your class, review the following OAuth 2.0 examples:
-
You can authenticate the send and receive client using a similar code:
client.Connect(host, port, socket); client.Authenticate(email, generatedPassword);
-
Handle all exceptions and print error messages to the console.
-
You must do so asynchronously
-
Before connecting or authenticating, always verify the state of the
clientto prevent issues:IsConnected: Checks if the client is currently connected to the server.IsAuthenticated: Checks if the client is successfully authenticated.
-
When done using a client, it should be disconnected using similar code:
client.Disconnect(true);
Testing
- Create an instance of the class in the
Main() - Ensure that the connection to the clients are not throwing exceptions, but if they do, the exception should be handled.
A sending client must be connected and authenticated before sending emails. Sending clients all implement the MailKit.IMailTransport interface.
For reference, here’s a sample code snippet for sending an email using SMTP: 📌 Sending Messages with SMTP
Requirements:
- Implement the following methods to send emails of type
MimeMessage:SendMessageAsync()
- Your solution must handle exceptions and log any errors to the console.
Testing
Call the method in the Main() and send yourself an email.
A retrieving client must also be connected to the email provider's server and authenticated before using it. These clients all implement the MailKit.IMailStore interface.
For reference, here’s a sample code snippet for retrieving emails using IMAP: 📌 Retrieving Emails with IMAP
Instructions
-
Implement the following method to retrieve emails from the
Inboxfolder:DownloadAllEmailsAsync()
-
Your solution must handle exceptions and log any errors to the console.
-
Inbox: A defaultMailFolderthat exists on any serverGetMessageAsync(UniqueId)is the method to use to download an email.- This UniqueId is useful for deleting emails at a later point.
Testing
Call the method in the Main() and display the title, subject and date of the retrieved emails. Compare with the list of emails in the Gmail app.
As for downloading emails, the retrieve client should be connected and authenticated before deleting an email. To do so, the email is marked as "Deleted", but will not be deleted until the folder is expunged.
For reference, here’s a sample code snippet:
Instructions:
- Implement the following methods to delete an email from the
Inboxfolder:DeleteMessageAsync()
- You must open the Inbox in Read-Write mode first.
- The
Uidrepresents the value of the unique Id assigned by the Imap server when an email appears in a folder. - You don't need to worry about this for now, you can simply delete the message with Unique Id 1 to test it out and ensure you have a few emails in the mail box.
| Criteria | Explanation | Points |
|---|---|---|
| Functionality | The Console App provides all the basic emailing functionalities required in this assignment | 5 |
| Modularity | The service class is designed to be flexible and easily extended if needed. | 5 |
| Async | All service classes are implemented using asynchronous calls | 10 |
| Code Quality | Error handling and robustness | 10 |
| Coding Style | Use of comments. Use of naming conventions. Avoid the use of magic strings within the classes definition. |
5 |