Skip to content
Open
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
Binary file added MediaBrowser.Plugins.SmtpNotifications.dll
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Services;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Tasks;

namespace MediaBrowser.Plugins.SmtpNotifications.Api
{

[Route("/Notification/SMTP/Test/{UserID}", "POST", Summary = "Tests SMTP")]
public class TestNotification : IReturnVoid
public class TestNotification : IReturn<string>
{
[ApiMember(Name = "UserID", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string UserID { get; set; }
Expand All @@ -29,19 +32,21 @@ public ServerApiEndpoints(IUserManager userManager, ILogger logger, IEncryptionM
_encryption = encryption;
}

public void Post(TestNotification request)
public string Post(TestNotification request)
{

var task = new Notifier(_logger, _encryption).SendNotification(new UserNotification
{
Date = DateTime.UtcNow,
Description = "This is a test notification from Emby Server",
Level = Model.Notifications.NotificationLevel.Normal,
Name = "Emby: Test Notification",
Name = "Test Notification",
User = _userManager.GetUserById(request.UserID)

}, CancellationToken.None);

Task.WaitAll(task);

return ("{\"status\": \"success\"}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public PluginConfiguration()
public class SMTPOptions
{
public bool Enabled { get; set; }
public string EmailFromName { get; set; }
public string EmailFrom { get; set; }
public string EmailTo { get; set; }
public string Server { get; set; }
Expand All @@ -28,7 +29,7 @@ public class SMTPOptions
public string PwData { get; set; }
public string MediaBrowserUserId { get; set; }
public bool SSL { get; set; }

public bool ?IgnoreCertificateErrors { get; set; }
public SMTPOptions()
{
Port = 25;
Expand Down
47 changes: 36 additions & 11 deletions MediaBrowser.Plugins.SmtpNotifications/Configuration/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,23 @@
<input is="emby-checkbox" type="checkbox" id="chkEnableSMTP" />
<span>Enable notifications for this user</span>
</label>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtEmailFromName" name="txtEmailFromName" label="Email from Name:" />
<div class="fieldDescription">
The name component of the sender information for your notification emails.
</div>
</div>

<div class="inputContainer">
<input is="emby-input" type="text" id="txtEmailFrom" name="txtEmailFrom" required="required" label="Email from:" />
<div class="fieldDescription">
Email Address that messages will be sent from. Sending from a free email service such as gmail, hotmail, or yahoo is not recommended. The providers may block repeated outgoing, automated email.
Email Address that messages will be sent from (format email@address.here). Sending from a free email service such as gmail, hotmail, or yahoo is not recommended. The providers may block repeated outgoing, automated email.
</div>
</div>
<div class="inputContainer">
<input is="emby-input" type="text" id="txtEmailTo" name="txtEmailTo" required="required" label="Email to:" />
<div class="fieldDescription">
Email Address that messaged will be sent to.
Email Address that the message will be sent to.
</div>
</div>
<div class="inputContainer">
Expand Down Expand Up @@ -64,6 +71,12 @@
Password for your SMTP Server.
</div>
</div>

<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="chkIgnoreCertificateErrors" />
<span>Ignore SSL Certificate Errors</span>
</label>

<div>
<button is="emby-button" type="button" class="raised button-cancel block" id="testNotification">
<span>Test Notification</span>
Expand Down Expand Up @@ -95,8 +108,8 @@
return userId == c.MediaBrowserUserId;

})[0] || { Enabled: false };

page.querySelector('#chkEnableSMTP').checked = smtpConfig.Enabled;
$('#txtEmailFromName', page).val(smtpConfig.EmailFromName || '');
$('#txtEmailFrom', page).val(smtpConfig.EmailFrom || '');
$('#txtEmailTo', page).val(smtpConfig.EmailTo || '');
$('#txtServer', page).val(smtpConfig.Server || '');
Expand All @@ -105,6 +118,7 @@
page.querySelector('#chkEnableAuth').checked = smtpConfig.UseCredentials || false;
$('#txtUsername', page).val(smtpConfig.Username || '');
$('#txtPassword', page).val(smtpConfig.Password || '');
page.querySelector('#chkIgnoreCertificateErrors').checked = smtpConfig.IgnoreCertificateErrors;

Dashboard.hideLoadingMsg();
});
Expand All @@ -123,9 +137,11 @@

Dashboard.showLoadingMsg();

var onError = function () {
var onError = function (response) {
require(['alert'], function (alert) {
alert("There was an error sending the test email. Please check your email settings and try again.");
response.text().then(function(responsetext) {
alert("There was an error sending the test email. Please check your email settings and try again. <br/><br/>Error: " + responsetext);
});
});
};

Expand All @@ -142,13 +158,20 @@

ApiClient.ajax({
type: "POST",
url: ApiClient.getUrl("Notification/SMTP/Test/" + c.MediaBrowserUserId)

}).catch(onError);
dataType: "json",
url: ApiClient.getUrl("Notification/SMTP/Test/" + c.MediaBrowserUserId),
}).catch(onError).then(function(test_result) {

Dashboard.hideLoadingMsg();

if(test_result)
{
require(['alert'], function (alert) {
alert("Test Notification Email sent successfully");
});
}
})
});

Dashboard.hideLoadingMsg();

}, onError);
});

