@@ -3,17 +3,18 @@ package mail
33import (
44 "context"
55 "fmt"
6- "math"
76 "mokapi/imap"
7+ "mokapi/media"
8+ "mokapi/smtp"
89 "strings"
910)
1011
1112func (h * Handler ) Fetch (req * imap.FetchRequest , res imap.FetchResponse , ctx context.Context ) error {
1213 c := imap .ClientFromContext (ctx )
1314 mb := c .Session ["mailbox" ].(* Mailbox )
1415 selected := c .Session ["selected" ].(string )
15- f , ok := mb .Folders [ selected ]
16- if ! ok {
16+ f := mb .Select ( selected )
17+ if f == nil {
1718 return fmt .Errorf ("mailbox not found" )
1819 }
1920
@@ -61,47 +62,176 @@ func writeMessage(msg *Mail, opt imap.FetchOptions, w imap.MessageWriter) {
6162 if opt .Flags {
6263 w .WriteFlags (msg .Flags ... )
6364 }
65+ if opt .Envelope {
66+ env := & imap.Envelope {
67+ Date : msg .Time ,
68+ Subject : msg .Subject ,
69+ From : toEnvelopeAddressList (msg .From ),
70+ ReplyTo : toEnvelopeAddressList (msg .ReplyTo ),
71+ To : toEnvelopeAddressList (msg .To ),
72+ Cc : toEnvelopeAddressList (msg .Cc ),
73+ Bcc : toEnvelopeAddressList (msg .Bcc ),
74+ InReplyTo : msg .InReplyTo ,
75+ MessageId : msg .MessageId ,
76+ }
77+
78+ sender := toEnvelopeAddress (msg .Sender )
79+ if sender != nil {
80+ env .Sender = []imap.Address {* sender }
81+ }
82+
83+ w .WriteEnvelope (env )
84+ }
85+ if opt .BodyStructure {
86+ ct := media .ParseContentType (msg .ContentType )
87+
88+ bs := & imap.BodyStructure {
89+ Type : ct .Type ,
90+ Subtype : ct .Subtype ,
91+ Params : ct .Parameters ,
92+ Encoding : msg .ContentTransferEncoding ,
93+ Size : uint32 (msg .Size ),
94+ }
95+
96+ for _ , part := range msg .Attachments {
97+ partType := media .ParseContentType (part .ContentType )
98+ p := imap.BodyStructure {
99+ Type : partType .Type ,
100+ Subtype : partType .Subtype ,
101+ Params : partType .Parameters ,
102+ Encoding : part .Header ["Content-Transfer-Encoding" ],
103+ Size : uint32 (len (part .Data )),
104+ Disposition : part .Disposition ,
105+ }
106+ if part .ContentId != "" {
107+ p .ContentId = & part .ContentId
108+ }
109+ if part .Disposition != "" && part .Disposition != "inline" {
110+ p .Disposition = part .Disposition
111+ }
112+ bs .Parts = append (bs .Parts , p )
113+
114+ }
115+
116+ w .WriteBodyStructure (bs )
117+ }
64118
65119 for _ , body := range opt .Body {
66120 bw := w .WriteBody (body )
67- if body .Type == "header" {
68- for _ , field := range body .Fields {
69- switch strings .ToLower (field ) {
70- case "date" :
71- bw .WriteHeader ("date" , msg .Time .Format (imap .DateTimeLayout ))
72- case "subject" :
73- bw .WriteHeader ("subject" , msg .Subject )
74- case "from" :
75- bw .WriteHeader ("from" , addressListToString (msg .From ))
76- case "to" :
77- bw .WriteHeader ("to" , addressListToString (msg .To ))
78- case "cc" :
79- if msg .Cc != nil {
80- bw .WriteHeader ("cc" , addressListToString (msg .Cc ))
121+
122+ if body .Specifier == "header" {
123+ if len (body .Fields ) == 0 {
124+ bw .WriteHeader ("date" , msg .Time .Format (imap .DateTimeLayout ))
125+ bw .WriteHeader ("subject" , msg .Subject )
126+ bw .WriteHeader ("from" , addressListToString (msg .From ))
127+ bw .WriteHeader ("to" , addressListToString (msg .To ))
128+ if msg .Cc != nil {
129+ bw .WriteHeader ("cc" , addressListToString (msg .Cc ))
130+ }
131+ bw .WriteHeader ("message-id" , msg .MessageId )
132+ bw .WriteHeader ("content-type" , msg .ContentType )
133+ // Add any additional headers
134+ for name , value := range msg .Headers {
135+ // avoid writing duplicates
136+ if ! isStandardHeader (name ) {
137+ bw .WriteHeader (name , value )
81138 }
82- case "message-id" :
83- bw .WriteHeader ("message-id" , msg .MessageId )
84- case "content-type" :
85- bw .WriteHeader ("content-type" , msg .ContentType )
86- default :
87- if v , ok := msg .Headers [field ]; ok {
88- bw .WriteHeader (field , v )
139+ }
140+ } else {
141+ for _ , field := range body .Fields {
142+ switch strings .ToLower (field ) {
143+ case "date" :
144+ bw .WriteHeader ("date" , msg .Time .Format (imap .DateTimeLayout ))
145+ case "subject" :
146+ bw .WriteHeader ("subject" , msg .Subject )
147+ case "from" :
148+ bw .WriteHeader ("from" , addressListToString (msg .From ))
149+ case "to" :
150+ bw .WriteHeader ("to" , addressListToString (msg .To ))
151+ case "cc" :
152+ if msg .Cc != nil {
153+ bw .WriteHeader ("cc" , addressListToString (msg .Cc ))
154+ }
155+ case "message-id" :
156+ bw .WriteHeader ("message-id" , msg .MessageId )
157+ case "content-type" :
158+ bw .WriteHeader ("content-type" , msg .ContentType )
159+ default :
160+ if v , ok := msg .Headers [field ]; ok {
161+ bw .WriteHeader (field , v )
162+ }
89163 }
90164 }
91165 }
92- } else if body .Type == "text" {
93- if body .Partially != nil {
94- n := int (math .Min (float64 (len (msg .Body )), float64 (body .Partially .Limit )))
95- bw .WriteBody (msg .Body [body .Partially .Offset :n ])
96- } else {
166+ } else if body .Specifier == "text" {
167+ if strings .HasPrefix (msg .ContentType , "multipart/" ) && len (body .Parts ) == 0 {
168+ // https://datatracker.ietf.org/doc/html/rfc3501#section-7.4.2
169+ continue
170+ }
171+ if len (body .Parts ) == 0 || len (msg .Attachments ) == 0 {
97172 bw .WriteBody (msg .Body )
173+ } else {
174+ for _ , part := range body .Parts {
175+ index := part - 1
176+ if index < 0 || index > len (msg .Attachments ) {
177+ continue
178+ }
179+ p := msg .Attachments [index ]
180+ bw .WriteBody (string (p .Data ))
181+ }
98182 }
99- } else if body .Type == "" {
100- for k , v := range msg .Headers {
101- bw .WriteHeader (k , v )
183+ } else if body .Specifier == "" {
184+ if body .Parts == nil {
185+ for k , v := range msg .Headers {
186+ bw .WriteHeader (k , v )
187+ }
188+ bw .WriteBody (msg .Body )
189+ } else {
190+ index := body .Parts [0 ] - 1
191+ if index < 0 || index > len (msg .Attachments ) {
192+ continue
193+ }
194+ att := msg .Attachments [index ]
195+ for k , v := range att .Header {
196+ bw .WriteHeader (k , v )
197+ }
198+ bw .WriteBody (string (att .Data ))
102199 }
103- bw . WriteBody ( msg . Body )
200+
104201 }
105202 bw .Close ()
106203 }
107204}
205+
206+ func isStandardHeader (name string ) bool {
207+ switch strings .ToLower (name ) {
208+ case "date" , "subject" , "from" , "to" , "cc" , "message-id" , "content-type" :
209+ return true
210+ default :
211+ return false
212+ }
213+ }
214+
215+ func toEnvelopeAddressList (list []smtp.Address ) []imap.Address {
216+ if len (list ) == 0 {
217+ return nil
218+ }
219+
220+ result := make ([]imap.Address , len (list ))
221+ for i , addr := range list {
222+ result [i ] = * toEnvelopeAddress (& addr )
223+ }
224+ return result
225+ }
226+
227+ func toEnvelopeAddress (addr * smtp.Address ) * imap.Address {
228+ if addr == nil {
229+ return nil
230+ }
231+ parts := strings .Split (addr .Address , "@" )
232+ return & imap.Address {
233+ Name : addr .Name ,
234+ Mailbox : parts [0 ],
235+ Host : parts [1 ],
236+ }
237+ }
0 commit comments