Blame


1 1d5ddf5d 2024-02-20 o package apub
2 1d5ddf5d 2024-02-20 o
3 1d5ddf5d 2024-02-20 o import (
4 1d5ddf5d 2024-02-20 o "bytes"
5 1d5ddf5d 2024-02-20 o "fmt"
6 77918b00 2024-03-07 o "io"
7 1d5ddf5d 2024-02-20 o "net/mail"
8 71191436 2024-02-28 o "net/smtp"
9 1d5ddf5d 2024-02-20 o "strings"
10 1d5ddf5d 2024-02-20 o "time"
11 1d5ddf5d 2024-02-20 o )
12 1d5ddf5d 2024-02-20 o
13 1d5ddf5d 2024-02-20 o func MarshalMail(activity *Activity) ([]byte, error) {
14 1d5ddf5d 2024-02-20 o buf := &bytes.Buffer{}
15 1d5ddf5d 2024-02-20 o
16 77918b00 2024-03-07 o from, err := LookupActor(activity.AttributedTo)
17 1d5ddf5d 2024-02-20 o if err != nil {
18 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("lookup actor %s: %w", activity.AttributedTo, err)
19 1d5ddf5d 2024-02-20 o }
20 77918b00 2024-03-07 o fmt.Fprintf(buf, "From: %s\n", from.Address())
21 1d5ddf5d 2024-02-20 o
22 77918b00 2024-03-07 o var rcpt []string
23 77918b00 2024-03-07 o for _, u := range activity.To {
24 77918b00 2024-03-07 o if u == PublicCollection {
25 77918b00 2024-03-07 o continue
26 77918b00 2024-03-07 o }
27 77918b00 2024-03-07 o actor, err := LookupActor(u)
28 77918b00 2024-03-07 o if err != nil {
29 77918b00 2024-03-07 o return nil, fmt.Errorf("lookup actor %s: %w", u, err)
30 77918b00 2024-03-07 o }
31 77918b00 2024-03-07 o rcpt = append(rcpt, actor.Address().String())
32 77918b00 2024-03-07 o }
33 77918b00 2024-03-07 o fmt.Fprintln(buf, "To:", strings.Join(rcpt, ", "))
34 77918b00 2024-03-07 o
35 77918b00 2024-03-07 o var rcptcc []string
36 1d5ddf5d 2024-02-20 o if activity.CC != nil {
37 77918b00 2024-03-07 o for _, u := range activity.CC {
38 77918b00 2024-03-07 o if u == PublicCollection {
39 1d5ddf5d 2024-02-20 o continue
40 77918b00 2024-03-07 o } else if u == from.Followers {
41 77918b00 2024-03-07 o rcptcc = append(rcptcc, from.FollowersAddress().String())
42 77918b00 2024-03-07 o continue
43 1d5ddf5d 2024-02-20 o }
44 77918b00 2024-03-07 o actor, err := LookupActor(u)
45 1d5ddf5d 2024-02-20 o if err != nil {
46 1d5ddf5d 2024-02-20 o return nil, fmt.Errorf("lookup actor %s: %w", u, err)
47 1d5ddf5d 2024-02-20 o }
48 77918b00 2024-03-07 o rcptcc = append(rcptcc, actor.Address().String())
49 1d5ddf5d 2024-02-20 o }
50 77918b00 2024-03-07 o fmt.Fprintln(buf, "CC:", strings.Join(rcptcc, ", "))
51 1d5ddf5d 2024-02-20 o }
52 1d5ddf5d 2024-02-20 o
53 1d5ddf5d 2024-02-20 o fmt.Fprintf(buf, "Date: %s\n", activity.Published.Format(time.RFC822))
54 1d5ddf5d 2024-02-20 o fmt.Fprintf(buf, "Message-ID: <%s>\n", activity.ID)
55 1d5ddf5d 2024-02-20 o if activity.Audience != "" {
56 1d5ddf5d 2024-02-20 o fmt.Fprintf(buf, "List-ID: <%s>\n", activity.Audience)
57 1d5ddf5d 2024-02-20 o }
58 1d5ddf5d 2024-02-20 o if activity.InReplyTo != "" {
59 1d5ddf5d 2024-02-20 o fmt.Fprintf(buf, "References: <%s>\n", activity.InReplyTo)
60 1d5ddf5d 2024-02-20 o }
61 1d5ddf5d 2024-02-20 o
62 1d5ddf5d 2024-02-20 o if activity.Source.Content != "" && activity.Source.MediaType == "text/markdown" {
63 1d5ddf5d 2024-02-20 o fmt.Fprintln(buf, "Content-Type: text/plain; charset=utf-8")
64 77918b00 2024-03-07 o } else if activity.MediaType != "" {
65 1d5ddf5d 2024-02-20 o fmt.Fprintln(buf, "Content-Type:", activity.MediaType)
66 77918b00 2024-03-07 o } else {
67 77918b00 2024-03-07 o fmt.Fprintln(buf, "Content-Type:", "text/html; charset=utf-8")
68 1d5ddf5d 2024-02-20 o }
69 1d5ddf5d 2024-02-20 o fmt.Fprintln(buf, "Subject:", activity.Name)
70 1d5ddf5d 2024-02-20 o fmt.Fprintln(buf)
71 1d5ddf5d 2024-02-20 o if activity.Source.Content != "" && activity.Source.MediaType == "text/markdown" {
72 1d5ddf5d 2024-02-20 o fmt.Fprintln(buf, activity.Source.Content)
73 1d5ddf5d 2024-02-20 o } else {
74 1d5ddf5d 2024-02-20 o fmt.Fprintln(buf, activity.Content)
75 1d5ddf5d 2024-02-20 o }
76 1d5ddf5d 2024-02-20 o _, err = mail.ReadMessage(bytes.NewReader(buf.Bytes()))
77 1d5ddf5d 2024-02-20 o return buf.Bytes(), err
78 1d5ddf5d 2024-02-20 o }
79 71191436 2024-02-28 o
80 77918b00 2024-03-07 o func UnmarshalMail(msg *mail.Message) (*Activity, error) {
81 77918b00 2024-03-07 o date, err := msg.Header.Date()
82 71191436 2024-02-28 o if err != nil {
83 77918b00 2024-03-07 o return nil, fmt.Errorf("parse message date: %w", err)
84 77918b00 2024-03-07 o }
85 77918b00 2024-03-07 o from, err := msg.Header.AddressList("From")
86 77918b00 2024-03-07 o if err != nil {
87 77918b00 2024-03-07 o return nil, fmt.Errorf("parse From: %w", err)
88 77918b00 2024-03-07 o }
89 77918b00 2024-03-07 o wfrom, err := Finger(from[0].Address)
90 77918b00 2024-03-07 o if err != nil {
91 77918b00 2024-03-07 o return nil, fmt.Errorf("webfinger From: %w", err)
92 77918b00 2024-03-07 o }
93 77918b00 2024-03-07 o
94 25fed994 2024-03-12 o var wto, wcc []string
95 d802abd8 2024-03-12 o var tags []Activity
96 25fed994 2024-03-12 o if msg.Header.Get("To") != "" {
97 25fed994 2024-03-12 o to, err := msg.Header.AddressList("To")
98 25fed994 2024-03-12 o // ignore missing To line. Some ActivityPub servers only have the
99 25fed994 2024-03-12 o // PublicCollection listed, which we don't care about.
100 25fed994 2024-03-12 o if err != nil {
101 25fed994 2024-03-12 o return nil, fmt.Errorf("parse To address list: %w", err)
102 25fed994 2024-03-12 o }
103 2469f664 2024-03-12 o actors, err := fingerAll(to)
104 25fed994 2024-03-12 o if err != nil {
105 25fed994 2024-03-12 o return nil, fmt.Errorf("webfinger To addresses: %w", err)
106 25fed994 2024-03-12 o }
107 2469f664 2024-03-12 o wto = make([]string, len(actors))
108 d802abd8 2024-03-12 o tags = make([]Activity, len(actors))
109 2469f664 2024-03-12 o for i, a := range actors {
110 d802abd8 2024-03-12 o addr := strings.Trim(to[i].Address, "<>")
111 d802abd8 2024-03-12 o tags[i] = Activity{Type: "Mention", Href: a.ID, Name: "@" + addr}
112 2469f664 2024-03-12 o wto[i] = a.ID
113 2469f664 2024-03-12 o }
114 77918b00 2024-03-07 o }
115 77918b00 2024-03-07 o if msg.Header.Get("CC") != "" {
116 77918b00 2024-03-07 o cc, err := msg.Header.AddressList("CC")
117 77918b00 2024-03-07 o if err != nil {
118 77918b00 2024-03-07 o return nil, fmt.Errorf("parse CC address list: %w", err)
119 77918b00 2024-03-07 o }
120 2469f664 2024-03-12 o actors, err := fingerAll(cc)
121 77918b00 2024-03-07 o if err != nil {
122 77918b00 2024-03-07 o return nil, fmt.Errorf("webfinger CC addresses: %w", err)
123 77918b00 2024-03-07 o }
124 2469f664 2024-03-12 o wcc = make([]string, len(actors))
125 2469f664 2024-03-12 o for i, a := range actors {
126 2469f664 2024-03-12 o wcc[i] = a.ID
127 2469f664 2024-03-12 o }
128 77918b00 2024-03-07 o }
129 77918b00 2024-03-07 o
130 77918b00 2024-03-07 o buf := &bytes.Buffer{}
131 77918b00 2024-03-07 o if _, err := io.Copy(buf, msg.Body); err != nil {
132 77918b00 2024-03-07 o return nil, fmt.Errorf("read message body: %v", err)
133 77918b00 2024-03-07 o }
134 77918b00 2024-03-07 o
135 77918b00 2024-03-07 o return &Activity{
136 186ac3bf 2024-03-12 o AtContext: NormContext,
137 77918b00 2024-03-07 o Type: "Note",
138 77918b00 2024-03-07 o AttributedTo: wfrom.ID,
139 77918b00 2024-03-07 o To: wto,
140 77918b00 2024-03-07 o CC: wcc,
141 77918b00 2024-03-07 o MediaType: "text/markdown",
142 77918b00 2024-03-07 o Name: strings.TrimSpace(msg.Header.Get("Subject")),
143 77918b00 2024-03-07 o Content: strings.TrimSpace(buf.String()),
144 77918b00 2024-03-07 o InReplyTo: strings.Trim(msg.Header.Get("In-Reply-To"), "<>"),
145 77918b00 2024-03-07 o Published: &date,
146 d802abd8 2024-03-12 o Tag: tags,
147 77918b00 2024-03-07 o }, nil
148 77918b00 2024-03-07 o }
149 77918b00 2024-03-07 o
150 77918b00 2024-03-07 o func SendMail(addr string, auth smtp.Auth, from string, to []string, activity *Activity) error {
151 77918b00 2024-03-07 o msg, err := MarshalMail(activity)
152 77918b00 2024-03-07 o if err != nil {
153 71191436 2024-02-28 o return fmt.Errorf("marshal to mail message: %w", err)
154 71191436 2024-02-28 o }
155 77918b00 2024-03-07 o return smtp.SendMail(addr, auth, from, to, msg)
156 71191436 2024-02-28 o }