partial DSA support
[gnupg.git] / g10 / compress.c
1 /* compress.c - compress filter
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * GNUPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GNUPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <zlib.h>
29
30 #include "util.h"
31 #include "memory.h"
32 #include "packet.h"
33 #include "filter.h"
34 #include "options.h"
35
36
37 static void
38 init_compress( compress_filter_context_t *zfx, z_stream *zs )
39 {
40     int rc;
41     int level;
42
43
44     if( opt.compress >= 0 && opt.compress <= 9 )
45         level = opt.compress;
46     else if( opt.compress == -1 )
47         level = Z_DEFAULT_COMPRESSION;
48     else if( opt.compress == 10 ) /* remove this ! */
49         level = 0;
50     else {
51         log_error("invalid compression level; using default level\n");
52         level = Z_DEFAULT_COMPRESSION;
53     }
54
55     if( (rc = deflateInit( zs, level )) != Z_OK ) {
56         log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
57                                rc == Z_MEM_ERROR ? "out of core" :
58                                rc == Z_VERSION_ERROR ? "invalid lib version" :
59                                                        "unknown error" );
60     }
61
62     zfx->outbufsize = 4096;
63     zfx->outbuf = m_alloc( zfx->outbufsize );
64 }
65
66 static int
67 do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a )
68 {
69     int zrc;
70     unsigned n;
71
72     do {
73         zs->next_out = zfx->outbuf;
74         zs->avail_out = zfx->outbufsize;
75         zrc = deflate( zs, flush );
76         if( zrc == Z_STREAM_END && flush == Z_FINISH )
77             ;
78         else if( zrc != Z_OK ) {
79             if( zs->msg )
80                 log_fatal("zlib deflate problem: %s\n", zs->msg );
81             else
82                 log_fatal("zlib deflate problem: rc=%d\n", zrc );
83         }
84         n = zfx->outbufsize - zs->avail_out;
85         if( DBG_FILTER )
86             log_debug("deflate returned: avail_in=%u, avail_out=%u, n=%u\n",
87                 (unsigned)zs->avail_in, (unsigned)zs->avail_out, (unsigned)n );
88
89         if( iobuf_write( a, zfx->outbuf, n ) ) {
90             log_debug("deflate: iobuf_write failed\n");
91             return G10ERR_WRITE_FILE;
92         }
93     } while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) );
94     return 0;
95 }
96
97 static void
98 init_uncompress( compress_filter_context_t *zfx, z_stream *zs )
99 {
100     int rc;
101
102     /****************
103      * PGP uses a windowsize of 13 bits. Using a negative value for
104      * it forces zlib not to expect a zlib header.  This is a
105      * undocumented feature, Peter Gutmann told me about.
106      */
107     if( (rc = zfx->pgpmode? inflateInit2( zs, -13)
108                           : inflateInit( zs )) != Z_OK ) {
109         log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
110                                rc == Z_MEM_ERROR ? "out of core" :
111                                rc == Z_VERSION_ERROR ? "invalid lib version" :
112                                                        "unknown error" );
113     }
114
115     zfx->inbufsize = 1024;
116     zfx->inbuf = m_alloc( zfx->inbufsize );
117     zs->avail_in = 0;
118 }
119
120 static int
121 do_uncompress( compress_filter_context_t *zfx, z_stream *zs,
122                IOBUF a, size_t *ret_len )
123 {
124     int zrc;
125     int rc=0;
126     size_t n;
127     byte *p;
128     int c;
129
130     if( DBG_FILTER )
131         log_debug("do_uncompress: avail_in=%u, avail_out=%u\n",
132                 (unsigned)zs->avail_in, (unsigned)zs->avail_out);
133     do {
134         if( zs->avail_in < zfx->inbufsize ) {
135             n = zs->avail_in;
136             if( !n )
137                 zs->next_in = zfx->inbuf;
138             for( p=zfx->inbuf+n; n < zfx->inbufsize; n++, p++ ) {
139                 if( (c=iobuf_get(a)) == -1 )
140                     break;
141                 *p = c & 0xff;
142             }
143             zs->avail_in = n;
144         }
145         zrc = inflate( zs, Z_PARTIAL_FLUSH );
146         if( DBG_FILTER )
147             log_debug("inflate returned: avail_in=%u, avail_out=%u, zrc=%d\n",
148                    (unsigned)zs->avail_in, (unsigned)zs->avail_out, zrc);
149         if( zrc == Z_STREAM_END )
150             rc = -1; /* eof FIXME: return remaining bytes until EOF */
151         else if( zrc != Z_OK ) {
152             if( zs->msg )
153                 log_fatal("zlib inflate problem: %s\n", zs->msg );
154             else
155                 log_fatal("zlib inflate problem: rc=%d\n", zrc );
156         }
157     } while( zs->avail_out && zrc != Z_STREAM_END );
158     *ret_len = zfx->outbufsize - zs->avail_out;
159     if( DBG_FILTER )
160         log_debug("do_uncompress: returning %u bytes\n", (unsigned)*ret_len );
161     return rc;
162 }
163
164 int
165 compress_filter( void *opaque, int control,
166                  IOBUF a, byte *buf, size_t *ret_len)
167 {
168     size_t size = *ret_len;
169     compress_filter_context_t *zfx = opaque;
170     z_stream *zs = zfx->opaque;
171     int rc=0;
172
173     if( control == IOBUFCTRL_UNDERFLOW ) {
174         if( !zfx->status ) {
175             zs = zfx->opaque = m_alloc_clear( sizeof *zs );
176             init_uncompress( zfx, zs );
177             zfx->status = 1;
178         }
179
180         zs->next_out = buf;
181         zs->avail_out = size;
182         zfx->outbufsize = size; /* needed only for calculation */
183         rc = do_uncompress( zfx, zs, a, ret_len );
184     }
185     else if( control == IOBUFCTRL_FLUSH ) {
186         if( !zfx->status ) {
187             PACKET pkt;
188             PKT_compressed cd;
189
190             memset( &cd, 0, sizeof cd );
191             cd.len = 0;
192             cd.algorithm = 2; /* zlib */
193             init_packet( &pkt );
194             pkt.pkttype = PKT_COMPRESSED;
195             pkt.pkt.compressed = &cd;
196             if( build_packet( a, &pkt ))
197                 log_bug("build_packet(PKT_COMPRESSED) failed\n");
198             zs = zfx->opaque = m_alloc_clear( sizeof *zs );
199             init_compress( zfx, zs );
200             zfx->status = 2;
201         }
202
203         zs->next_in = buf;
204         zs->avail_in = size;
205         rc = do_compress( zfx, zs, Z_NO_FLUSH, a );
206     }
207     else if( control == IOBUFCTRL_FREE ) {
208         if( zfx->status == 1 ) {
209             inflateEnd(zs);
210             m_free(zs);
211             zfx->opaque = NULL;
212             m_free(zfx->outbuf); zfx->outbuf = NULL;
213         }
214         else if( zfx->status == 2 ) {
215             zs->next_in = buf;
216             zs->avail_in = 0;
217             do_compress( zfx, zs, Z_FINISH, a );
218             deflateEnd(zs);
219             m_free(zs);
220             zfx->opaque = NULL;
221             m_free(zfx->outbuf); zfx->outbuf = NULL;
222         }
223     }
224     else if( control == IOBUFCTRL_DESC )
225         *(char**)buf = "compress_filter";
226     return rc;
227 }
228
229 /****************
230  * Handle a compressed packet
231  */
232 int
233 handle_compressed( PKT_compressed *cd,
234                    int (*callback)(IOBUF, void *), void *passthru )
235 {
236     compress_filter_context_t cfx;
237     int rc;
238
239     memset( &cfx, 0, sizeof cfx );
240     if( cd->algorithm == 1 )
241         cfx.pgpmode = 1;
242     else if( cd->algorithm != 2  )
243         return G10ERR_COMPR_ALGO;
244
245     iobuf_push_filter( cd->buf, compress_filter, &cfx );
246     if( callback )
247         rc = callback(cd->buf, passthru );
248     else
249         rc = proc_packets(cd->buf);
250     iobuf_pop_filter( cd->buf, compress_filter, &cfx );
251   #if 0
252     if( cd->len )
253         iobuf_set_limit( cd->buf, 0 ); /* disable the readlimit */
254     else
255         iobuf_clear_eof( cd->buf );
256   #endif
257     cd->buf = NULL;
258     return rc;
259 }
260