[svn-upgrade] Integrating new upstream version, iodine (0.4.0)
[debian/iodine.git] / src / encoding.c
1 /*
2  * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <stdio.h>
18 #include <strings.h>
19 #include <string.h>
20
21 /* For FreeBSD */
22 #ifndef MIN
23 #define MIN(a,b) ((a)<(b)?(a):(b))
24 #endif
25
26 #define SPACING 63
27 #define ENC_CHUNK 8
28 #define RAW_CHUNK 5
29
30 static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ98765-";
31 static const char padder[] = " 1234";
32 static char reverse32[128];
33 static int reverse_init = 0;
34
35 /* Eat 5 bytes from src, write 8 bytes to dest */
36 static void
37 encode_chunk(char *dest, const char *src)
38 {
39         unsigned char c;
40
41         *dest++ = base32[(*src & 0xF8) >> 3];   /* 1111 1000 first byte */
42
43         c = (*src++ & 0x07) << 2;               /* 0000 0111 first byte */
44         c |=  ((*src & 0xC0) >> 6);             /* 1100 0000 second byte */
45         *dest++ = base32[(int) c];
46
47         *dest++ = base32[(*src & 0x3E) >> 1];   /* 0011 1110 second byte */
48
49         c = (*src++ & 0x01) << 4;               /* 0000 0001 second byte */
50         c |=  ((*src & 0xF0) >> 4);             /* 1111 0000 third byte */
51         *dest++ = base32[(int) c];
52         
53         c = (*src++ & 0x0F) << 1;               /* 0000 1111 third byte */
54         c |=  ((*src & 0x80) >> 7);             /* 1000 0000 fourth byte */
55         *dest++ = base32[(int) c];
56         
57         *dest++ = base32[(*src & 0x7C) >> 2];   /* 0111 1100 fourth byte */
58
59         c = (*src++ & 0x03) << 3;               /* 0000 0011 fourth byte */
60         c |=  ((*src & 0xE0) >> 5);             /* 1110 0000 fifth byte */
61         *dest++ = base32[(int) c];
62
63         *dest++ = base32[*src++ & 0x1F];        /* 0001 1111 fifth byte */
64 }
65
66 /* Eat 8 bytes from src, write 5 bytes to dest */
67 static void
68 decode_chunk(char *dest, char *src)
69 {
70         unsigned char c;
71         int i;
72
73         if (!reverse_init) {
74                 for (i = 0; i < 32; i++) {
75                         c = base32[i];
76                         reverse32[(int) c] = i;
77                 }
78                 reverse_init = 1;
79         }
80
81         c = reverse32[(int) *src++] << 3;               /* Take bits 11111 from byte 1 */
82         c |= (reverse32[(int) *src] & 0x1C) >> 2;       /* Take bits 11100 from byte 2 */
83         *dest++ = c;
84         
85         c = (reverse32[(int) *src++] & 0x3) << 6;       /* Take bits 00011 from byte 2 */
86         c |= reverse32[(int) *src++] << 1;              /* Take bits 11111 from byte 3 */
87         c |= (reverse32[(int) *src] & 0x10) >> 4;       /* Take bits 10000 from byte 4 */
88         *dest++ = c;
89         
90         c = (reverse32[(int) *src++] & 0xF) << 4;       /* Take bits 01111 from byte 4 */
91         c |= (reverse32[(int) *src] & 0x1E) >> 1;       /* Take bits 11110 from byte 5 */
92         *dest++ = c;
93         
94         c = reverse32[(int) *src++] << 7;               /* Take bits 00001 from byte 5 */
95         c |= reverse32[(int) *src++] << 2;              /* Take bits 11111 from byte 6 */
96         c |= (reverse32[(int) *src] & 0x18) >> 3;       /* Take bits 11000 from byte 7 */
97         *dest++ = c;
98         
99         c = (reverse32[(int) *src++] & 0x7) << 5;       /* Take bits 00111 from byte 7 */
100         c |= reverse32[(int) *src++];                   /* Take bits 11111 from byte 8 */
101         *dest++ = c;
102 }
103
104 int
105 encode_data(const char *buf, const size_t len, int space, char *dest)
106 {
107         int final;
108         int write;
109         int realwrite;
110         int chunks;
111         int leftovers;
112         int i;
113         char encoded[255];
114         char padding[5];
115         const char *dp;
116         char *pp;
117         char *ep;
118
119         space -= space / SPACING;
120         chunks = (space - 1) / ENC_CHUNK;
121         while ((chunks + 1) * ENC_CHUNK + 1 > space) {
122                 chunks--;
123         }
124         write = RAW_CHUNK * chunks;
125         write = MIN(write, len); /* do not use more bytes than is available; */
126         final = (write == len); /* is this the last block? */
127         chunks = write / RAW_CHUNK;
128         leftovers = write % RAW_CHUNK;
129
130         memset(encoded, 0, sizeof(encoded));
131         ep = encoded;
132         dp = buf;
133         for (i = 0; i < chunks; i++) {
134                 encode_chunk(ep, dp);
135                 ep += ENC_CHUNK;
136                 dp += RAW_CHUNK;
137         }
138         realwrite = ENC_CHUNK * chunks;
139         memset(padding, 0, sizeof(padding));
140         pp = padding;
141         if (leftovers) {
142                 pp += RAW_CHUNK - leftovers;
143                 memcpy(pp, dp, leftovers);
144
145                 pp = padding;
146                 *ep++ = padder[leftovers];
147                 encode_chunk(ep, pp);
148                 
149                 realwrite += ENC_CHUNK + 1;     /* plus padding character */
150         }
151         ep = encoded;
152         if (len > 0) {
153                 for (i = 1; i <= realwrite; i++) {
154                         if (i % SPACING == 0) {
155                                 *dest++ = '.';
156                         }
157                         *dest++ = *ep++;
158                 }
159         }
160         
161         return write;
162 }
163
164 int
165 decode_data(char *dest, int size, const char *src, char *srcend)
166 {
167         int len;
168         int i;
169         int chunks;
170         int padded;
171         char encoded[255];
172         char padding[5];
173         int enclen;
174         char *pp;
175         char *ep;
176
177         memset(encoded, 0, sizeof(encoded));
178         memset(dest, 0, size);
179
180         /* First byte is not encoded */
181         *dest++ = *src++;
182         len = 1;
183
184         ep = encoded;
185         enclen = 0;
186         while(enclen < sizeof(encoded) && src < srcend) {
187                 if(*src == '.') {
188                         src++;
189                         continue;
190                 }
191
192                 *ep++ = *src++;
193                 enclen++;
194         }
195         chunks = enclen / 8;
196         padded = enclen % 8;
197
198         ep = encoded;
199         for (i = 0; i < chunks-1; i++) {
200                 decode_chunk(dest, ep);
201                 dest += RAW_CHUNK;
202                 ep += ENC_CHUNK;
203                 len += RAW_CHUNK;
204         }
205         /* Read last chunk */
206         if (padded) {
207                 pp = padding;
208                 padded = *ep++ - '0';
209                 decode_chunk(pp, ep);
210                 pp += RAW_CHUNK - padded;
211                 memcpy(dest, pp, padded);
212                 len += padded;
213         } else {
214                 decode_chunk(dest, ep);
215                 len += RAW_CHUNK;
216         }
217
218         return len;
219 }
220