utf8ify_rfc822_string() is in libcitadel now
[citadel.git] / libcitadel / lib / base64.c
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.
7 //
8 // Copyright (c) 1987-2022 by the citadel.org team
9 //
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.
12
13
14 #define _GNU_SOURCE
15 #include "sysdep.h"
16 #include <ctype.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <sys/select.h>
23 #include <fcntl.h>
24 #include <sys/types.h>
25 #include "libcitadel.h"
26
27
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;
38         char encodebuf[3];
39         size_t bytes_output = 0;
40
41         while (bytes_processed < sourcelen) {
42                 size_t remain = sourcelen - bytes_processed;
43                 if (remain >= 3) {
44                         memcpy(encodebuf, ptr, 3);
45                         ptr += 3;
46                         bytes_processed += 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)] 
52                         );
53                         bytes_output += 4;
54                                 
55                 }
56                 else if (remain == 2) {
57                         memcpy(encodebuf, ptr, 2);
58                         encodebuf[2] = 0;
59                         ptr += 2;
60                         bytes_processed += 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) )]
65                         );
66                         bytes_output += 4;
67                 }
68                 else if (remain == 1) {
69                         memcpy(encodebuf, ptr, 1);
70                         encodebuf[1] = 0;
71                         encodebuf[2] = 0;
72                         ptr += 1;
73                         bytes_processed += 1;
74                         sprintf(&dest[bytes_output], "%c%c==",
75                                 alphabet[((encodebuf[0] & 0xFC) >> 2)],
76                                 alphabet[( ((encodebuf[0] & 0x03) << 4) )]
77                         );
78                         bytes_output += 4;
79                 }
80                 if ( ((bytes_processed % 57) == 0) || (bytes_processed >= sourcelen) ) {
81                         if (linebreaks) {
82                                 sprintf(&dest[bytes_output], "\r\n");
83                                 bytes_output += 2;
84                         }
85                 }
86
87         }
88
89         return bytes_output;
90 }
91
92
93 // convert base64 alphabet characters to 6-bit decimal values
94 char b64unalphabet(char ch) {
95         if (isupper(ch)) {
96                 return(ch - 'A');
97         }
98         else if (islower(ch)) {
99                 return(ch - 'a' + 26);
100         }
101         else if (isdigit(ch)) {
102                 return(ch - '0' + 52);
103         }
104         else if (ch == '+') {
105                 return(62);
106         }
107         else if (ch == '/') {
108                 return(63);
109         }
110         else if (ch == '=') {                   // this character marks the end of an encoded block
111                 return(64);
112         }
113         else {                                  // anything else is an invalid character
114                 return(65);
115         }
116 }
117
118
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;
127         int decodepos = 0;
128         char decodebuf[4];
129
130         while (bytes_read < source_len) {
131
132                 char ch = b64unalphabet(source[bytes_read++]);
133                 if (ch < 65) {
134                         decodebuf[decodepos++] = ch;
135                 }
136                 if (decodepos == 4) {                   // we have a quartet of encoded bytes now
137                         decodepos = 0;
138                         int increment = 0;              // number of decoded bytes found in this encoded quartet
139
140                         ch = decodebuf[0];
141                         if (ch != 64) {
142                                 dest[bytes_decoded] = ch << 2;
143                         }
144
145                         ch = decodebuf[1];
146                         if (ch != 64) {
147                                 dest[bytes_decoded] |= ((ch & 0x30) >> 4);
148                                 dest[bytes_decoded+1] = ((ch & 0x0F) << 4);
149                                 increment = 1;
150                         }
151
152                         ch = decodebuf[2];
153                         if (ch != 64) {
154                                 dest[bytes_decoded+1] |= ((ch & 0x3C) >> 2);
155                                 dest[bytes_decoded+2] = ((ch & 0x03) << 6);
156                                 increment = 2;
157                         }
158
159                         ch = decodebuf[3];
160                         if (ch != 64) {
161                                 dest[bytes_decoded+2] |= (ch & 0x3F);
162                                 increment = 3;
163                         }
164
165                         bytes_decoded += increment;
166                 }
167         }
168
169         return bytes_decoded;
170 }