xref: /haiku/src/kits/support/Base64.cpp (revision 9642f7705b27e5c270c15fa526d14e1848c2c27d)
1 /*
2  * Copyright 2011-2013 Haiku, Inc. All rights reserved.
3  * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
4  */
5 
6 
7 #include <mail_encoding.h>
8 
9 #include <ctype.h>
10 #include <string.h>
11 #include <SupportDefs.h>
12 
13 
14 static const char kBase64Alphabet[64] = {
15 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
16 	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
17 	'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
18 	'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
19 	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
20 	'+',
21 	'/'
22 };
23 
24 
25 ssize_t
26 encode_base64(char *out, const char *in, off_t length, int headerMode)
27 {
28 	uint32 concat;
29 	int i = 0;
30 	int k = 0;
31 	int lineLength = 4;
32 		// Stop before it actually gets too long
33 
34 	while (i < length) {
35 		concat = ((in[i] & 0xff) << 16);
36 
37 		if ((i+1) < length)
38 			concat |= ((in[i+1] & 0xff) << 8);
39 		if ((i+2) < length)
40 			concat |= (in[i+2] & 0xff);
41 
42 		i += 3;
43 
44 		out[k++] = kBase64Alphabet[(concat >> 18) & 63];
45 		out[k++] = kBase64Alphabet[(concat >> 12) & 63];
46 		out[k++] = kBase64Alphabet[(concat >> 6) & 63];
47 		out[k++] = kBase64Alphabet[concat & 63];
48 
49 		if (i >= length) {
50 			int v;
51 			for (v = 0; v <= (i - length); v++)
52 				out[k-v] = '=';
53 		}
54 
55 		lineLength += 4;
56 
57 		// No line breaks in header mode, since the text is part of a Subject:
58 		// line or some other single header line.  The header code will do word
59 		// wrapping separately from this encoding stuff.
60 		if (!headerMode && lineLength > BASE64_LINELENGTH) {
61 			out[k++] = '\r';
62 			out[k++] = '\n';
63 
64 			lineLength = 4;
65 		}
66 	}
67 
68 	return k;
69 }
70 
71 
72 ssize_t
73 decode_base64(char *out, const char *in, off_t length)
74 {
75 	uint32 concat, value;
76 	int lastOutLine = 0;
77 	int i, j;
78 	int outIndex = 0;
79 
80 	for (i = 0; i < length; i += 4) {
81 		concat = 0;
82 
83 		for (j = 0; j < 4 && (i + j) < length; j++) {
84 			value = in[i + j];
85 
86 			if (value == '\n' || value == '\r') {
87 				// jump over line breaks
88 				lastOutLine = outIndex;
89 				i++;
90 				j--;
91 				continue;
92 			}
93 
94 			if ((value >= 'A') && (value <= 'Z'))
95 				value -= 'A';
96 			else if ((value >= 'a') && (value <= 'z'))
97 				value = value - 'a' + 26;
98 			else if ((value >= '0') && (value <= '9'))
99 				value = value - '0' + 52;
100 			else if (value == '+')
101 				value = 62;
102 			else if (value == '/')
103 				value = 63;
104 			else if (value == '=')
105 				break;
106 			else {
107 				// there is an invalid character in this line - we will
108 				// ignore the whole line and go to the next
109 				outIndex = lastOutLine;
110 				while (i < length && in[i] != '\n' && in[i] != '\r')
111 					i++;
112 				concat = 0;
113 			}
114 
115 			value = value << ((3-j)*6);
116 
117 			concat |= value;
118 		}
119 
120 		if (j > 1)
121 			out[outIndex++] = (concat & 0x00ff0000) >> 16;
122 		if (j > 2)
123 			out[outIndex++] = (concat & 0x0000ff00) >> 8;
124 		if (j > 3)
125 			out[outIndex++] = (concat & 0x000000ff);
126 	}
127 
128 	return outIndex;
129 }
130 
131 
132 #if __GNUC__ <= 2
133 	// BeOS-ABI compatible wrappers.
134 	ssize_t encode_base64(char *out, char *in, off_t length)
135 	{
136 		return encode_base64(out, in, length, 0);
137 	}
138 
139 	ssize_t	decode_base64(char *out, const char *in, off_t length,
140 		bool /*replace_cr*/)
141 	{
142 		return decode_base64(out, in, length);
143 	}
144 #endif	//  __GNUC__ <= 2
145