|
1 | 1 | package imapmemserver |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bufio" |
| 5 | + "bytes" |
| 6 | + "net/mail" |
| 7 | + "net/textproto" |
4 | 8 | "sort" |
| 9 | + "strings" |
5 | 10 |
|
6 | 11 | "github.com/emersion/go-imap/v2" |
7 | 12 | "github.com/emersion/go-imap/v2/imapserver" |
@@ -142,31 +147,78 @@ func compareByCriterion(a, b *message, key imap.SortKey) int { |
142 | 147 | return 0 |
143 | 148 |
|
144 | 149 | case imap.SortKeyFrom: |
145 | | - // TODO: For a real implementation, extract the From header and compare |
146 | | - return 0 |
| 150 | + // NOTE: A fully compliant implementation as per RFC 5256 would parse |
| 151 | + // the address and sort by mailbox, then host. This is a simplified |
| 152 | + // case-insensitive comparison of the full header value. |
| 153 | + fromA := getHeader(a.buf, "From") |
| 154 | + fromB := getHeader(b.buf, "From") |
| 155 | + return strings.Compare(strings.ToLower(fromA), strings.ToLower(fromB)) |
147 | 156 |
|
148 | 157 | case imap.SortKeyTo: |
149 | | - // TODO: For a real implementation, you would extract the To header and compare |
150 | | - return 0 |
| 158 | + // NOTE: Simplified comparison. See SortKeyFrom. |
| 159 | + toA := getHeader(a.buf, "To") |
| 160 | + toB := getHeader(b.buf, "To") |
| 161 | + return strings.Compare(strings.ToLower(toA), strings.ToLower(toB)) |
151 | 162 |
|
152 | 163 | case imap.SortKeyCc: |
153 | | - // TODO: For a real implementation, you would extract the Cc header and compare |
154 | | - return 0 |
| 164 | + // NOTE: Simplified comparison. See SortKeyFrom. |
| 165 | + ccA := getHeader(a.buf, "Cc") |
| 166 | + ccB := getHeader(b.buf, "Cc") |
| 167 | + return strings.Compare(strings.ToLower(ccA), strings.ToLower(ccB)) |
155 | 168 |
|
156 | 169 | case imap.SortKeySubject: |
157 | | - // TODO: For a real implementation, you would extract the Subject header and compare |
158 | | - return 0 |
| 170 | + // RFC 5256 specifies i;ascii-casemap collation, which is case-insensitive. |
| 171 | + subjA := getHeader(a.buf, "Subject") |
| 172 | + subjB := getHeader(b.buf, "Subject") |
| 173 | + return strings.Compare(strings.ToLower(subjA), strings.ToLower(subjB)) |
159 | 174 |
|
160 | 175 | case imap.SortKeyDisplay: |
161 | | - // SORT=DISPLAY (RFC 5957) - Use a locale-sensitive version of the string |
162 | | - // For now, treat it the same as the subject sorting for this implementation |
163 | | - // TODO: For a real implementation, use proper locale-aware sorting of display names |
164 | | - // A full implementation would handle internationalized text according to |
165 | | - // the user's locale settings and apply proper collation rules |
166 | | - return 0 |
| 176 | + // RFC 5957: sort by display-name, fallback to mailbox. |
| 177 | + fromA := getHeader(a.buf, "From") |
| 178 | + fromB := getHeader(b.buf, "From") |
| 179 | + |
| 180 | + addrA, errA := mail.ParseAddress(fromA) |
| 181 | + addrB, errB := mail.ParseAddress(fromB) |
| 182 | + |
| 183 | + var displayA, displayB string |
| 184 | + |
| 185 | + if errA == nil { |
| 186 | + if addrA.Name != "" { |
| 187 | + displayA = addrA.Name |
| 188 | + } else { |
| 189 | + displayA = addrA.Address |
| 190 | + } |
| 191 | + } else { |
| 192 | + displayA = fromA // Fallback to raw header on parse error |
| 193 | + } |
| 194 | + |
| 195 | + if errB == nil { |
| 196 | + if addrB.Name != "" { |
| 197 | + displayB = addrB.Name |
| 198 | + } else { |
| 199 | + displayB = addrB.Address |
| 200 | + } |
| 201 | + } else { |
| 202 | + displayB = fromB // Fallback to raw header on parse error |
| 203 | + } |
| 204 | + |
| 205 | + // A full implementation would use locale-aware sorting (e.g., golang.org/x/text/collate). |
| 206 | + // A case-insensitive comparison is a reasonable and significant improvement. |
| 207 | + return strings.Compare(strings.ToLower(displayA), strings.ToLower(displayB)) |
167 | 208 |
|
168 | 209 | default: |
169 | 210 | // Default to no sorting for unknown criteria |
170 | 211 | return 0 |
171 | 212 | } |
172 | 213 | } |
| 214 | + |
| 215 | +// getHeader extracts a header value from a message's raw bytes. |
| 216 | +// It performs a case-insensitive search for the key. |
| 217 | +func getHeader(buf []byte, key string) string { |
| 218 | + r := textproto.NewReader(bufio.NewReader(bytes.NewReader(buf))) |
| 219 | + hdr, err := r.ReadMIMEHeader() |
| 220 | + if err != nil { |
| 221 | + return "" // Or log the error |
| 222 | + } |
| 223 | + return hdr.Get(key) |
| 224 | +} |
0 commit comments