...
Source file
src/tawesoft.co.uk/go/email/util.go
Documentation:
src/tawesoft.co.uk/go/email/util.go
1 package email
2
3 import (
4 "crypto/rand"
5 "fmt"
6 "io"
7 "mime"
8 "strings"
9 "time"
10 )
11
12
13
14 func NewMessageID(host string) string {
15 var rnd = make([]byte, 16)
16 rand.Read(rnd)
17 var tnow = time.Now().UTC().Format("20060102150405")
18 return fmt.Sprintf("%s.%x@%s", tnow, rnd, host)
19 }
20
21
22 func strall(
23 xs string,
24 f func(c rune) bool,
25 ) bool {
26 for _, x := range xs {
27
28 if !f(x) { return false }
29 }
30
31 return true
32 }
33
34
35 func optionalQEncode(x string) string {
36 var onlyPrintableAscii = func(c rune) bool {
37 return (c >= 0x20) && (c <= 0x7e)
38 }
39
40 if strall(x, onlyPrintableAscii) {
41 return x
42 } else {
43 return mime.QEncoding.Encode("utf-8", x)
44 }
45 }
46
47
48
49
50
51 type lineBreaker struct {
52 column int
53 writer io.Writer
54 }
55
56
57
58
59
60
61 func (lb lineBreaker) Write(p []byte) (n int, err error) {
62
63 const LIMIT = 76
64 var offset int
65 var written int
66 lb.column += len(p)
67
68 for offset = 0; lb.column >= 76; lb.column -= LIMIT {
69 n, err := lb.writer.Write(p[offset:offset + LIMIT])
70 written += n
71 if err != nil { return written, err }
72
73 offset += LIMIT
74
75 if offset != len(p) {
76 n, err := io.WriteString(lb.writer, "\r\n")
77 written += n
78 if err != nil { return written, err }
79 }
80 }
81
82 if offset != len(p) {
83 n, err := lb.writer.Write(p[offset:])
84 written += n
85 if err != nil { return written, err }
86 }
87
88 return written, nil
89 }
90
91
92 var fwsWrapErr = fmt.Errorf("header value component too long")
93 var fwsNoneErr = fmt.Errorf("header value too long")
94
95 func fwsWrap(line string, keyLen int, maxLine int) (string, error) {
96
97
98
99
100
101 keyLen += 2
102 if len(line) + keyLen <= maxLine { return line, nil }
103
104 result := make([]string, 0)
105
106 offset := 0
107 start, idx := 0, 0
108 remainingBudget := maxLine - keyLen
109 remainingBudget++
110
111 lineBudgetIncludingIndent := remainingBudget
112
113 for {
114
115
116 idx = strings.IndexByte(line[offset:], ' ')
117
118
119 if (idx >= 0) && (idx <= remainingBudget) {
120
121 remainingBudget -= idx + 1
122 offset += idx + 1
123 continue
124 } else if (idx < 0) && (len(line[start:]) <= lineBudgetIncludingIndent) {
125
126 segment := line[start:]
127 if len(segment) > lineBudgetIncludingIndent { return "", fwsWrapErr }
128 result = append(result, segment)
129 break
130 } else if (idx < 0) {
131
132
133 if offset < 1 {
134
135 return "", fwsWrapErr
136 }
137
138 a := line[start:offset-1]
139 b := line[offset:]
140 if len(a) > lineBudgetIncludingIndent { return "", fwsWrapErr }
141 if len(b) > maxLine - 1 { return "", fwsWrapErr }
142 result = append(result, line[start:offset-1], line[offset:])
143 break
144 } else {
145 if offset < 1 {
146
147 return "", fwsWrapErr
148 }
149
150
151 segment := line[start:offset-1]
152 if len(segment) > lineBudgetIncludingIndent { return "", fwsWrapErr }
153 result = append(result, segment)
154 remainingBudget = maxLine - 1 + 1
155
156 lineBudgetIncludingIndent = remainingBudget
157 start = offset
158 continue
159 }
160 }
161
162
163 return strings.Join(result, "\r\n "), nil
164 }
165
166 func fwsNone(line string, keyLen int, maxLine int) (string, error) {
167 keyLen += 2
168 if len(line) + keyLen <= maxLine { return line, nil }
169 return "", fwsNoneErr
170 }
171
View as plain text