]>
Commit | Line | Data |
---|---|---|
c680a8e1 RS |
1 | // Copyright 2011 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 packet | |
6 | ||
7 | import ( | |
8 | "compress/bzip2" | |
9 | "compress/flate" | |
10 | "compress/zlib" | |
11 | "golang.org/x/crypto/openpgp/errors" | |
12 | "io" | |
13 | "strconv" | |
14 | ) | |
15 | ||
16 | // Compressed represents a compressed OpenPGP packet. The decompressed contents | |
17 | // will contain more OpenPGP packets. See RFC 4880, section 5.6. | |
18 | type Compressed struct { | |
19 | Body io.Reader | |
20 | } | |
21 | ||
22 | const ( | |
23 | NoCompression = flate.NoCompression | |
24 | BestSpeed = flate.BestSpeed | |
25 | BestCompression = flate.BestCompression | |
26 | DefaultCompression = flate.DefaultCompression | |
27 | ) | |
28 | ||
29 | // CompressionConfig contains compressor configuration settings. | |
30 | type CompressionConfig struct { | |
31 | // Level is the compression level to use. It must be set to | |
32 | // between -1 and 9, with -1 causing the compressor to use the | |
33 | // default compression level, 0 causing the compressor to use | |
34 | // no compression and 1 to 9 representing increasing (better, | |
35 | // slower) compression levels. If Level is less than -1 or | |
36 | // more then 9, a non-nil error will be returned during | |
37 | // encryption. See the constants above for convenient common | |
38 | // settings for Level. | |
39 | Level int | |
40 | } | |
41 | ||
42 | func (c *Compressed) parse(r io.Reader) error { | |
43 | var buf [1]byte | |
44 | _, err := readFull(r, buf[:]) | |
45 | if err != nil { | |
46 | return err | |
47 | } | |
48 | ||
49 | switch buf[0] { | |
50 | case 1: | |
51 | c.Body = flate.NewReader(r) | |
52 | case 2: | |
53 | c.Body, err = zlib.NewReader(r) | |
54 | case 3: | |
55 | c.Body = bzip2.NewReader(r) | |
56 | default: | |
57 | err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) | |
58 | } | |
59 | ||
60 | return err | |
61 | } | |
62 | ||
63 | // compressedWriterCloser represents the serialized compression stream | |
64 | // header and the compressor. Its Close() method ensures that both the | |
65 | // compressor and serialized stream header are closed. Its Write() | |
66 | // method writes to the compressor. | |
67 | type compressedWriteCloser struct { | |
68 | sh io.Closer // Stream Header | |
69 | c io.WriteCloser // Compressor | |
70 | } | |
71 | ||
72 | func (cwc compressedWriteCloser) Write(p []byte) (int, error) { | |
73 | return cwc.c.Write(p) | |
74 | } | |
75 | ||
76 | func (cwc compressedWriteCloser) Close() (err error) { | |
77 | err = cwc.c.Close() | |
78 | if err != nil { | |
79 | return err | |
80 | } | |
81 | ||
82 | return cwc.sh.Close() | |
83 | } | |
84 | ||
85 | // SerializeCompressed serializes a compressed data packet to w and | |
86 | // returns a WriteCloser to which the literal data packets themselves | |
87 | // can be written and which MUST be closed on completion. If cc is | |
88 | // nil, sensible defaults will be used to configure the compression | |
89 | // algorithm. | |
90 | func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) { | |
91 | compressed, err := serializeStreamHeader(w, packetTypeCompressed) | |
92 | if err != nil { | |
93 | return | |
94 | } | |
95 | ||
96 | _, err = compressed.Write([]byte{uint8(algo)}) | |
97 | if err != nil { | |
98 | return | |
99 | } | |
100 | ||
101 | level := DefaultCompression | |
102 | if cc != nil { | |
103 | level = cc.Level | |
104 | } | |
105 | ||
106 | var compressor io.WriteCloser | |
107 | switch algo { | |
108 | case CompressionZIP: | |
109 | compressor, err = flate.NewWriter(compressed, level) | |
110 | case CompressionZLIB: | |
111 | compressor, err = zlib.NewWriterLevel(compressed, level) | |
112 | default: | |
113 | s := strconv.Itoa(int(algo)) | |
114 | err = errors.UnsupportedError("Unsupported compression algorithm: " + s) | |
115 | } | |
116 | if err != nil { | |
117 | return | |
118 | } | |
119 | ||
120 | literaldata = compressedWriteCloser{compressed, compressor} | |
121 | ||
122 | return | |
123 | } |