Repository: owncloud/ocis
Pull request #9468 (2024-06-25 15:55:33)

fixed the notification service error when the user's display name contained special characters
<!--
Thanks for submitting a change to ownCloud!

This is the bug tracker for the oCIS component. Find other components at https://github.com/owncloud/core/blob/master/.github/CONTRIBUTING.md#guidelines

For fixing potential security issues please see https://owncloud.org/security/

To make it possible for us to get your change reviewed and merged please carefully fill out the requested information below.

Please note that any kind of change needs first be submitted to the master branch which holds the next version of oCIS.

Please set the following labels:

- Set label "Status:Needs-Review" for review or "Status:In-Progress" if the PR still has open tasks.
- Assignment: assign to self
- Reviewers: pick at least one
-->

## Description
<!--- Describe your changes in detail -->
We fixed an error in the notification service that caused the email notification to fail when the user's display name contained special characters.


## Related Issue
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->
- Fixes [#9402](https://github.com/owncloud/ocis/issues/9402)

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->

## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
- test environment: local ocis + inbucket server

## Screenshots (if appropriate):

<img width="1240" alt="Screenshot 2024-06-25 at 17 30 47" src="https://github.com/owncloud/ocis/assets/11679989/3f1b85d3-ad9e-4fb9-b7f2-2f81e3fe08e0">
<img width="1227" alt="Screenshot 2024-06-25 at 17 32 45" src="https://github.com/owncloud/ocis/assets/11679989/eb220fbc-1967-4edd-b9c5-252a9eb6fce0">


## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Technical debt
- [ ] Tests only (no source changes)

## Checklist:
<!-- Tick the checkboxes when done. -->
<!-- Raise documentation ticket in owncloud.github.io/ -->
- [x] Code changes
- [x] Unit tests added
- [ ] Acceptance tests added
- [ ] Documentation ticket raised: <link> 


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [21.4% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [21.4% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [21.4% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [21.4% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [15.8% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [15.8% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [15.8% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=9468&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=9468&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=9468&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=9468)



Patch:
diff --git a/.drone.star b/.drone.star
index c8116afd13c..6560d132cdc 100644
--- a/.drone.star
+++ b/.drone.star
@@ -108,6 +108,7 @@ config = {
                 "EMAIL_PORT": "9000",
             },
             "extraServerEnvironment": {
+                "OCIS_ADD_RUN_SERVICES": "notifications",
                 "NOTIFICATIONS_SMTP_HOST": "email",
                 "NOTIFICATIONS_SMTP_PORT": "2500",
                 "NOTIFICATIONS_SMTP_INSECURE": "true",
diff --git a/changelog/unreleased/fix-email-notification.md b/changelog/unreleased/fix-email-notification.md
new file mode 100644
index 00000000000..0edc0a5924a
--- /dev/null
+++ b/changelog/unreleased/fix-email-notification.md
@@ -0,0 +1,6 @@
+Bugfix: Fix the email notification service
+
+We fixed an error in the notification service that caused the email notification to fail when the user's display name contained special characters.
+
+https://github.com/owncloud/ocis/pull/9467
+https://github.com/owncloud/ocis/issues/9402
diff --git a/deployments/examples/ocis_full/inbucket.yml b/deployments/examples/ocis_full/inbucket.yml
index 47e1d288985..495006edf2e 100644
--- a/deployments/examples/ocis_full/inbucket.yml
+++ b/deployments/examples/ocis_full/inbucket.yml
@@ -2,6 +2,8 @@
 services:
   ocis:
     environment:
+      # enable the notifications service
+      OCIS_ADD_RUN_SERVICES": "notifications"
       NOTIFICATIONS_SMTP_HOST: inbucket
       NOTIFICATIONS_SMTP_PORT: 2500
       NOTIFICATIONS_SMTP_SENDER: oCIS notifications <notifications@${OCIS_DOMAIN:-ocis.owncloud.test}>
diff --git a/ocis/pkg/runtime/service/service.go b/ocis/pkg/runtime/service/service.go
index a4f3faa2ff0..7c8ec280f5c 100644
--- a/ocis/pkg/runtime/service/service.go
+++ b/ocis/pkg/runtime/service/service.go
@@ -19,6 +19,7 @@ import (
 	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
 	"github.com/mohae/deepcopy"
 	"github.com/olekukonko/tablewriter"
+	notifications "github.com/owncloud/ocis/v2/services/notifications/pkg/command"
 	"github.com/thejerf/suture/v4"
 
 	ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
@@ -43,7 +44,6 @@ import (
 	idp "github.com/owncloud/ocis/v2/services/idp/pkg/command"
 	invitations "github.com/owncloud/ocis/v2/services/invitations/pkg/command"
 	nats "github.com/owncloud/ocis/v2/services/nats/pkg/command"
-	notifications "github.com/owncloud/ocis/v2/services/notifications/pkg/command"
 	ocdav "github.com/owncloud/ocis/v2/services/ocdav/pkg/command"
 	ocm "github.com/owncloud/ocis/v2/services/ocm/pkg/command"
 	ocs "github.com/owncloud/ocis/v2/services/ocs/pkg/command"
@@ -200,11 +200,6 @@ func NewService(options ...Option) (*Service, error) {
 		cfg.IDM.Commons = cfg.Commons
 		return idm.Execute(cfg.IDM)
 	})
-	reg(3, opts.Config.Notifications.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error {
-		cfg.Notifications.Context = ctx
-		cfg.Notifications.Commons = cfg.Commons
-		return notifications.Execute(cfg.Notifications)
-	})
 	reg(3, opts.Config.OCDav.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error {
 		cfg.OCDav.Context = ctx
 		cfg.OCDav.Commons = cfg.Commons
@@ -339,6 +334,11 @@ func NewService(options ...Option) (*Service, error) {
 		cfg.Invitations.Commons = cfg.Commons
 		return invitations.Execute(cfg.Invitations)
 	})
+	areg(opts.Config.Notifications.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error {
+		cfg.Notifications.Context = ctx
+		cfg.Notifications.Commons = cfg.Commons
+		return notifications.Execute(cfg.Notifications)
+	})
 
 	return s, nil
 }
diff --git a/services/notifications/pkg/channels/channels.go b/services/notifications/pkg/channels/channels.go
index d8d368a63ed..5ac537e3f4e 100644
--- a/services/notifications/pkg/channels/channels.go
+++ b/services/notifications/pkg/channels/channels.go
@@ -4,7 +4,7 @@ package channels
 import (
 	"context"
 	"crypto/tls"
-	"fmt"
+	stdmail "net/mail"
 	"strings"
 
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
@@ -31,16 +31,23 @@ type Message struct {
 
 // NewMailChannel instantiates a new mail communication channel.
 func NewMailChannel(cfg config.Config, logger log.Logger) (Channel, error) {
+	a, err := stdmail.ParseAddress(cfg.Notifications.SMTP.Sender)
+	if err != nil {
+		logger.Err(err).Msg("parsing error, the 'smtp_sender' must be a valid single RFC 5322 address.")
+		return nil, err
+	}
 	return Mail{
-		conf:   cfg,
-		logger: logger,
+		conf:        cfg,
+		smtpAddress: *a,
+		logger:      logger,
 	}, nil
 }
 
 // Mail is the communication channel for email.
 type Mail struct {
-	conf   config.Config
-	logger log.Logger
+	conf        config.Config
+	smtpAddress stdmail.Address
+	logger      log.Logger
 }
 
 func (m Mail) getMailClient() (*mail.SMTPClient, error) {
@@ -113,11 +120,7 @@ func (m Mail) SendMessage(ctx context.Context, message *Message) error {
 	}
 
 	email := mail.NewMSG()
-	if message.Sender != "" {
-		email.SetFrom(fmt.Sprintf("%s via %s", message.Sender, m.conf.Notifications.SMTP.Sender)).AddTo(message.Recipient...)
-	} else {
-		email.SetFrom(m.conf.Notifications.SMTP.Sender).AddTo(message.Recipient...)
-	}
+	email.SetFrom(appendSender(message.Sender, m.smtpAddress)).AddTo(message.Recipient...)
 	email.SetSubject(message.Subject)
 	email.SetBody(mail.TextPlain, message.TextBody)
 	if message.HTMLBody != "" {
@@ -129,3 +132,10 @@ func (m Mail) SendMessage(ctx context.Context, message *Message) error {
 
 	return email.Send(smtpClient)
 }
+
+func appendSender(sender string, a stdmail.Address) string {
+	if strings.TrimSpace(sender) != "" {
+		a.Name = strings.TrimSpace(sender + " via " + a.Name)
+	}
+	return a.String()
+}
diff --git a/services/notifications/pkg/config/parser/parse.go b/services/notifications/pkg/config/parser/parse.go
index 032d2a6b345..c1beebca03c 100644
--- a/services/notifications/pkg/config/parser/parse.go
+++ b/services/notifications/pkg/config/parser/parse.go
@@ -3,6 +3,8 @@ package parser
 import (
 	"errors"
 	"fmt"
+	"net/mail"
+	"strings"
 
 	ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
 	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
@@ -54,6 +56,17 @@ func Validate(cfg *config.Config) error {
 		}
 	}
 
+	{
+		if strings.TrimSpace(cfg.Notifications.SMTP.Sender) == "" {
+			return fmt.Errorf("the 'smtp_sender' must be a valid single RFC 5322 address. parsing error: the address cannot be empty")
+		}
+		if s, err := mail.ParseAddress(cfg.Notifications.SMTP.Sender); err == nil {
+			cfg.Notifications.SMTP.Sender = s.String()
+		} else {
+			return fmt.Errorf("the 'smtp_sender' must be a valid single RFC 5322 address. parsing error %w", err)
+		}
+	}
+
 	if cfg.ServiceAccount.ServiceAccountID == "" {
 		return shared.MissingServiceAccountID(cfg.Service.Name)
 	}


Test patch:
diff --git a/docs/ocis/development/testing.md b/docs/ocis/development/testing.md
index 3c10257a4d0..b252bba26f4 100644
--- a/docs/ocis/development/testing.md
+++ b/docs/ocis/development/testing.md
@@ -359,6 +359,7 @@ ocis/bin/ocis init --insecure true
 
 # run oCIS
 PROXY_ENABLE_BASIC_AUTH=true \
+OCIS_ADD_RUN_SERVICES=notifications \
 NOTIFICATIONS_SMTP_HOST=localhost \
 NOTIFICATIONS_SMTP_PORT=2500 \
 NOTIFICATIONS_SMTP_INSECURE=true \
diff --git a/services/notifications/pkg/channels/channels_test.go b/services/notifications/pkg/channels/channels_test.go
new file mode 100644
index 00000000000..5140859d980
--- /dev/null
+++ b/services/notifications/pkg/channels/channels_test.go
@@ -0,0 +1,64 @@
+package channels
+
+import (
+	"net/mail"
+	"testing"
+)
+
+func Test_appendSender(t *testing.T) {
+	type args struct {
+		sender string
+		a      mail.Address
+	}
+
+	a1, err := mail.ParseAddress("ownCloud <noreply@example.com>")
+	if err != nil {
+		t.Error(err)
+	}
+	a2, err := mail.ParseAddress("noreply@example.com")
+	if err != nil {
+		t.Error(err)
+	}
+
+	tests := []struct {
+		name   string
+		sender string
+		want1  string
+		want2  string
+	}{
+		{
+			name:   "empty sender",
+			sender: "",
+			want1:  `"ownCloud" <noreply@example.com>`,
+			want2:  `<noreply@example.com>`,
+		},
+		{
+			name:   "not empty sender",
+			sender: `Joe Q. Public`,
+			want1:  `"Joe Q. Public via ownCloud" <noreply@example.com>`,
+			want2:  `"Joe Q. Public via" <noreply@example.com>`,
+		},
+		{
+			name:   "sender whit comma and semicolon",
+			sender: `Joe, Q; Public:`,
+			want1:  `"Joe, Q; Public: via ownCloud" <noreply@example.com>`,
+			want2:  `"Joe, Q; Public: via" <noreply@example.com>`,
+		},
+		{
+			name:   "sender with quotes",
+			sender: `Joe Q. "Public"`,
+			want1:  `"Joe Q. \"Public\" via ownCloud" <noreply@example.com>`,
+			want2:  `"Joe Q. \"Public\" via" <noreply@example.com>`,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := appendSender(tt.sender, *a1); got != tt.want1 {
+				t.Errorf("appendSender() = %v, want %v", got, tt.want1)
+			}
+			if got := appendSender(tt.sender, *a2); got != tt.want2 {
+				t.Errorf("appendSender() = %v, want %v", got, tt.want2)
+			}
+		})
+	}
+}