1package moxio
2
3import (
4 "encoding/base64"
5 "io"
6)
7
8// implement io.Closer
9type closerFunc func() error
10
11func (f closerFunc) Close() error {
12 return f()
13}
14
15// Base64Writer turns a writer for data into one that writes base64 content on
16// \r\n separated lines of max 76+2 characters length.
17func Base64Writer(w io.Writer) io.WriteCloser {
18 lw := &lineWrapper{w: w}
19 bw := base64.NewEncoder(base64.StdEncoding, lw)
20 return struct {
21 io.Writer
22 io.Closer
23 }{
24 Writer: bw,
25 Closer: closerFunc(func() error {
26 if err := bw.Close(); err != nil {
27 return err
28 }
29 return lw.Close()
30 }),
31 }
32}
33
34type lineWrapper struct {
35 w io.Writer
36 n int // Written on current line.
37}
38
39func (lw *lineWrapper) Write(buf []byte) (int, error) {
40 wrote := 0
41 for len(buf) > 0 {
42 // base64 has max 76 data bytes on per line. ../rfc/2045:1372
43 n := min(76-lw.n, len(buf))
44 nn, err := lw.w.Write(buf[:n])
45 if nn > 0 {
46 wrote += nn
47 buf = buf[nn:]
48 }
49 if err != nil {
50 return wrote, err
51 }
52 lw.n += nn
53 if lw.n == 76 {
54 _, err := lw.w.Write([]byte("\r\n"))
55 if err != nil {
56 return wrote, err
57 }
58 lw.n = 0
59 }
60 }
61 return wrote, nil
62}
63
64func (lw *lineWrapper) Close() error {
65 if lw.n > 0 {
66 lw.n = 0
67 _, err := lw.w.Write([]byte("\r\n"))
68 return err
69 }
70 return nil
71}
72