(utf8 bool, w io.Writer)
| 125 | } |
| 126 | |
| 127 | func (info RecipientInfo) WriteTo(utf8 bool, w io.Writer) error { |
| 128 | // DSN format uses structure similar to MIME header, so we reuse |
| 129 | // MIME generator here. |
| 130 | h := textproto.Header{} |
| 131 | |
| 132 | if info.FinalRecipient == "" { |
| 133 | return errors.New("dsn: Final-Recipient is required") |
| 134 | } |
| 135 | finalRcpt, err := address.SelectIDNA(utf8, info.FinalRecipient) |
| 136 | if err != nil { |
| 137 | return fmt.Errorf("dsn: cannot convert Final-Recipient to a suitable representation: %w", err) |
| 138 | } |
| 139 | if utf8 { |
| 140 | h.Add("Final-Recipient", "utf8; "+finalRcpt) |
| 141 | } else { |
| 142 | h.Add("Final-Recipient", "rfc822; "+finalRcpt) |
| 143 | } |
| 144 | |
| 145 | if info.Action == "" { |
| 146 | return errors.New("dsn: Action is required") |
| 147 | } |
| 148 | h.Add("Action", string(info.Action)) |
| 149 | if info.Status[0] == 0 { |
| 150 | return errors.New("dsn: Status is required") |
| 151 | } |
| 152 | h.Add("Status", fmt.Sprintf("%d.%d.%d", info.Status[0], info.Status[1], info.Status[2])) |
| 153 | |
| 154 | if smtpErr, ok := info.DiagnosticCode.(*smtp.SMTPError); ok { |
| 155 | // Error message may contain newlines if it is received from another SMTP server. |
| 156 | // But we cannot directly insert CR/LF into Disagnostic-Code so rewrite it. |
| 157 | h.Add("Diagnostic-Code", fmt.Sprintf("smtp; %d %d.%d.%d %s", |
| 158 | smtpErr.Code, smtpErr.EnhancedCode[0], smtpErr.EnhancedCode[1], smtpErr.EnhancedCode[2], |
| 159 | strings.ReplaceAll(strings.ReplaceAll(smtpErr.Message, "\n", " "), "\r", " "))) |
| 160 | } else if utf8 { |
| 161 | // It might contain Unicode, so don't include it if we are not allowed to. |
| 162 | // ... I didn't bother implementing mangling logic to remove Unicode |
| 163 | // characters. |
| 164 | errorDesc := info.DiagnosticCode.Error() |
| 165 | errorDesc = strings.ReplaceAll(strings.ReplaceAll(errorDesc, "\n", " "), "\r", " ") |
| 166 | |
| 167 | h.Add("Diagnostic-Code", "X-Maddy; "+errorDesc) |
| 168 | } |
| 169 | |
| 170 | if info.RemoteMTA != "" { |
| 171 | remoteMTA, err := dns.SelectIDNA(utf8, info.RemoteMTA) |
| 172 | if err != nil { |
| 173 | return fmt.Errorf("dsn: cannot convert Remote-MTA to a suitable representation: %w", err) |
| 174 | } |
| 175 | |
| 176 | h.Add("Remote-MTA", "dns; "+remoteMTA) |
| 177 | } |
| 178 | |
| 179 | return textproto.WriteHeader(w, h) |
| 180 | } |
| 181 | |
| 182 | type Envelope struct { |
| 183 | MsgID string |
no test coverage detected