Expand All @@ -175,6 +198,7 @@

smtpConfig.MediaBrowserUserId = userId;
smtpConfig.Enabled = form.querySelector('#chkEnableSMTP').checked;
smtpConfig.EmailFromName = $('#txtEmailFromName', form).val();
smtpConfig.EmailFrom = $('#txtEmailFrom', form).val();
smtpConfig.EmailTo = $('#txtEmailTo', form).val();
smtpConfig.Server = $('#txtServer', form).val();
Expand All @@ -183,6 +207,7 @@
smtpConfig.SSL = form.querySelector('#chkEnableSSL').checked;
smtpConfig.Username = $('#txtUsername', form).val();
smtpConfig.Password = $('#txtPassword', form).val();
smtpConfig.IgnoreCertificateErrors = form.querySelector('#chkIgnoreCertificateErrors').checked;

ApiClient.updatePluginConfiguration(pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,36 @@

<PropertyGroup>
<TargetFrameworks>netstandard2.0;</TargetFrameworks>
<AssemblyVersion>3.1.6.0</AssemblyVersion>
<FileVersion>3.1.6.0</FileVersion>
<AssemblyVersion>4.0.0.4</AssemblyVersion>
<FileVersion>4.0.0.4</FileVersion>
<PackageReleaseNotes>Utilising MailKit and MimeKit - Anthony Musgrove (anthony.musgrove@newymods.com.au)</PackageReleaseNotes>
</PropertyGroup>

<ItemGroup>
<None Remove="Configuration\config.html" />
<None Remove="Dependencies\MailKit.dll" />
<None Remove="Dependencies\MimeKit.dll" />
<None Remove="thumb.jpg" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Configuration\config.html" />
<EmbeddedResource Include="Dependencies\MailKit.dll" />
<EmbeddedResource Include="Dependencies\MimeKit.dll" />
<EmbeddedResource Include="thumb.jpg" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="mediabrowser.server.core" Version="4.0.0.2" />
</ItemGroup>

<ItemGroup>
<Reference Include="MailKit">
<HintPath>Dependencies\MailKit.dll</HintPath>
</Reference>
<Reference Include="MimeKit">
<HintPath>Dependencies\MimeKit.dll</HintPath>
</Reference>
</ItemGroup>

</Project>
117 changes: 90 additions & 27 deletions MediaBrowser.Plugins.SmtpNotifications/Notifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,26 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Threading;
using System.Threading.Tasks;

/* NB:
*
* We don't recommend that you use the SmtpClient class for new development because SmtpClient doesn't support many
* modern protocols. Use MailKit or other libraries instead. For more information, see SmtpClient shouldn't be
* used on GitHub.
*
* Rewritten to utilise MailKit v2.7 & MimeKit v2.8
*
* Anthony Musgrove, 2nd June 2020
*/
using MailKit;
using MimeKit;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Data.SqlTypes;
using MediaBrowser.Controller.Session;

namespace MediaBrowser.Plugins.SmtpNotifications
{
public class Notifier : INotificationService
Expand All @@ -35,8 +51,16 @@ public bool IsEnabledForUser(User user)

private SMTPOptions GetOptions(User user)
{
return Plugin.Instance.Configuration.Options
.FirstOrDefault(i => string.Equals(i.MediaBrowserUserId, user.Id.ToString("N"), StringComparison.OrdinalIgnoreCase));
try
{
return Plugin.Instance.Configuration.Options
.FirstOrDefault(i => string.Equals(i.MediaBrowserUserId, user.Id.ToString("N"), StringComparison.OrdinalIgnoreCase));
}
catch (Exception ex)
{
_logger.Error("Error getting SMTP options for userId '{0}': {1}", user.Id, ex.Message);
return (null);
}
}

public string Name
Expand All @@ -48,45 +72,84 @@ public async Task SendNotification(UserNotification request, CancellationToken c
{
var options = GetOptions(request.User);

using (var mail = new MailMessage(options.EmailFrom, options.EmailTo)
var mail = new MimeMessage(); var bodyBuilder = new BodyBuilder();

try
{
Subject = "Emby: " + request.Name,
Body = string.Format("{0}\n\n{1}", request.Name, request.Description)
})
mail.From.Add(new MailboxAddress(options.EmailFromName, options.EmailFrom));
mail.To.Add(new MailboxAddress("", options.EmailTo));
}
catch (Exception ex)
{
using (var client = new SmtpClient
{
Host = options.Server,
Port = options.Port,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Timeout = 20000
})
{
if (options.SSL) client.EnableSsl = true;
_logger.Error("Error creating Mailbox Address objects for from:{0} or to:{1} (error: {2})", options.EmailFrom, options.EmailTo, ex.Message);

_logger.Info("Sending email {0} with subject {1}", options.EmailTo, mail.Subject);
/* If testing SMTP, push exception to API caller */
if (request.Name == "Test Notification")
throw ex;
}

if (options.UseCredentials)
{
var pw = string.IsNullOrWhiteSpace(options.Password) ? _encryption.DecryptString(options.PwData) : options.Password;
client.Credentials = new NetworkCredential(options.Username, pw);
}
mail.Subject = "Emby: " + request.Name;

bodyBuilder.HtmlBody = string.Format("{0}<br/><br/>{1}", request.Name, request.Description);
bodyBuilder.TextBody = string.Format("{0}\n\n{1}", request.Name, request.Description);

mail.Body = bodyBuilder.ToMessageBody();

var client = new MailKit.Net.Smtp.SmtpClient();

if (options.IgnoreCertificateErrors.GetValueOrDefault(false))
client.ServerCertificateValidationCallback = this.sslCertificateValidationCallback;

client.Timeout = 20000;

try
{
_logger.Info("Connecting to smtpserver at {0}:{1}", options.Server, options.Port);

await client.ConnectAsync(options.Server, options.Port, options.SSL).ConfigureAwait(false);

if (options.UseCredentials)
{
_logger.Info("Authenticating to smtpserver using {0}/<hidden>", options.Username);

var pw = string.IsNullOrWhiteSpace(options.Password) ? _encryption.DecryptString(options.PwData) : options.Password;

try
{
await client.SendMailAsync(mail).ConfigureAwait(false);

_logger.Info("Completed sending email {0} with subject {1}", options.EmailTo, mail.Subject);
await client.AuthenticateAsync(new NetworkCredential(options.Username, pw)).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Error("Error sending email: {0} ", ex);
_logger.Info("Failed to authenticate to SMTP server: {0}", ex.Message);

/* If testing SMTP, push exception to API caller */
if (request.Name == "Test Notification")
throw ex;
}
}

_logger.Info("Sending email {0} with subject {1}", options.EmailTo, mail.Subject);

await client.SendAsync(mail, default, null).ConfigureAwait(false);

_logger.Info("Completed sending email {0} with subject {1}", options.EmailTo, mail.Subject);
}
catch (Exception ex)
{
_logger.Error("Error sending email: {0} ", ex);

/* If testing SMTP, push exception to API caller */
if (request.Name == "Test Notification")
throw ex;
}
}


private bool sslCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return (true);
}

private bool IsValid(SMTPOptions options)
{
return !string.IsNullOrEmpty(options.EmailFrom) &&
Expand Down
Loading