...

Source file src/mime/quotedprintable/writer.go

Documentation: mime/quotedprintable

		 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