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. Use, duplication, or disclosure
11 // is subject to the terms of the GNU General Public License, version 3.
22 #include <sys/select.h>
24 #include <sys/types.h>
25 #include "libcitadel.h"
28 // Encode raw binary data into base64
29 // dest Destination buffer supplied by the caller
30 // source Source binary data to be encoded
31 // sourcelen Stop after reading this many bytes
32 // linebreaks If nonzero, insert CRLF after every 76 bytes
33 // return value The length of the encoded data, not including the null terminator
34 size_t CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen, int linebreaks) {
35 static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
36 size_t bytes_processed = 0;
37 char *ptr = (char *)source;
39 size_t bytes_output = 0;
41 while (bytes_processed < sourcelen) {
42 size_t remain = sourcelen - bytes_processed;
44 memcpy(encodebuf, ptr, 3);
47 sprintf(&dest[bytes_output], "%c%c%c%c",
48 alphabet[((encodebuf[0] & 0xFC) >> 2)],
49 alphabet[( ((encodebuf[0] & 0x03) << 4) | ((encodebuf[1] & 0xF0) >> 4) )],
50 alphabet[( ((encodebuf[1] & 0x0F) << 2) | ((encodebuf[2] & 0xC0) >> 6) )],
51 alphabet[(encodebuf[2] & 0x3F)]
56 else if (remain == 2) {
57 memcpy(encodebuf, ptr, 2);
61 sprintf(&dest[bytes_output], "%c%c%c=",
62 alphabet[((encodebuf[0] & 0xFC) >> 2)],
63 alphabet[( ((encodebuf[0] & 0x03) << 4) | ((encodebuf[1] & 0xF0) >> 4) )],
64 alphabet[( ((encodebuf[1] & 0x0F) << 2) )]
68 else if (remain == 1) {
69 memcpy(encodebuf, ptr, 1);
74 sprintf(&dest[bytes_output], "%c%c==",
75 alphabet[((encodebuf[0] & 0xFC) >> 2)],
76 alphabet[( ((encodebuf[0] & 0x03) << 4) )]
80 if ( ((bytes_processed % 57) == 0) || (bytes_processed >= sourcelen) ) {
82 sprintf(&dest[bytes_output], "\r\n");
93 // convert base64 alphabet characters to 6-bit decimal values
94 char b64unalphabet(char ch) {
98 else if (islower(ch)) {
99 return(ch - 'a' + 26);
101 else if (isdigit(ch)) {
102 return(ch - '0' + 52);
104 else if (ch == '+') {
107 else if (ch == '/') {
110 else if (ch == '=') { // this character marks the end of an encoded block
113 else { // anything else is an invalid character
119 // Decode base64 back to binary
120 // dest Destination buffer
121 // source Source base64-encoded buffer
122 // source_len Stop after parsing this many bytes
123 // return value Decoded length
124 size_t CtdlDecodeBase64(char *dest, const char *source, size_t source_len) {
125 size_t bytes_read = 0;
126 size_t bytes_decoded = 0;
130 while (bytes_read < source_len) {
132 char ch = b64unalphabet(source[bytes_read++]);
134 decodebuf[decodepos++] = ch;
136 if (decodepos == 4) { // we have a quartet of encoded bytes now
138 int increment = 0; // number of decoded bytes found in this encoded quartet
142 dest[bytes_decoded] = ch << 2;
147 dest[bytes_decoded] |= ((ch & 0x30) >> 4);
148 dest[bytes_decoded+1] = ((ch & 0x0F) << 4);
154 dest[bytes_decoded+1] |= ((ch & 0x3C) >> 2);
155 dest[bytes_decoded+2] = ((ch & 0x03) << 6);
161 dest[bytes_decoded+2] |= (ch & 0x3F);
165 bytes_decoded += increment;
169 return bytes_decoded;