LEFT | RIGHT |
(no file at all) | |
| 1 // Copyright 2010 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 armor |
| 6 |
| 7 import ( |
| 8 "encoding/base64" |
| 9 "io" |
| 10 ) |
| 11 |
| 12 var armorHeaderSep = []byte(": ") |
| 13 var blockEnd = []byte("\n=") |
| 14 var newline = []byte("\n") |
| 15 var armorEndOfLineOut = []byte("-----\n") |
| 16 |
| 17 // writeSlices writes its arguments to the given Writer. |
| 18 func writeSlices(out io.Writer, slices ...[]byte) (err error) { |
| 19 for _, s := range slices { |
| 20 _, err = out.Write(s) |
| 21 if err != nil { |
| 22 return err |
| 23 } |
| 24 } |
| 25 return |
| 26 } |
| 27 |
| 28 // lineBreaker breaks data across several lines, all of the same byte length |
| 29 // (except possibly the last). Lines are broken with a single '\n'. |
| 30 type lineBreaker struct { |
| 31 lineLength int |
| 32 line []byte |
| 33 used int |
| 34 out io.Writer |
| 35 haveWritten bool |
| 36 } |
| 37 |
| 38 func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { |
| 39 return &lineBreaker{ |
| 40 lineLength: lineLength, |
| 41 line: make([]byte, lineLength), |
| 42 used: 0, |
| 43 out: out, |
| 44 } |
| 45 } |
| 46 |
| 47 func (l *lineBreaker) Write(b []byte) (n int, err error) { |
| 48 n = len(b) |
| 49 |
| 50 if n == 0 { |
| 51 return |
| 52 } |
| 53 |
| 54 if l.used == 0 && l.haveWritten { |
| 55 _, err = l.out.Write([]byte{'\n'}) |
| 56 if err != nil { |
| 57 return |
| 58 } |
| 59 } |
| 60 |
| 61 if l.used+len(b) < l.lineLength { |
| 62 l.used += copy(l.line[l.used:], b) |
| 63 return |
| 64 } |
| 65 |
| 66 l.haveWritten = true |
| 67 _, err = l.out.Write(l.line[0:l.used]) |
| 68 if err != nil { |
| 69 return |
| 70 } |
| 71 excess := l.lineLength - l.used |
| 72 l.used = 0 |
| 73 |
| 74 _, err = l.out.Write(b[0:excess]) |
| 75 if err != nil { |
| 76 return |
| 77 } |
| 78 |
| 79 _, err = l.Write(b[excess:]) |
| 80 return |
| 81 } |
| 82 |
| 83 func (l *lineBreaker) Close() (err error) { |
| 84 if l.used > 0 { |
| 85 _, err = l.out.Write(l.line[0:l.used]) |
| 86 if err != nil { |
| 87 return |
| 88 } |
| 89 } |
| 90 |
| 91 return |
| 92 } |
| 93 |
| 94 // encoding keeps track of a running CRC24 over the data which has been written |
| 95 // to it and outputs a OpenPGP checksum when closed, followed by an armor |
| 96 // trailer. |
| 97 // |
| 98 // It's built into a stack of io.Writers: |
| 99 // encoding -> base64 encoder -> lineBreaker -> out |
| 100 type encoding struct { |
| 101 out io.Writer |
| 102 breaker *lineBreaker |
| 103 b64 io.WriteCloser |
| 104 crc uint32 |
| 105 blockType []byte |
| 106 } |
| 107 |
| 108 func (e *encoding) Write(data []byte) (n int, err error) { |
| 109 e.crc = crc24(e.crc, data) |
| 110 return e.b64.Write(data) |
| 111 } |
| 112 |
| 113 func (e *encoding) Close() (err error) { |
| 114 err = e.b64.Close() |
| 115 if err != nil { |
| 116 return |
| 117 } |
| 118 e.breaker.Close() |
| 119 |
| 120 var checksumBytes [3]byte |
| 121 checksumBytes[0] = byte(e.crc >> 16) |
| 122 checksumBytes[1] = byte(e.crc >> 8) |
| 123 checksumBytes[2] = byte(e.crc) |
| 124 |
| 125 var b64ChecksumBytes [4]byte |
| 126 base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) |
| 127 |
| 128 return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorE
nd, e.blockType, armorEndOfLine) |
| 129 } |
| 130 |
| 131 // Encode returns a WriteCloser which will encode the data written to it in |
| 132 // OpenPGP armor. |
| 133 func Encode(out io.Writer, blockType string, headers map[string]string) (w io.Wr
iteCloser, err error) { |
| 134 bType := []byte(blockType) |
| 135 err = writeSlices(out, armorStart, bType, armorEndOfLineOut) |
| 136 if err != nil { |
| 137 return |
| 138 } |
| 139 |
| 140 for k, v := range headers { |
| 141 err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), new
line) |
| 142 if err != nil { |
| 143 return |
| 144 } |
| 145 } |
| 146 |
| 147 _, err = out.Write(newline) |
| 148 if err != nil { |
| 149 return |
| 150 } |
| 151 |
| 152 e := &encoding{ |
| 153 out: out, |
| 154 breaker: newLineBreaker(out, 64), |
| 155 crc: crc24Init, |
| 156 blockType: bType, |
| 157 } |
| 158 e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) |
| 159 return e, nil |
| 160 } |
LEFT | RIGHT |