...

Source file src/pkg/mime/quotedprintable/writer.go

     1	// Copyright 2015 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package quotedprintable
     6	
     7	import "io"
     8	
     9	const lineMaxLen = 76
    10	
    11	// A Writer is a quoted-printable writer that implements io.WriteCloser.
    12	type Writer struct {
    13		// Binary mode treats the writer's input as pure binary and processes end of
    14		// line bytes as binary data.
    15		Binary bool
    16	
    17		w    io.Writer
    18		i    int
    19		line [78]byte
    20		cr   bool
    21	}
    22	
    23	// NewWriter returns a new Writer that writes to w.
    24	func NewWriter(w io.Writer) *Writer {
    25		return &Writer{w: w}
    26	}
    27	
    28	// Write encodes p using quoted-printable encoding and writes it to the
    29	// underlying io.Writer. It limits line length to 76 characters. The encoded
    30	// bytes are not necessarily flushed until the Writer is closed.
    31	func (w *Writer) Write(p []byte) (n int, err error) {
    32		for i, b := range p {
    33			switch {
    34			// Simple writes are done in batch.
    35			case b >= '!' && b <= '~' && b != '=':
    36				continue
    37			case isWhitespace(b) || !w.Binary && (b == '\n' || b == '\r'):
    38				continue
    39			}
    40	
    41			if i > n {
    42				if err := w.write(p[n:i]); err != nil {
    43					return n, err
    44				}
    45				n = i
    46			}
    47	
    48			if err := w.encode(b); err != nil {
    49				return n, err
    50			}
    51			n++
    52		}
    53	
    54		if n == len(p) {
    55			return n, nil
    56		}
    57	
    58		if err := w.write(p[n:]); err != nil {
    59			return n, err
    60		}
    61	
    62		return len(p), nil
    63	}
    64	
    65	// Close closes the Writer, flushing any unwritten data to the underlying
    66	// io.Writer, but does not close the underlying io.Writer.
    67	func (w *Writer) Close() error {
    68		if err := w.checkLastByte(); err != nil {
    69			return err
    70		}
    71	
    72		return w.flush()
    73	}
    74	
    75	// write limits text encoded in quoted-printable to 76 characters per line.
    76	func (w *Writer) write(p []byte) error {
    77		for _, b := range p {
    78			if b == '\n' || b == '\r' {
    79				// If the previous byte was \r, the CRLF has already been inserted.
    80				if w.cr && b == '\n' {
    81					w.cr = false
    82					continue
    83				}
    84	
    85				if b == '\r' {
    86					w.cr = true
    87				}
    88	
    89				if err := w.checkLastByte(); err != nil {
    90					return err
    91				}
    92				if err := w.insertCRLF(); err != nil {
    93					return err
    94				}
    95				continue
    96			}
    97	
    98			if w.i == lineMaxLen-1 {
    99				if err := w.insertSoftLineBreak(); err != nil {
   100					return err
   101				}
   102			}
   103	
   104			w.line[w.i] = b
   105			w.i++
   106			w.cr = false
   107		}
   108	
   109		return nil
   110	}
   111	
   112	func (w *Writer) encode(b byte) error {
   113		if lineMaxLen-1-w.i < 3 {
   114			if err := w.insertSoftLineBreak(); err != nil {
   115				return err
   116			}
   117		}
   118	
   119		w.line[w.i] = '='
   120		w.line[w.i+1] = upperhex[b>>4]
   121		w.line[w.i+2] = upperhex[b&0x0f]
   122		w.i += 3
   123	
   124		return nil
   125	}
   126	
   127	const upperhex = "0123456789ABCDEF"
   128	
   129	// checkLastByte encodes the last buffered byte if it is a space or a tab.
   130	func (w *Writer) checkLastByte() error {
   131		if w.i == 0 {
   132			return nil
   133		}
   134	
   135		b := w.line[w.i-1]
   136		if isWhitespace(b) {
   137			w.i--
   138			if err := w.encode(b); err != nil {
   139				return err
   140			}
   141		}
   142	
   143		return nil
   144	}
   145	
   146	func (w *Writer) insertSoftLineBreak() error {
   147		w.line[w.i] = '='
   148		w.i++
   149	
   150		return w.insertCRLF()
   151	}
   152	
   153	func (w *Writer) insertCRLF() error {
   154		w.line[w.i] = '\r'
   155		w.line[w.i+1] = '\n'
   156		w.i += 2
   157	
   158		return w.flush()
   159	}
   160	
   161	func (w *Writer) flush() error {
   162		if _, err := w.w.Write(w.line[:w.i]); err != nil {
   163			return err
   164		}
   165	
   166		w.i = 0
   167		return nil
   168	}
   169	
   170	func isWhitespace(b byte) bool {
   171		return b == ' ' || b == '\t'
   172	}
   173	

View as plain text