Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
root = true

[*]
charset = utf-8
end_of_line = crlf
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.cs]
# Indentation
indent_size = 4
tab_width = 4

# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true

# Spacing
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false

# Wrapping
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true

# Using directives
csharp_using_directive_placement = outside_namespace:suggestion
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false

# this. qualification
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion

# Language keywords vs BCL types
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion

# var preferences
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = false:suggestion

# Expression-bodied members
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_properties = true:silent

# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion

# Null checking
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion

# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion

# Code style
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion

[*.{csproj,props,targets}]
indent_size = 2

[*.{json,yml,yaml}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
4 changes: 2 additions & 2 deletions src/TailoredApps.Shared.DateTime/DateTimeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace TailoredApps.Shared.DateTime
namespace TailoredApps.Shared.DateTime
{
/// <summary>
/// Simple implementation used for <see cref="IDateTimeProvider"/>
Expand Down Expand Up @@ -35,4 +35,4 @@ public class DateTimeProvider : IDateTimeProvider
/// </summary>
public System.DateTime UtcToday => System.DateTime.UtcNow.Date;
}
}
}
4 changes: 2 additions & 2 deletions src/TailoredApps.Shared.DateTime/IDateTimeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace TailoredApps.Shared.DateTime
namespace TailoredApps.Shared.DateTime
{
/// <summary>
/// Simple interface for mocking <see cref="System.DateTime"/> used for unit testing and configuration in mocks time used in tests.
Expand Down Expand Up @@ -30,4 +30,4 @@ public interface IDateTimeProvider
/// </summary>
System.DateTime UtcToday { get; }
}
}
}
66 changes: 33 additions & 33 deletions src/TailoredApps.Shared.Email.Models/MailMessage.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
using System;
using System.Collections.Generic;

namespace TailoredApps.Shared.Email.Models
{
/// <summary>Model wiadomości e-mail.</summary>
public class MailMessage
{
/// <summary>Temat wiadomości.</summary>
public string Topic { get; set; }

/// <summary>Nadawca wiadomości.</summary>
public string Sender { get; set; }

/// <summary>Odbiorca wiadomości.</summary>
public string Recipent { get; set; }

/// <summary>Kopia CC wiadomości.</summary>
public string Copy { get; set; }

/// <summary>Treść wiadomości (plain text).</summary>
public string Body { get; set; }

/// <summary>Treść wiadomości (HTML).</summary>
public string HtmlBody { get; set; }

/// <summary>Załączniki: nazwa pliku → zawartość Base64.</summary>
public Dictionary<string, string> Attachements { get; set; }

/// <summary>Data wysłania wiadomości.</summary>
public DateTimeOffset Date { get; set; }
}
}
using System;
using System.Collections.Generic;
namespace TailoredApps.Shared.Email.Models
{
/// <summary>Represents an e-mail message.</summary>
public class MailMessage
{
/// <summary>The subject of the message.</summary>
public string Topic { get; set; }
/// <summary>The sender of the message.</summary>
public string Sender { get; set; }
/// <summary>The recipient of the message.</summary>
public string Recipent { get; set; }
/// <summary>The CC (carbon copy) recipient of the message.</summary>
public string Copy { get; set; }
/// <summary>The plain-text body of the message.</summary>
public string Body { get; set; }
/// <summary>The HTML body of the message.</summary>
public string HtmlBody { get; set; }
/// <summary>Attachments as a dictionary mapping file name to Base64-encoded content.</summary>
public Dictionary<string, string> Attachements { get; set; }
/// <summary>The date and time the message was sent.</summary>
public DateTimeOffset Date { get; set; }
}
}
13 changes: 7 additions & 6 deletions src/TailoredApps.Shared.Email.Office365/AuthenticationConfig.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.Identity.Web;
using System;
using System.Globalization;
using Microsoft.Identity.Web;

