1 // This is a simple implementation of a Base64 encoder/decoder as specified
2 // in RFC 2045 section 6.8. In the past, someone tried to make this "elegant"
3 // and in the process they made it broken when certain conditions exist. If
4 // you are reading this and it isn't broken, don't try to improve it. It works
5 // and I don't want to fix it again. I don't care how many nanoseconds you think
6 // you can shave off the execution time. Don't fucking touch it.
8 // Copyright (c) 1987-2022 by the citadel.org team
10 // This program is open source software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 3 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <sys/select.h>
34 #include <sys/types.h>
35 #include "libcitadel.h"
38 // Encode raw binary data into base64
39 // dest Destination buffer supplied by the caller
40 // source Source binary data to be encoded
41 // sourcelen Stop after reading this many bytes
42 // linebreaks If nonzero, insert CRLF after every 76 bytes
43 // return value The length of the encoded data, not including the null terminator
44 size_t CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen, int linebreaks) {
45 static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
46 size_t bytes_processed = 0;
47 char *ptr = (char *)source;
49 size_t bytes_output = 0;
51 while (bytes_processed < sourcelen) {
52 size_t remain = sourcelen - bytes_processed;
54 memcpy(encodebuf, ptr, 3);
57 sprintf(&dest[bytes_output], "%c%c%c%c",
58 alphabet[((encodebuf[0] & 0xFC) >> 2)],
59 alphabet[( ((encodebuf[0] & 0x03) << 4) | ((encodebuf[1] & 0xF0) >> 4) )],
60 alphabet[( ((encodebuf[1] & 0x0F) << 2) | ((encodebuf[2] & 0xC0) >> 6) )],
61 alphabet[(encodebuf[2] & 0x3F)]
66 else if (remain == 2) {
67 memcpy(encodebuf, ptr, 2);
71 sprintf(&dest[bytes_output], "%c%c%c=",
72 alphabet[((encodebuf[0] & 0xFC) >> 2)],
73 alphabet[( ((encodebuf[0] & 0x03) << 4) | ((encodebuf[1] & 0xF0) >> 4) )],
74 alphabet[( ((encodebuf[1] & 0x0F) << 2) )]
78 else if (remain == 1) {
79 memcpy(encodebuf, ptr, 1);
84 sprintf(&dest[bytes_output], "%c%c==",
85 alphabet[((encodebuf[0] & 0xFC) >> 2)],
86 alphabet[( ((encodebuf[0] & 0x03) << 4) )]
90 if ( ((bytes_processed % 57) == 0) || (bytes_processed >= sourcelen) ) {
91 sprintf(&dest[bytes_output], "\r\n");
101 // convert base64 alphabet characters to 6-bit decimal values
102 char unalphabet(char ch) {
106 else if (islower(ch)) {
107 return(ch - 'a' + 26);
109 else if (isdigit(ch)) {
110 return(ch - '0' + 52);
112 else if (ch == '+') {
115 else if (ch == '/') {
118 else if (ch == '=') { // this character marks the end of an encoded block
121 else { // anything else is an invalid character
127 // Decode base64 back to binary
128 // dest Destination buffer
129 // source Source base64-encoded buffer
130 // source_len Stop after parsing this many bytes
131 // return value Decoded length
132 size_t CtdlDecodeBase64(char *dest, const char *source, size_t source_len) {
133 size_t bytes_read = 0;
134 size_t bytes_decoded = 0;
138 while (bytes_read < source_len) {
140 char ch = unalphabet(source[bytes_read++]);
142 decodebuf[decodepos++] = ch;
144 if (decodepos == 4) { // we have a quartet of encoded bytes now
146 int increment = 0; // number of decoded bytes found in this encoded quartet
150 dest[bytes_decoded] = ch << 2;
155 dest[bytes_decoded] |= ((ch & 0x30) >> 4);
156 dest[bytes_decoded+1] = ((ch & 0x0F) << 4);
162 dest[bytes_decoded+1] |= ((ch & 0x3C) >> 2);
163 dest[bytes_decoded+2] = ((ch & 0x03) << 6);
169 dest[bytes_decoded+2] |= (ch & 0x3F);
173 bytes_decoded += increment;
177 return bytes_decoded;