382f1519370d77cd33f473023f493571caf0a53a
[paraslash.git] / base64.c
1 /*
2  * The code in this file was taken from openssh-5.2p1, Copyright (c) 1996 by
3  * Internet Software Consortium.  Portions Copyright (c) 1995 by International
4  * Business Machines, Inc.
5  */
6
7 /** \file base64.c Uudecode and base64decode implementation. */
8
9 #include <regex.h>
10
11 #include "para.h"
12 #include "error.h"
13 #include "base64.h"
14 #include "string.h"
15
16 static const char Base64[] =
17         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
18 static const char Pad64 = '=';
19
20 /** Maximal possible size of the decoded data. */
21 #define BASE64_MAX_DECODED_SIZE(_encoded_size) ((_encoded_size) / 4 * 3)
22
23 /**
24  * base64-decode a buffer.
25  *
26  * \param src The buffer to decode.
27  * \param encoded_size The special value -1 means: look for terminating zero byte.
28  * \param result Points to dynamically allocated target buffer on success.
29  * \param decoded_size Number of bytes written to \a result.
30  *
31  * Skips all whitespace anywhere. Converts characters, four at a time, starting
32  * at (or after) src from base - 64 numbers into three 8 bit bytes in the
33  * target area.
34  *
35  * It is OK to pass a \p NULL pointer as \a decoded_size. The result is
36  * terminated with a zero byte.
37  *
38  * \return Standard. The contents of result \a and \a decoded_size are
39  * undefined on failure.
40  */
41 int base64_decode(char const *src, size_t encoded_size, char **result,
42                 size_t *decoded_size)
43 {
44         unsigned int tarindex, state;
45         int ch;
46         char *pos;
47         const char *end = src + encoded_size;
48         unsigned char *target;
49
50         if (encoded_size == (size_t)-1)
51                 encoded_size = strlen(src);
52         target = para_malloc(BASE64_MAX_DECODED_SIZE(encoded_size) + 1);
53
54         state = 0;
55         tarindex = 0;
56
57         while (src < end) {
58                 ch = *src++;
59                 if (para_isspace(ch)) /* Skip whitespace anywhere. */
60                         continue;
61
62                 if (ch == Pad64)
63                         break;
64
65                 pos = strchr(Base64, ch);
66                 if (pos == NULL) /* A non-base64 character. */
67                         goto fail;
68
69                 switch (state) {
70                 case 0:
71                         target[tarindex] = (pos - Base64) << 2;
72                         state = 1;
73                         break;
74                 case 1:
75                         target[tarindex] |= (pos - Base64) >> 4;
76                         target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
77                         tarindex++;
78                         state = 2;
79                         break;
80                 case 2:
81                         target[tarindex] |= (pos - Base64) >> 2;
82                         target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
83                         tarindex++;
84                         state = 3;
85                         break;
86                 case 3:
87                         target[tarindex] |= pos - Base64;
88                         tarindex++;
89                         state = 0;
90                         break;
91                 }
92         }
93
94         /*
95          * We are done decoding Base-64 chars.  Let's see if we ended
96          * on a byte boundary, and/or with erroneous trailing characters.
97          */
98
99         if (*src == Pad64) {            /* We got a pad char. */
100                 ch = *src++;            /* Skip it, get next. */
101                 switch (state) {
102                 case 0:         /* Invalid = in first position */
103                 case 1:         /* Invalid = in second position */
104                         goto fail;
105
106                 case 2:         /* Valid, means one byte of info */
107                         /* Skip any number of spaces. */
108                         for (; ch != '\0'; ch = *src++)
109                                 if (!isspace(ch))
110                                         break;
111                         /* Make sure there is another trailing = sign. */
112                         if (ch != Pad64)
113                                 goto fail;
114                         ch = *src++;            /* Skip the = */
115                         /* Fall through to "single trailing =" case. */
116                         /* FALLTHROUGH */
117
118                 case 3:         /* Valid, means two bytes of info */
119                         /*
120                          * We know this char is an =.  Is there anything but
121                          * whitespace after it?
122                          */
123                         for (; ch != '\0'; ch = *src++)
124                                 if (!isspace(ch))
125                                         goto fail;
126
127                         /*
128                          * Now make sure for cases 2 and 3 that the "extra"
129                          * bits that slopped past the last full byte were
130                          * zeros.  If we don't check them, they become a
131                          * subliminal channel.
132                          */
133                         if (target[tarindex] != 0)
134                                 goto fail;
135                 }
136         } else {
137                 /*
138                  * We ended by seeing the end of the string.  Make sure we
139                  * have no partial bytes lying around.
140                  */
141                 if (state != 0)
142                         goto fail;
143         }
144         /* success */
145         target[tarindex] = '\0'; /* just to be sure */
146         *result = (char *)target;
147         if (decoded_size)
148                 *decoded_size = tarindex;
149         return 1;
150 fail:
151         free(target);
152         return -E_BASE64;
153 }
154
155 /**
156  * Decode a buffer using the uuencode Base64 algorithm.
157  *
158  * \param src The buffer to decode.
159  * \param encoded_size Number of input bytes in the source buffer.
160  * \param result Contains the decoded data on success.
161  * \param decoded_size Number of output bytes on success.
162  *
163  * This is just a simple wrapper for \ref base64_decode() which strips
164  * whitespace.
165  *
166  * \return The return value of the underlying call to \ref base64_decode().
167  *
168  * \sa uuencode(1), uudecode(1).
169  */
170 int uudecode(char const *src, size_t encoded_size, char **result,
171                 size_t *decoded_size)
172 {
173         const char *end = src + encoded_size, *p;
174
175         /* skip whitespace and data */
176         for (p = src; p < end && (*p == ' ' || *p == '\t'); p++)
177                 ;
178         for (; p < end && *p != '\0' && *p != ' ' && *p != '\t'; p++)
179                 ;
180         /* and remove trailing whitespace because base64_decode needs this */
181         return base64_decode(src, p - src, result, decoded_size);
182 }