namespace TailoredApps.Shared.Email.Office365
{
Expand All @@ -12,12 +12,13 @@ namespace TailoredApps.Shared.Email.Office365
/// </summary>
public class AuthenticationConfig
{
/// <summary>The configuration key used to bind this section from application settings.</summary>
public static string ConfigurationKey => "Mail:Providers:Office365";
/// <summary>
/// instance of Azure AD, for example public Azure or a Sovereign cloud (Azure China, Germany, US government, etc ...)
/// </summary>
public string Instance { get; set; } = "https://login.microsoftonline.com/{0}";

/// <summary>
/// Graph API endpoint, could be public Azure (default) or a Sovereign cloud (US government, etc ...)
/// </summary>
Expand All @@ -35,13 +36,13 @@ public class AuthenticationConfig
/// Guid used by the application to uniquely identify itself to Azure AD
/// </summary>
public string ClientId { get; set; }

/// <summary>
/// MailBox
/// </summary>
public string MailBox { get; set; }



/// <summary>
/// URL of the authority
Expand Down Expand Up @@ -74,7 +75,7 @@ public string Authority
/// <remarks>
public CertificateDescription Certificate { get; set; }


}


Expand Down
56 changes: 49 additions & 7 deletions src/TailoredApps.Shared.Email.Office365/Office365EmailProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
using MailKit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Threading.Tasks;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;
Expand All @@ -8,16 +14,14 @@
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using MimeKit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Threading.Tasks;
using TailoredApps.Shared.Email.Models;

namespace TailoredApps.Shared.Email.Office365
{
/// <summary>
/// Email provider implementation for Office 365 using IMAP with OAuth2 (client-credentials flow).
/// Authenticates against Azure AD as a confidential client application.
/// </summary>
public class Office365EmailProvider : IEmailProvider
{
private readonly IOptions<AuthenticationConfig> options;
Expand All @@ -26,6 +30,11 @@ public class Office365EmailProvider : IEmailProvider

};
private readonly IConfidentialClientApplication confidentialClientApplication;
/// <summary>
/// Initializes a new instance of <see cref="Office365EmailProvider"/> and builds
/// the confidential client application using either a client secret or a certificate.
/// </summary>
/// <param name="options">The Office 365 authentication configuration options.</param>
public Office365EmailProvider(IOptions<AuthenticationConfig> options)
{
this.options = options;
Expand Down Expand Up @@ -85,6 +94,18 @@ private static bool IsAppUsingClientSecret(AuthenticationConfig config)
else
throw new Exception("You must choose between using client secret or certificate. Please update appsettings.json file.");
}
/// <summary>
/// Retrieves e-mail messages from the configured Office 365 mailbox using IMAP with OAuth2.
/// </summary>
/// <param name="folderName">
/// The name of the IMAP sub-folder to search in. Defaults to the inbox when empty.
/// </param>
/// <param name="sender">Optional filter: only messages whose From address contains this value are returned.</param>
/// <param name="recipent">Optional filter: only messages whose To address contains this value are returned.</param>
/// <param name="fromLast">
/// Optional time-span filter: only messages delivered within the last <paramref name="fromLast"/> are returned.
/// </param>
/// <returns>A collection of <see cref="Models.MailMessage"/> objects matching the specified criteria.</returns>
public async Task<ICollection<Models.MailMessage>> GetMail(string folderName = "", string sender = "", string recipent = "", TimeSpan? fromLast = null)
{
var response = new List<Models.MailMessage>();
Expand Down Expand Up @@ -163,13 +184,24 @@ private Dictionary<string, string> GetAttachements(IEnumerable<MimeEntity> attac
return result;
}

/// <summary>Sends an e-mail message via Office 365.</summary>
/// <param name="recipnet">The recipient e-mail address.</param>
/// <param name="topic">The subject of the message.</param>
/// <param name="messageBody">The body content of the message.</param>
/// <param name="attachments">A dictionary of attachment file names mapped to their byte content.</param>
/// <returns>A string result or identifier for the sent message.</returns>
public async Task<string> SendMail(string recipnet, string topic, string messageBody, Dictionary<string, byte[]> attachments)
{
throw new NotImplementedException();
}
}
/// <summary>Extension methods for registering the Office 365 email provider with the DI container.</summary>
public static class Office365EmailProviderExtensions
{
/// <summary>
/// Registers <see cref="Office365EmailProvider"/> and its configuration options with the service collection.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
public static void RegisterOffice365Provider(this IServiceCollection services)
{
services.AddOptions<AuthenticationConfig>();
Expand All @@ -178,14 +210,24 @@ public static void RegisterOffice365Provider(this IServiceCollection services)
}
}

/// <summary>
/// Configures <see cref="AuthenticationConfig"/> options by binding values from the application configuration.
/// </summary>
public class Office365EmailConfigureOptions : IConfigureOptions<AuthenticationConfig>
{
private readonly IConfiguration configuration;

/// <summary>
/// Initializes a new instance of <see cref="Office365EmailConfigureOptions"/>.
/// </summary>
/// <param name="configuration">The application configuration used to read Office 365 settings.</param>
public Office365EmailConfigureOptions(IConfiguration configuration)
{
this.configuration = configuration;
}

/// <summary>Populates <paramref name="options"/> with values from the application configuration section.</summary>
/// <param name="options">The <see cref="AuthenticationConfig"/> instance to configure.</param>
public void Configure(AuthenticationConfig options)
{
var section = configuration.GetSection(AuthenticationConfig.ConfigurationKey).Get<AuthenticationConfig>();
Expand Down
Loading
Loading