7 5acd8b1b 2024-03-21 o "mime/quotedprintable"
14 d3dfb672 2024-03-18 o func MarshalMail(activity *Activity, client *Client) ([]byte, error) {
15 d3dfb672 2024-03-18 o msg, err := marshalMail(activity, client)
16 d3dfb672 2024-03-18 o if err != nil {
17 d3dfb672 2024-03-18 o return nil, err
19 d3dfb672 2024-03-18 o return encodeMsg(msg), nil
22 d3dfb672 2024-03-18 o func marshalMail(activity *Activity, client *Client) (*mail.Message, error) {
23 d3dfb672 2024-03-18 o if client == nil {
24 d3dfb672 2024-03-18 o client = &DefaultClient
27 d3dfb672 2024-03-18 o msg := new(mail.Message)
28 d3dfb672 2024-03-18 o msg.Header = make(mail.Header)
29 d3dfb672 2024-03-18 o var actors []Actor
30 d3dfb672 2024-03-18 o from, err := client.LookupActor(activity.AttributedTo)
31 1d5ddf5d 2024-02-20 o if err != nil {
32 d3dfb672 2024-03-18 o return nil, fmt.Errorf("build From: lookup actor %s: %w", activity.AttributedTo, err)
34 d3dfb672 2024-03-18 o actors = append(actors, *from)
35 d3dfb672 2024-03-18 o msg.Header["From"] = []string{from.Address().String()}
37 d3dfb672 2024-03-18 o var addrs, collections []string
38 d3dfb672 2024-03-18 o for _, id := range activity.To {
39 d3dfb672 2024-03-18 o if id == PublicCollection {
43 d3dfb672 2024-03-18 o a, err := client.LookupActor(id)
44 77918b00 2024-03-07 o if err != nil {
45 d3dfb672 2024-03-18 o return nil, fmt.Errorf("build To: lookup actor %s: %w", id, err)
47 d3dfb672 2024-03-18 o if a.Type == "Collection" || a.Type == "OrderedCollection" {
48 d3dfb672 2024-03-18 o collections = append(collections, a.ID)
50 d3dfb672 2024-03-18 o addrs = append(addrs, a.Address().String())
51 d3dfb672 2024-03-18 o actors = append(actors, *a)
54 d3dfb672 2024-03-18 o for _, id := range collections {
55 d3dfb672 2024-03-18 o if i := indexFollowers(actors, id); i >= 0 {
56 d3dfb672 2024-03-18 o addrs = append(addrs, actors[i].FollowersAddress().String())
59 d3dfb672 2024-03-18 o msg.Header["To"] = addrs
61 d3dfb672 2024-03-18 o addrs, collections = []string{}, []string{}
62 d3dfb672 2024-03-18 o for _, id := range activity.CC {
63 d3dfb672 2024-03-18 o if id == PublicCollection {
67 d3dfb672 2024-03-18 o a, err := client.LookupActor(id)
68 d3dfb672 2024-03-18 o if err != nil {
69 d3dfb672 2024-03-18 o return nil, fmt.Errorf("build CC: lookup actor %s: %w", id, err)
71 d3dfb672 2024-03-18 o if a.Type == "Collection" || a.Type == "OrderedCollection" {
72 d3dfb672 2024-03-18 o collections = append(collections, a.ID)
75 d3dfb672 2024-03-18 o addrs = append(addrs, a.Address().String())
76 d3dfb672 2024-03-18 o actors = append(actors, *a)
78 d3dfb672 2024-03-18 o for _, id := range collections {
79 d3dfb672 2024-03-18 o if i := indexFollowers(actors, id); i >= 0 {
80 d3dfb672 2024-03-18 o addrs = append(addrs, actors[i].FollowersAddress().String())
83 d3dfb672 2024-03-18 o msg.Header["CC"] = addrs
85 af1bda8f 2024-03-18 o msg.Header["Date"] = []string{activity.Published.Format(time.RFC1123Z)}
86 d3dfb672 2024-03-18 o msg.Header["Message-ID"] = []string{"<" + activity.ID + ">"}
87 d3dfb672 2024-03-18 o msg.Header["Subject"] = []string{activity.Name}
88 1d5ddf5d 2024-02-20 o if activity.Audience != "" {
89 d3dfb672 2024-03-18 o msg.Header["List-ID"] = []string{"<" + activity.Audience + ">"}
91 1d5ddf5d 2024-02-20 o if activity.InReplyTo != "" {
92 d3dfb672 2024-03-18 o msg.Header["In-Reply-To"] = []string{"<" + activity.InReplyTo + ">"}
95 d3dfb672 2024-03-18 o msg.Body = strings.NewReader(activity.Content)
96 d3dfb672 2024-03-18 o msg.Header["Content-Type"] = []string{"text/html; charset=utf-8"}
97 1d5ddf5d 2024-02-20 o if activity.Source.Content != "" && activity.Source.MediaType == "text/markdown" {
98 d3dfb672 2024-03-18 o msg.Body = strings.NewReader(activity.Source.Content)
99 d3dfb672 2024-03-18 o msg.Header["Content-Type"] = []string{"text/plain; charset=utf-8"}
100 184e4de6 2024-03-13 o } else if activity.MediaType == "text/markdown" {
101 d3dfb672 2024-03-18 o msg.Header["Content-Type"] = []string{"text/plain; charset=utf-8"}
103 d3dfb672 2024-03-18 o return msg, nil
106 d3dfb672 2024-03-18 o func indexFollowers(actors []Actor, id string) int {
107 d3dfb672 2024-03-18 o for i := range actors {
108 d3dfb672 2024-03-18 o if actors[i].Followers == id {
115 d3dfb672 2024-03-18 o func UnmarshalMail(msg *mail.Message, client *Client) (*Activity, error) {
116 d3dfb672 2024-03-18 o if client == nil {
117 d3dfb672 2024-03-18 o client = &DefaultClient
119 662ec973 2024-03-14 o ct := msg.Header.Get("Content-Type")
120 662ec973 2024-03-14 o if strings.HasPrefix(ct, "multipart") {
121 662ec973 2024-03-14 o return nil, fmt.Errorf("cannot unmarshal from multipart message")
124 77918b00 2024-03-07 o date, err := msg.Header.Date()
125 71191436 2024-02-28 o if err != nil {
126 77918b00 2024-03-07 o return nil, fmt.Errorf("parse message date: %w", err)
128 77918b00 2024-03-07 o from, err := msg.Header.AddressList("From")
129 77918b00 2024-03-07 o if err != nil {
130 77918b00 2024-03-07 o return nil, fmt.Errorf("parse From: %w", err)
132 d3dfb672 2024-03-18 o wfrom, err := client.Finger(from[0].Address)
133 77918b00 2024-03-07 o if err != nil {
134 77918b00 2024-03-07 o return nil, fmt.Errorf("webfinger From: %w", err)
137 25fed994 2024-03-12 o var wto, wcc []string
138 d802abd8 2024-03-12 o var tags []Activity
139 25fed994 2024-03-12 o if msg.Header.Get("To") != "" {
140 25fed994 2024-03-12 o to, err := msg.Header.AddressList("To")
141 25fed994 2024-03-12 o // ignore missing To line. Some ActivityPub servers only have the
142 25fed994 2024-03-12 o // PublicCollection listed, which we don't care about.
143 25fed994 2024-03-12 o if err != nil {
144 25fed994 2024-03-12 o return nil, fmt.Errorf("parse To address list: %w", err)
146 d3dfb672 2024-03-18 o actors, err := client.fingerAll(to)
147 25fed994 2024-03-12 o if err != nil {
148 25fed994 2024-03-12 o return nil, fmt.Errorf("webfinger To addresses: %w", err)
150 2469f664 2024-03-12 o wto = make([]string, len(actors))
151 2469f664 2024-03-12 o for i, a := range actors {
152 d802abd8 2024-03-12 o addr := strings.Trim(to[i].Address, "<>")
153 eec64935 2024-03-16 o if strings.Contains(addr, "+followers") {
154 eec64935 2024-03-16 o wto[i] = a.Followers
157 eec64935 2024-03-16 o tags = append(tags, Activity{Type: "Mention", Href: a.ID, Name: "@" + addr})
161 77918b00 2024-03-07 o if msg.Header.Get("CC") != "" {
162 77918b00 2024-03-07 o cc, err := msg.Header.AddressList("CC")
163 77918b00 2024-03-07 o if err != nil {
164 77918b00 2024-03-07 o return nil, fmt.Errorf("parse CC address list: %w", err)
166 d3dfb672 2024-03-18 o actors, err := client.fingerAll(cc)
167 77918b00 2024-03-07 o if err != nil {
168 77918b00 2024-03-07 o return nil, fmt.Errorf("webfinger CC addresses: %w", err)
170 2469f664 2024-03-12 o wcc = make([]string, len(actors))
171 2469f664 2024-03-12 o for i, a := range actors {
172 eec64935 2024-03-16 o if strings.Contains(cc[i].Address, "+followers") {
173 eec64935 2024-03-16 o wcc[i] = a.Followers
180 77918b00 2024-03-07 o buf := &bytes.Buffer{}
181 5acd8b1b 2024-03-21 o if msg.Header.Get("Content-Transfer-Encoding") == "quoted-printable" {
182 5acd8b1b 2024-03-21 o _, err = io.Copy(buf, quotedprintable.NewReader(msg.Body))
184 5acd8b1b 2024-03-21 o _, err = io.Copy(buf, msg.Body)
186 5acd8b1b 2024-03-21 o if err != nil {
187 77918b00 2024-03-07 o return nil, fmt.Errorf("read message body: %v", err)
189 d3dfb672 2024-03-18 o content := strings.TrimSpace(strings.ReplaceAll(buf.String(), "\r", ""))
191 77918b00 2024-03-07 o return &Activity{
192 186ac3bf 2024-03-12 o AtContext: NormContext,
194 77918b00 2024-03-07 o AttributedTo: wfrom.ID,
197 77918b00 2024-03-07 o MediaType: "text/markdown",
198 77918b00 2024-03-07 o Name: strings.TrimSpace(msg.Header.Get("Subject")),
199 d3dfb672 2024-03-18 o Content: content,
200 77918b00 2024-03-07 o InReplyTo: strings.Trim(msg.Header.Get("In-Reply-To"), "<>"),
201 77918b00 2024-03-07 o Published: &date,
206 77918b00 2024-03-07 o func SendMail(addr string, auth smtp.Auth, from string, to []string, activity *Activity) error {
207 d3dfb672 2024-03-18 o msg, err := MarshalMail(activity, nil)
208 77918b00 2024-03-07 o if err != nil {
209 71191436 2024-02-28 o return fmt.Errorf("marshal to mail message: %w", err)
211 77918b00 2024-03-07 o return smtp.SendMail(addr, auth, from, to, msg)
214 d3dfb672 2024-03-18 o func encodeMsg(msg *mail.Message) []byte {
215 d3dfb672 2024-03-18 o buf := &bytes.Buffer{}
216 d3dfb672 2024-03-18 o // Lead with "From", end with "Subject" to make some mail clients happy.
217 d3dfb672 2024-03-18 o fmt.Fprintln(buf, "From:", msg.Header.Get("From"))
218 d3dfb672 2024-03-18 o for k, v := range msg.Header {
220 d3dfb672 2024-03-18 o case "Subject", "From":
223 d3dfb672 2024-03-18 o fmt.Fprintf(buf, "%s: %s\n", k, strings.Join(v, ", "))
226 d3dfb672 2024-03-18 o fmt.Fprintln(buf, "Subject:", msg.Header.Get("Subject"))
227 d3dfb672 2024-03-18 o fmt.Fprintln(buf)
228 d3dfb672 2024-03-18 o io.Copy(buf, msg.Body)
229 d3dfb672 2024-03-18 o return buf.Bytes()