13 d3dfb672 2024-03-18 o func MarshalMail(activity *Activity, client *Client) ([]byte, error) {
14 d3dfb672 2024-03-18 o msg, err := marshalMail(activity, client)
15 d3dfb672 2024-03-18 o if err != nil {
16 d3dfb672 2024-03-18 o return nil, err
18 d3dfb672 2024-03-18 o return encodeMsg(msg), nil
21 d3dfb672 2024-03-18 o func marshalMail(activity *Activity, client *Client) (*mail.Message, error) {
22 d3dfb672 2024-03-18 o if client == nil {
23 d3dfb672 2024-03-18 o client = &DefaultClient
26 d3dfb672 2024-03-18 o msg := new(mail.Message)
27 d3dfb672 2024-03-18 o msg.Header = make(mail.Header)
28 d3dfb672 2024-03-18 o var actors []Actor
29 d3dfb672 2024-03-18 o from, err := client.LookupActor(activity.AttributedTo)
30 1d5ddf5d 2024-02-20 o if err != nil {
31 d3dfb672 2024-03-18 o return nil, fmt.Errorf("build From: lookup actor %s: %w", activity.AttributedTo, err)
33 d3dfb672 2024-03-18 o actors = append(actors, *from)
34 d3dfb672 2024-03-18 o msg.Header["From"] = []string{from.Address().String()}
36 d3dfb672 2024-03-18 o var addrs, collections []string
37 d3dfb672 2024-03-18 o for _, id := range activity.To {
38 d3dfb672 2024-03-18 o if id == PublicCollection {
42 d3dfb672 2024-03-18 o a, err := client.LookupActor(id)
43 77918b00 2024-03-07 o if err != nil {
44 d3dfb672 2024-03-18 o return nil, fmt.Errorf("build To: lookup actor %s: %w", id, err)
46 d3dfb672 2024-03-18 o if a.Type == "Collection" || a.Type == "OrderedCollection" {
47 d3dfb672 2024-03-18 o collections = append(collections, a.ID)
49 d3dfb672 2024-03-18 o addrs = append(addrs, a.Address().String())
50 d3dfb672 2024-03-18 o actors = append(actors, *a)
53 d3dfb672 2024-03-18 o for _, id := range collections {
54 d3dfb672 2024-03-18 o if i := indexFollowers(actors, id); i >= 0 {
55 d3dfb672 2024-03-18 o addrs = append(addrs, actors[i].FollowersAddress().String())
58 d3dfb672 2024-03-18 o msg.Header["To"] = addrs
60 d3dfb672 2024-03-18 o addrs, collections = []string{}, []string{}
61 d3dfb672 2024-03-18 o for _, id := range activity.CC {
62 d3dfb672 2024-03-18 o if id == PublicCollection {
66 d3dfb672 2024-03-18 o a, err := client.LookupActor(id)
67 d3dfb672 2024-03-18 o if err != nil {
68 d3dfb672 2024-03-18 o return nil, fmt.Errorf("build CC: lookup actor %s: %w", id, err)
70 d3dfb672 2024-03-18 o if a.Type == "Collection" || a.Type == "OrderedCollection" {
71 d3dfb672 2024-03-18 o collections = append(collections, a.ID)
74 d3dfb672 2024-03-18 o addrs = append(addrs, a.Address().String())
75 d3dfb672 2024-03-18 o actors = append(actors, *a)
77 d3dfb672 2024-03-18 o for _, id := range collections {
78 d3dfb672 2024-03-18 o if i := indexFollowers(actors, id); i >= 0 {
79 d3dfb672 2024-03-18 o addrs = append(addrs, actors[i].FollowersAddress().String())
82 d3dfb672 2024-03-18 o msg.Header["CC"] = addrs
84 af1bda8f 2024-03-18 o msg.Header["Date"] = []string{activity.Published.Format(time.RFC1123Z)}
85 d3dfb672 2024-03-18 o msg.Header["Message-ID"] = []string{"<" + activity.ID + ">"}
86 d3dfb672 2024-03-18 o msg.Header["Subject"] = []string{activity.Name}
87 1d5ddf5d 2024-02-20 o if activity.Audience != "" {
88 d3dfb672 2024-03-18 o msg.Header["List-ID"] = []string{"<" + activity.Audience + ">"}
90 1d5ddf5d 2024-02-20 o if activity.InReplyTo != "" {
91 d3dfb672 2024-03-18 o msg.Header["In-Reply-To"] = []string{"<" + activity.InReplyTo + ">"}
94 d3dfb672 2024-03-18 o msg.Body = strings.NewReader(activity.Content)
95 d3dfb672 2024-03-18 o msg.Header["Content-Type"] = []string{"text/html; charset=utf-8"}
96 1d5ddf5d 2024-02-20 o if activity.Source.Content != "" && activity.Source.MediaType == "text/markdown" {
97 d3dfb672 2024-03-18 o msg.Body = strings.NewReader(activity.Source.Content)
98 d3dfb672 2024-03-18 o msg.Header["Content-Type"] = []string{"text/plain; charset=utf-8"}
99 184e4de6 2024-03-13 o } else if activity.MediaType == "text/markdown" {
100 d3dfb672 2024-03-18 o msg.Header["Content-Type"] = []string{"text/plain; charset=utf-8"}
102 d3dfb672 2024-03-18 o return msg, nil
105 d3dfb672 2024-03-18 o func indexFollowers(actors []Actor, id string) int {
106 d3dfb672 2024-03-18 o for i := range actors {
107 d3dfb672 2024-03-18 o if actors[i].Followers == id {
114 d3dfb672 2024-03-18 o func UnmarshalMail(msg *mail.Message, client *Client) (*Activity, error) {
115 d3dfb672 2024-03-18 o if client == nil {
116 d3dfb672 2024-03-18 o client = &DefaultClient
118 662ec973 2024-03-14 o ct := msg.Header.Get("Content-Type")
119 662ec973 2024-03-14 o if strings.HasPrefix(ct, "multipart") {
120 662ec973 2024-03-14 o return nil, fmt.Errorf("cannot unmarshal from multipart message")
122 662ec973 2024-03-14 o enc := msg.Header.Get("Content-Transfer-Encoding")
123 662ec973 2024-03-14 o if enc == "quoted-printable" {
124 662ec973 2024-03-14 o return nil, fmt.Errorf("cannot decode message with transfer encoding: %s", enc)
127 77918b00 2024-03-07 o date, err := msg.Header.Date()
128 71191436 2024-02-28 o if err != nil {
129 77918b00 2024-03-07 o return nil, fmt.Errorf("parse message date: %w", err)
131 77918b00 2024-03-07 o from, err := msg.Header.AddressList("From")
132 77918b00 2024-03-07 o if err != nil {
133 77918b00 2024-03-07 o return nil, fmt.Errorf("parse From: %w", err)
135 d3dfb672 2024-03-18 o wfrom, err := client.Finger(from[0].Address)
136 77918b00 2024-03-07 o if err != nil {
137 77918b00 2024-03-07 o return nil, fmt.Errorf("webfinger From: %w", err)
140 25fed994 2024-03-12 o var wto, wcc []string
141 d802abd8 2024-03-12 o var tags []Activity
142 25fed994 2024-03-12 o if msg.Header.Get("To") != "" {
143 25fed994 2024-03-12 o to, err := msg.Header.AddressList("To")
144 25fed994 2024-03-12 o // ignore missing To line. Some ActivityPub servers only have the
145 25fed994 2024-03-12 o // PublicCollection listed, which we don't care about.
146 25fed994 2024-03-12 o if err != nil {
147 25fed994 2024-03-12 o return nil, fmt.Errorf("parse To address list: %w", err)
149 d3dfb672 2024-03-18 o actors, err := client.fingerAll(to)
150 25fed994 2024-03-12 o if err != nil {
151 25fed994 2024-03-12 o return nil, fmt.Errorf("webfinger To addresses: %w", err)
153 2469f664 2024-03-12 o wto = make([]string, len(actors))
154 2469f664 2024-03-12 o for i, a := range actors {
155 d802abd8 2024-03-12 o addr := strings.Trim(to[i].Address, "<>")
156 eec64935 2024-03-16 o if strings.Contains(addr, "+followers") {
157 eec64935 2024-03-16 o wto[i] = a.Followers
160 eec64935 2024-03-16 o tags = append(tags, Activity{Type: "Mention", Href: a.ID, Name: "@" + addr})
164 77918b00 2024-03-07 o if msg.Header.Get("CC") != "" {
165 77918b00 2024-03-07 o cc, err := msg.Header.AddressList("CC")
166 77918b00 2024-03-07 o if err != nil {
167 77918b00 2024-03-07 o return nil, fmt.Errorf("parse CC address list: %w", err)
169 d3dfb672 2024-03-18 o actors, err := client.fingerAll(cc)
170 77918b00 2024-03-07 o if err != nil {
171 77918b00 2024-03-07 o return nil, fmt.Errorf("webfinger CC addresses: %w", err)
173 2469f664 2024-03-12 o wcc = make([]string, len(actors))
174 2469f664 2024-03-12 o for i, a := range actors {
175 eec64935 2024-03-16 o if strings.Contains(cc[i].Address, "+followers") {
176 eec64935 2024-03-16 o wcc[i] = a.Followers
183 77918b00 2024-03-07 o buf := &bytes.Buffer{}
184 77918b00 2024-03-07 o if _, err := io.Copy(buf, msg.Body); err != nil {
185 77918b00 2024-03-07 o return nil, fmt.Errorf("read message body: %v", err)
187 d3dfb672 2024-03-18 o content := strings.TrimSpace(strings.ReplaceAll(buf.String(), "\r", ""))
189 77918b00 2024-03-07 o return &Activity{
190 186ac3bf 2024-03-12 o AtContext: NormContext,
192 77918b00 2024-03-07 o AttributedTo: wfrom.ID,
195 77918b00 2024-03-07 o MediaType: "text/markdown",
196 77918b00 2024-03-07 o Name: strings.TrimSpace(msg.Header.Get("Subject")),
197 d3dfb672 2024-03-18 o Content: content,
198 77918b00 2024-03-07 o InReplyTo: strings.Trim(msg.Header.Get("In-Reply-To"), "<>"),
199 77918b00 2024-03-07 o Published: &date,
204 77918b00 2024-03-07 o func SendMail(addr string, auth smtp.Auth, from string, to []string, activity *Activity) error {
205 d3dfb672 2024-03-18 o msg, err := MarshalMail(activity, nil)
206 77918b00 2024-03-07 o if err != nil {
207 71191436 2024-02-28 o return fmt.Errorf("marshal to mail message: %w", err)
209 77918b00 2024-03-07 o return smtp.SendMail(addr, auth, from, to, msg)
212 d3dfb672 2024-03-18 o func encodeMsg(msg *mail.Message) []byte {
213 d3dfb672 2024-03-18 o buf := &bytes.Buffer{}
214 d3dfb672 2024-03-18 o // Lead with "From", end with "Subject" to make some mail clients happy.
215 d3dfb672 2024-03-18 o fmt.Fprintln(buf, "From:", msg.Header.Get("From"))
216 d3dfb672 2024-03-18 o for k, v := range msg.Header {
218 d3dfb672 2024-03-18 o case "Subject", "From":
221 d3dfb672 2024-03-18 o fmt.Fprintf(buf, "%s: %s\n", k, strings.Join(v, ", "))
224 d3dfb672 2024-03-18 o fmt.Fprintln(buf, "Subject:", msg.Header.Get("Subject"))
225 d3dfb672 2024-03-18 o fmt.Fprintln(buf)
226 d3dfb672 2024-03-18 o io.Copy(buf, msg.Body)
227 d3dfb672 2024-03-18 o return buf.Bytes()