Skip to content

Commit 1be70cc

Browse files
committed
Completes #274 -- Added InvokeProcess Rule.
1 parent 77a7747 commit 1be70cc

34 files changed

+1341
-1122
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Papercut
2+
//
3+
// Copyright © 2008 - 2012 Ken Robertson
4+
// Copyright © 2013 - 2024 Jaben Cargman
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
19+
namespace Papercut.Core.Infrastructure.Logging;
20+
21+
public static class LoggerExtensions
22+
{
23+
public static bool ErrorWithContext(this ILogger logger, Exception? exception, string message, params object?[] propertyValues)
24+
{
25+
if (logger == null) throw new ArgumentNullException(nameof(logger));
26+
27+
logger.Error(exception, message, propertyValues);
28+
29+
return true;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Papercut
2+
//
3+
// Copyright © 2008 - 2012 Ken Robertson
4+
// Copyright © 2013 - 2024 Jaben Cargman
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
19+
using System.Text.RegularExpressions;
20+
21+
using MimeKit;
22+
using Papercut.Common.Helper;
23+
using Papercut.Rules.Domain.Conditional;
24+
25+
namespace Papercut.Rules.App.Conditional;
26+
27+
public static class ConditionalRuleExtensions
28+
{
29+
public static bool IsConditionalForwardRuleMatch(this IConditionalRule rule, MimeMessage mimeMessage)
30+
{
31+
if (rule == null) throw new ArgumentNullException(nameof(rule));
32+
if (mimeMessage == null) throw new ArgumentNullException(nameof(mimeMessage));
33+
34+
if (rule.RegexHeaderMatch.IsSet())
35+
{
36+
var allHeaders = string.Join("\r\n", mimeMessage.Headers.Select(h => h.ToString()));
37+
38+
if (!IsMatch(rule.RegexHeaderMatch, allHeaders))
39+
return false;
40+
}
41+
42+
if (rule.RegexBodyMatch.IsSet())
43+
{
44+
var bodyText = string.Join("\r\n",
45+
mimeMessage.BodyParts.OfType<TextPart>().Where(s => !s.IsAttachment));
46+
47+
if (!IsMatch(rule.RegexBodyMatch, bodyText))
48+
return false;
49+
}
50+
51+
return true;
52+
}
53+
54+
private static bool IsMatch(string match, string searchText)
55+
{
56+
var regex = new Regex(match, RegexOptions.IgnoreCase);
57+
return regex.IsMatch(searchText);
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Papercut
2+
//
3+
// Copyright © 2008 - 2012 Ken Robertson
4+
// Copyright © 2013 - 2024 Jaben Cargman
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
19+
using Autofac;
20+
21+
using MimeKit;
22+
using Papercut.Core.Domain.Rules;
23+
using Papercut.Message;
24+
using Papercut.Rules.App.Relaying;
25+
using Papercut.Rules.Domain.Conditional.Forwarding;
26+
27+
namespace Papercut.Rules.App.Conditional.Forwarding;
28+
29+
public class ConditionalForwardRuleDispatch : BaseRelayRuleDispatch<ConditionalForwardRule>
30+
{
31+
public ConditionalForwardRuleDispatch(Lazy<MimeMessageLoader> mimeMessageLoader, ILogger logger)
32+
: base(mimeMessageLoader, logger)
33+
{
34+
}
35+
36+
protected override bool RuleMatches(ConditionalForwardRule rule, MimeMessage mimeMessage)
37+
{
38+
return rule.IsConditionalForwardRuleMatch(mimeMessage);
39+
}
40+
41+
#region Begin Static Container Registrations
42+
43+
/// <summary>
44+
/// Called dynamically from the RegisterStaticMethods() call in the container module.
45+
/// </summary>
46+
/// <param name="builder"></param>
47+
[UsedImplicitly]
48+
static void Register(ContainerBuilder builder)
49+
{
50+
if (builder == null) throw new ArgumentNullException(nameof(builder));
51+
52+
builder.RegisterType<ConditionalForwardRuleDispatch>()
53+
.As<IRuleDispatcher<ConditionalForwardRule>>().InstancePerDependency();
54+
}
55+
56+
#endregion
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Papercut
2+
//
3+
// Copyright © 2008 - 2012 Ken Robertson
4+
// Copyright © 2013 - 2024 Jaben Cargman
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
19+
using Autofac;
20+
21+
using MimeKit;
22+
using Papercut.Core.Domain.Message;
23+
using Papercut.Core.Domain.Rules;
24+
using Papercut.Message;
25+
using Papercut.Message.Helpers;
26+
using Papercut.Rules.App.Relaying;
27+
using Papercut.Rules.Domain.Conditional.Forwarding;
28+
29+
using Polly;
30+
31+
namespace Papercut.Rules.App.Conditional.Forwarding;
32+
33+
public class ConditionalForwardWithRetryRuleDispatch : IRuleDispatcher<ConditionalForwardWithRetryRule>
34+
{
35+
private readonly ILogger _logger;
36+
37+
private readonly Lazy<MimeMessageLoader> _mimeMessageLoader;
38+
39+
public ConditionalForwardWithRetryRuleDispatch(Lazy<MimeMessageLoader> mimeMessageLoader, ILogger logger)
40+
{
41+
_mimeMessageLoader = mimeMessageLoader;
42+
_logger = logger;
43+
}
44+
45+
public async Task DispatchAsync(ConditionalForwardWithRetryRule rule, MessageEntry messageEntry, CancellationToken token)
46+
{
47+
if (rule == null) throw new ArgumentNullException(nameof(rule));
48+
if (messageEntry == null) throw new ArgumentNullException(nameof(messageEntry));
49+
50+
var message = await _mimeMessageLoader.Value.GetClonedAsync(messageEntry, token);
51+
52+
if (!RuleMatches(rule, message))
53+
{
54+
return;
55+
}
56+
57+
rule.PopulateFromRule(message);
58+
59+
var polly = Policy
60+
.Handle<Exception>()
61+
.WaitAndRetryAsync(
62+
rule.RetryAttempts,
63+
(attempt) => TimeSpan.FromSeconds(rule.RetryAttemptDelaySeconds),
64+
(exception, span) =>
65+
{
66+
_logger.Error(
67+
exception,
68+
"Failed to send {@MessageEntry} after {RetryAttempts}",
69+
messageEntry,
70+
rule.RetryAttempts);
71+
});
72+
73+
async Task SendMessage()
74+
{
75+
using (var client = await rule.CreateConnectedSmtpClientAsync(token))
76+
{
77+
await client.SendAsync(message, token);
78+
await client.DisconnectAsync(true, token);
79+
}
80+
}
81+
82+
await polly.ExecuteAsync(async () => await SendMessage());
83+
}
84+
85+
protected virtual bool RuleMatches(ConditionalForwardWithRetryRule rule, MimeMessage mimeMessage)
86+
{
87+
return rule.IsConditionalForwardRuleMatch(mimeMessage);
88+
}
89+
90+
#region Begin Static Container Registrations
91+
92+
/// <summary>
93+
/// Called dynamically from the RegisterStaticMethods() call in the container module.
94+
/// </summary>
95+
/// <param name="builder"></param>
96+
[UsedImplicitly]
97+
static void Register(ContainerBuilder builder)
98+
{
99+
if (builder == null) throw new ArgumentNullException(nameof(builder));
100+
101+
builder.RegisterType<ConditionalForwardWithRetryRuleDispatch>()
102+
.As<IRuleDispatcher<ConditionalForwardWithRetryRule>>().AsSelf().InstancePerDependency();
103+
}
104+
105+
#endregion
106+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Papercut
2+
//
3+
// Copyright © 2008 - 2012 Ken Robertson
4+
// Copyright © 2013 - 2024 Jaben Cargman
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
19+
using Autofac;
20+
21+
using MimeKit;
22+
using Papercut.Core.Domain.Rules;
23+
using Papercut.Message;
24+
using Papercut.Rules.App.Relaying;
25+
using Papercut.Rules.Domain.Conditional.Relaying;
26+
27+
namespace Papercut.Rules.App.Conditional.Relaying;
28+
29+
public class ConditionalRelayRuleDispatch : BaseRelayRuleDispatch<ConditionalRelayRule>
30+
{
31+
public ConditionalRelayRuleDispatch(Lazy<MimeMessageLoader> mimeMessageLoader, ILogger logger)
32+
: base(mimeMessageLoader, logger)
33+
{
34+
}
35+
36+
protected override bool RuleMatches(ConditionalRelayRule rule, MimeMessage mimeMessage)
37+
{
38+
return rule.IsConditionalForwardRuleMatch(mimeMessage);
39+
}
40+
41+
#region Begin Static Container Registrations
42+
43+
/// <summary>
44+
/// Called dynamically from the RegisterStaticMethods() call in the container module.
45+
/// </summary>
46+
/// <param name="builder"></param>
47+
[UsedImplicitly]
48+
static void Register(ContainerBuilder builder)
49+
{
50+
if (builder == null) throw new ArgumentNullException(nameof(builder));
51+
52+
builder.RegisterType<ConditionalRelayRuleDispatch>()
53+
.As<IRuleDispatcher<ConditionalRelayRule>>().AsSelf().InstancePerDependency();
54+
}
55+
56+
#endregion
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Papercut
2+
//
3+
// Copyright © 2008 - 2012 Ken Robertson
4+
// Copyright © 2013 - 2024 Jaben Cargman
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
19+
using Autofac;
20+
using Papercut.Core.Domain.Rules;
21+
using Papercut.Message;
22+
using Papercut.Rules.App.Relaying;
23+
using Papercut.Rules.Domain.Forwarding;
24+
25+
namespace Papercut.Rules.App.Forwarding;
26+
27+
public class ForwardRuleDispatch : BaseRelayRuleDispatch<ForwardRule>
28+
{
29+
public ForwardRuleDispatch(Lazy<MimeMessageLoader> mimeMessageLoader, ILogger logger)
30+
: base(mimeMessageLoader, logger)
31+
{
32+
}
33+
34+
#region Begin Static Container Registrations
35+
36+
/// <summary>
37+
/// Called dynamically from the RegisterStaticMethods() call in the container module.
38+
/// </summary>
39+
/// <param name="builder"></param>
40+
[UsedImplicitly]
41+
static void Register(ContainerBuilder builder)
42+
{
43+
if (builder == null) throw new ArgumentNullException(nameof(builder));
44+
45+
builder.RegisterType<ForwardRuleDispatch>()
46+
.As<IRuleDispatcher<ForwardRule>>().AsSelf().InstancePerDependency();
47+
}
48+
49+
#endregion
50+
}

0 commit comments

Comments
 (0)