Integrated the DKIM signer into serv_smtpclient, but disabled it
[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-2024 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
125 // AWESOME SAUCE ALERT:
126 // It is legal to specify the same pointer for the source and destination buffers.
127 // If you do so, the string will be "decoded in place".
128
129 size_t CtdlDecodeBase64(char *dest, const char *source, size_t source_len) {
130         size_t bytes_read = 0;
131         size_t bytes_decoded = 0;
132         int decodepos = 0;
133         char decodebuf[4];
134
135         while (bytes_read < source_len) {
136
137                 char ch = b64unalphabet(source[bytes_read++]);
138                 if (ch < 65) {
139                         decodebuf[decodepos++] = ch;
140                 }
141                 if (decodepos == 4) {                   // we have a quartet of encoded bytes now
142                         decodepos = 0;
143                         int increment = 0;              // number of decoded bytes found in this encoded quartet
144
145                         ch = decodebuf[0];
146                         if (ch != 64) {
147                                 dest[bytes_decoded] = ch << 2;
148                         }
149
150                         ch = decodebuf[1];
151                         if (ch != 64) {
152                                 dest[bytes_decoded] |= ((ch & 0x30) >> 4);
153                                 dest[bytes_decoded+1] = ((ch & 0x0F) << 4);
154                                 increment = 1;
155                         }
156
157                         ch = decodebuf[2];
158                         if (ch != 64) {
159                                 dest[bytes_decoded+1] |= ((ch & 0x3C) >> 2);
160                                 dest[bytes_decoded+2] = ((ch & 0x03) << 6);
161                                 increment = 2;
162                         }
163
164                         ch = decodebuf[3];
165                         if (ch != 64) {
166                                 dest[bytes_decoded+2] |= (ch & 0x3F);
167                                 increment = 3;
168                         }
169
170                         bytes_decoded += increment;
171                 }
172         }
173
174         return bytes_decoded;
175 }