diff --git a/apiclient/types/emailreceiver.go b/apiclient/types/emailreceiver.go index b709b298e..e503f7f51 100644 --- a/apiclient/types/emailreceiver.go +++ b/apiclient/types/emailreceiver.go @@ -10,7 +10,7 @@ type EmailReceiver struct { type EmailReceiverManifest struct { Name string `json:"name"` Description string `json:"description"` - User string `json:"user,omitempty"` + Alias string `json:"alias,omitempty"` Workflow string `json:"workflow"` AllowedSenders []string `json:"allowedSenders,omitempty"` } diff --git a/pkg/alias/get.go b/pkg/alias/get.go index 774fb48ec..ceb979c56 100644 --- a/pkg/alias/get.go +++ b/pkg/alias/get.go @@ -16,7 +16,7 @@ import ( kclient "sigs.k8s.io/controller-runtime/pkg/client" ) -func Get(ctx context.Context, c kclient.Client, obj v1.Aliasable, namespace string, name string) error { +func Get(ctx context.Context, c kclient.Client, obj v1.Aliasable, namespace, name string) error { var errLookup error if namespace == "" { gvk, err := c.GroupVersionKindFor(obj.(kclient.Object)) diff --git a/pkg/api/handlers/emailreceiver.go b/pkg/api/handlers/emailreceiver.go index 2762e1af4..f661e01b4 100644 --- a/pkg/api/handlers/emailreceiver.go +++ b/pkg/api/handlers/emailreceiver.go @@ -1,6 +1,8 @@ package handlers import ( + "fmt" + "github.com/obot-platform/obot/apiclient/types" "github.com/obot-platform/obot/pkg/alias" "github.com/obot-platform/obot/pkg/api" @@ -90,8 +92,12 @@ func convertEmailReceiver(emailReceiver v1.EmailReceiver, hostname string) *type EmailReceiverManifest: manifest, AddressAssigned: aliasAssigned, } - if hostname != "" && er.AddressAssigned != nil && *er.AddressAssigned { - er.EmailAddress = emailReceiver.Spec.User + "@" + hostname + if hostname != "" { + name := emailReceiver.Name + if er.AddressAssigned != nil && *er.AddressAssigned { + name = er.Alias + } + er.EmailAddress = fmt.Sprintf("%s@%s", name, hostname) } return er } diff --git a/pkg/api/handlers/tasks.go b/pkg/api/handlers/tasks.go index 21e8a5032..30439dfa6 100644 --- a/pkg/api/handlers/tasks.go +++ b/pkg/api/handlers/tasks.go @@ -410,7 +410,7 @@ func (t *TaskHandler) updateEmail(req api.Context, workflow *v1.Workflow, task t }, Spec: v1.EmailReceiverSpec{ EmailReceiverManifest: types.EmailReceiverManifest{ - User: workflow.Spec.Manifest.Alias, + Alias: workflow.Spec.Manifest.Alias, Workflow: workflow.Name, }, ThreadName: workflow.Spec.ThreadName, diff --git a/pkg/services/config.go b/pkg/services/config.go index 61239873c..999122eff 100644 --- a/pkg/services/config.go +++ b/pkg/services/config.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log/slog" + "net/url" "os" "path/filepath" "strings" @@ -186,6 +187,9 @@ func New(ctx context.Context, config Config) (*Services, error) { if config.UIHostname == "" { config.UIHostname = config.Hostname } + if config.EmailServerName == "" { + config.EmailServerName = config.UIHostname + } if strings.HasPrefix(config.Hostname, "localhost") || strings.HasPrefix(config.Hostname, "127.0.0.1") { config.Hostname = "http://" + config.Hostname @@ -195,6 +199,13 @@ func New(ctx context.Context, config Config) (*Services, error) { if !strings.HasPrefix(config.UIHostname, "http") { config.UIHostname = "https://" + config.UIHostname } + // Ensure that the email server name is just a hostname + u, err := url.Parse(config.EmailServerName) + if err != nil { + return nil, fmt.Errorf("invalid email server name: %w", err) + } + + config.EmailServerName = u.Hostname() c, err := newGPTScript(ctx, config.WorkspaceTool, config.DatasetsTool, config.ToolRegistry) if err != nil { diff --git a/pkg/smtp/smtp.go b/pkg/smtp/smtp.go index 9c2833d5a..fe978601a 100644 --- a/pkg/smtp/smtp.go +++ b/pkg/smtp/smtp.go @@ -88,13 +88,13 @@ func (s *Server) handler(_ net.Addr, from string, to []string, data []byte) erro continue } - ns, name, ok := strings.Cut(name, ".") - if !ok { - log.Infof("Skipping mail for %s: no namespace found", toAddr.Address) + name, ns, _ := strings.Cut(name, "+") + if ns == "" { + ns = system.DefaultNamespace } var emailReceiver v1.EmailReceiver - if err := alias.Get(s.ctx, s.c, &emailReceiver, ns, name); apierror.IsNotFound(err) { + if err = alias.Get(s.ctx, s.c, &emailReceiver, ns, name); apierror.IsNotFound(err) { log.Infof("Skipping mail for %s: no receiver found", toAddr.Address) continue } else if err != nil { @@ -106,15 +106,7 @@ func (s *Server) handler(_ net.Addr, from string, to []string, data []byte) erro continue } - if len(emailReceiver.Spec.AllowedSenders) > 0 { - for _, allowedSender := range emailReceiver.Spec.AllowedSenders { - if allowedSender == fromAddress.Address { - break - } - } - } - - if err := s.dispatchEmail(emailReceiver, body, message); err != nil { + if err = s.dispatchEmail(emailReceiver, body, message, from, to); err != nil { return fmt.Errorf("dispatch email: %w", err) } } @@ -158,6 +150,18 @@ func getBody(message *mail.Message) (string, error) { html = string(d) } } + } else if strings.HasPrefix(mediaType, "text/plain") || strings.HasPrefix(mediaType, "text/html") { + d, err := io.ReadAll(message.Body) + if err != nil { + return "", err + } + if message.Header.Get("Content-Transfer-Encoding") == "base64" { + d, err = base64.StdEncoding.DecodeString(string(d)) + if err != nil { + return "", err + } + } + html = string(d) } if html != "" { @@ -167,7 +171,7 @@ func getBody(message *mail.Message) (string, error) { return "", fmt.Errorf("failed to find text/plain body: %s", mediaType) } -func (s *Server) dispatchEmail(email v1.EmailReceiver, body string, message *mail.Message) error { +func (s *Server) dispatchEmail(email v1.EmailReceiver, body string, message *mail.Message, from, to string) error { var input struct { Type string `json:"type"` From string `json:"from"` @@ -177,8 +181,8 @@ func (s *Server) dispatchEmail(email v1.EmailReceiver, body string, message *mai } input.Type = "email" - input.From = message.Header.Get("From") - input.To = message.Header.Get("To") + input.From = from + input.To = to input.Subject = message.Header.Get("Subject") input.Body = body @@ -188,7 +192,7 @@ func (s *Server) dispatchEmail(email v1.EmailReceiver, body string, message *mai } var workflow v1.Workflow - if err := alias.Get(s.ctx, s.c, &workflow, email.Namespace, email.Spec.Workflow); err != nil { + if err = alias.Get(s.ctx, s.c, &workflow, email.Namespace, email.Spec.Workflow); err != nil { return err } diff --git a/pkg/storage/apis/otto.otto8.ai/v1/emailaddress.go b/pkg/storage/apis/otto.otto8.ai/v1/emailaddress.go index d69a5f713..b69e7e653 100644 --- a/pkg/storage/apis/otto.otto8.ai/v1/emailaddress.go +++ b/pkg/storage/apis/otto.otto8.ai/v1/emailaddress.go @@ -42,7 +42,7 @@ func (in *EmailReceiver) FieldNames() []string { } func (in *EmailReceiver) GetAliasName() string { - return in.Spec.EmailReceiverManifest.User + return in.Spec.EmailReceiverManifest.Alias } func (in *EmailReceiver) SetAssigned(assigned bool) { @@ -64,7 +64,7 @@ func (in *EmailReceiver) SetObservedGeneration(gen int64) { func (*EmailReceiver) GetColumns() [][]string { return [][]string{ {"Name", "Name"}, - {"User", "Spec.User"}, + {"Alias", "Spec.Alias"}, {"Workflow", "Spec.Workflow"}, {"Created", "{{ago .CreationTimestamp}}"}, {"Description", "Spec.Description"}, diff --git a/pkg/storage/openapi/generated/openapi_generated.go b/pkg/storage/openapi/generated/openapi_generated.go index 501932b34..c4baa6a50 100644 --- a/pkg/storage/openapi/generated/openapi_generated.go +++ b/pkg/storage/openapi/generated/openapi_generated.go @@ -1102,7 +1102,7 @@ func schema_obot_platform_obot_apiclient_types_EmailReceiverManifest(ref common. Format: "", }, }, - "user": { + "alias": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", @@ -5325,7 +5325,7 @@ func schema_storage_apis_ottootto8ai_v1_EmailReceiverSpec(ref common.ReferenceCa Format: "", }, }, - "user": { + "alias": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "",