Blob


1 package apub
3 import (
4 "bytes"
5 "fmt"
6 "net/mail"
7 "net/smtp"
8 "strings"
9 "time"
10 )
12 func MarshalMail(activity *Activity) ([]byte, error) {
13 buf := &bytes.Buffer{}
15 actor, err := LookupActor(activity.AttributedTo)
16 if err != nil {
17 return nil, fmt.Errorf("lookup actor %s: %w", activity.AttributedTo, err)
18 }
19 fmt.Fprintf(buf, "From: %s\n", actor.Address())
21 if activity.CC != nil {
22 buf.WriteString("To: ")
23 rcpt := append(activity.To, activity.CC...)
24 var addrs []string
25 for _, u := range rcpt {
26 if u == ToEveryone {
27 continue
28 }
29 actor, err = LookupActor(u)
30 if err != nil {
31 return nil, fmt.Errorf("lookup actor %s: %w", u, err)
32 }
33 addrs = append(addrs, actor.Address().String())
34 }
35 buf.WriteString(strings.Join(addrs, ", "))
36 buf.WriteString("\n")
37 }
39 fmt.Fprintf(buf, "Date: %s\n", activity.Published.Format(time.RFC822))
40 fmt.Fprintf(buf, "Message-ID: <%s>\n", activity.ID)
41 if activity.Audience != "" {
42 fmt.Fprintf(buf, "List-ID: <%s>\n", activity.Audience)
43 }
44 if activity.InReplyTo != "" {
45 fmt.Fprintf(buf, "References: <%s>\n", activity.InReplyTo)
46 }
48 if activity.Source.Content != "" && activity.Source.MediaType == "text/markdown" {
49 fmt.Fprintln(buf, "Content-Type: text/plain; charset=utf-8")
50 } else {
51 fmt.Fprintln(buf, "Content-Type:", activity.MediaType)
52 }
53 fmt.Fprintln(buf, "Subject:", activity.Name)
54 fmt.Fprintln(buf)
55 if activity.Source.Content != "" && activity.Source.MediaType == "text/markdown" {
56 fmt.Fprintln(buf, activity.Source.Content)
57 } else {
58 fmt.Fprintln(buf, activity.Content)
59 }
60 _, err = mail.ReadMessage(bytes.NewReader(buf.Bytes()))
61 return buf.Bytes(), err
62 }
64 func SendMail(client *smtp.Client, activity *Activity, from string, to ...string) error {
65 b, err := MarshalMail(activity)
66 if err != nil {
67 return fmt.Errorf("marshal to mail message: %w", err)
68 }
69 if err := client.Mail(from); err != nil {
70 return fmt.Errorf("mail command: %w", err)
71 }
72 for _, rcpt := range to {
73 if err := client.Rcpt(rcpt); err != nil {
74 return fmt.Errorf("rcpt command: %w", err)
75 }
76 }
77 wc, err := client.Data()
78 if err != nil {
79 return fmt.Errorf("data command: %w", err)
80 }
81 if _, err := wc.Write(b); err != nil {
82 return fmt.Errorf("write message: %w", err)
83 }
84 if err := wc.Close(); err != nil {
85 return fmt.Errorf("close message writer: %w", err)
86 }
87 return nil
88 }