summaryrefslogtreecommitdiff
path: root/dec.c
blob: 60c402649910768e97216a47af654db59ee744e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* -*- mode: c; c-file-style: "k&r" -*-  */

/* gddec.c -- Decode & extract signature from a gdiff-plus stream

   Copyright (C) 2000 by Martin Pool.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */


/* Here's a diagram of the decoding process:

       	       	       	   /---- OLDBODY <--- BODYCACHE
			  v		       ^
   UPSTREAM -chunked-> LTSTREAM	----> BODY ----+----> CLIENT
                \
                 -> SIGNATURE ---> SIGCACHE


   As we read input from upstream, we split the chunked encoding into
   the literal-token stream, and the server-generated signature.  We
   combine the ltstream with the old body to get the new value of the
   body.  This is sent downstream, and also written back into the cache.
   The signature is extracted and written into the signature cache
   so that we can send it up in the next request.
*/


#include "includes.h"

#include "hsync.h"
#include "hsyncproto.h"
#include "private.h"

#define MAXLITDATA	64*1024*1024
#define DEFLITBUFSIZE	1024


static int
_hs_copy(const uint32_t length,
	 hs_read_fn_t read_fn, void *read_priv,
	 hs_write_fn_t write_fn, void *write_priv)
{
    ssize_t ret;
    char *buf;

    buf = malloc(length);

    ret = _hs_read_loop(read_fn, read_priv, buf, length);
    if (ret >= 0 && (ret < (int32_t) length)) {
	errno = ENODATA;
	goto fail;
    }

    ret = _hs_write_loop(write_fn, write_priv, buf, ret);
    if ((unsigned) ret != length)
	goto fail;

    free(buf);
    return length;

  fail:
    free(buf);
    return -1;
}



static int _hs_check_gd_header(hs_read_fn_t ltread_fn, void *ltread_priv)
{
    int ret;
    uint32_t remote_magic, expect;

    expect = HS_LT_MAGIC;

    ret = _hs_read_netint(ltread_fn, ltread_priv, &remote_magic);
    return_val_if_fail(ret == 4, -1);
    if (remote_magic != expect) {
	_hs_fatal("version mismatch: %#010x != %#010x",
		  remote_magic, expect);
	errno = EBADMSG;
	return -1;
    }
    _hs_trace("got version %#010x", remote_magic);
    return 0;
}



ssize_t
hs_decode(hs_readofs_fn_t oldread_fn, void *oldread_priv,
	  hs_write_fn_t write_fn, void *write_priv,
	  hs_read_fn_t ltread_fn, void *ltread_priv,
	  hs_write_fn_t newsig_fn, void *newsig_priv, hs_stats_t * stats)
{
    int ret;
    uint8_t type;
    uint32_t length, offset;
    int kind;
    char *stats_str;

    _hs_trace("**** begin %s", __FUNCTION__);
    bzero(stats, sizeof *stats);
    if (_hs_check_gd_header(ltread_fn, ltread_priv) < 0)
	return -1;

    while (1) {
	 ret = _hs_inhale_command(ltread_fn, ltread_priv, &kind, &length, &offset);
	 if (ret < 0) {
	      _hs_error("error while trying to read command byte");
	      return -1;
	 }

	 if (kind == op_kind_eof) {
	      _hs_trace("op_eof");
	      break;		/* We're done! Cool bananas */
	 } else if (kind == op_kind_literal) {
	      _hs_trace("op_literal len=%d", length);
	      ret = _hs_copy(length, ltread_fn, ltread_priv, write_fn,
			     write_priv);
	      return_val_if_fail(ret >= 0, -1);
	      stats->lit_cmds++;
	      stats->lit_bytes += length;
	 } else if (kind == op_kind_signature) {
	      _hs_trace("op_signature len=%d", length);
	      ret = _hs_copy(length, ltread_fn, ltread_priv, newsig_fn,
			     newsig_priv);
	      return_val_if_fail(ret >= 0, -1);
	      stats->sig_cmds++;
	      stats->sig_bytes += length;
	} else if (kind == op_kind_copy) {
	     _hs_trace("op_copy offset=%d, len=%d", offset, length);
	     ret = _hs_copy_ofs(offset, length,
				oldread_fn, oldread_priv,
				write_fn, write_priv);
	     return_val_if_fail(ret >= 0, -1);
	     stats->copy_cmds++;
	     stats->copy_bytes += length;
	} else if (kind == op_kind_checksum) {
	     _hs_trace("CHECKSUM(len=%d)", length);
	     ret = _hs_check_checksum(ltread_fn, ltread_priv, length, filesum);
	} else {
	    _hs_fatal("unexpected op kind %d!", type);
	}
    }

    if (ret < 0) {
	return ret;
    }

    stats_str = hs_format_stats(stats);
    _hs_trace("completed: %s", stats_str);
    free(stats_str);
    
    return 1;
}