1 /*****************************************************************************
2    Major portions of this software are copyrighted by the Medical College
3    of Wisconsin, 1994-2000, and are released under the Gnu General Public
4    License, Version 2.  See the file README.Copyright for details.
5 ******************************************************************************/
6 
7 #include "stdlib.h"
8 
9 typedef unsigned char byte ;
10 
11 static int  dtable_mode = -1 ;    /* 1=encode, 2=decode, -1=neither */
12 static byte dtable[256] ;         /* encode/decode table */
13 static int linelen = 72 ;         /* line length (max 76) */
14 static int ncrlf   = 1 ;
15 
16 #define B64_goodchar(c) (dtable[c] != 0x80)  /* for decode only */
17 
18 #define B64_EOL1 '\r'   /* CR */
19 #define B64_EOL2 '\n'   /* LF */
20 
21 /*----------------------------------------------------------------------*/
22 
B64_set_crlf(int nn)23 void B64_set_crlf( int nn )
24 {
25    if( nn >= 1 && nn <= 2 ) ncrlf = nn ;
26    return ;
27 }
28 
29 /*----------------------------------------------------------------------*/
30 
B64_set_linelen(int ll)31 void B64_set_linelen( int ll )
32 {
33    if( ll >= 16 && ll <= 76 ) linelen = 4*(ll/4) ; /* multiple of 4 */
34    else                       linelen = 72 ;
35    return ;
36 }
37 
38 /*----------------------------------------------------------------------*/
39 
load_encode_table(void)40 static void load_encode_table(void)
41 {
42     int i ;
43     if( dtable_mode == 1 ) return ;
44     for (i = 0; i < 26; i++) {
45         dtable[i] = 'A' + i;
46         dtable[26 + i] = 'a' + i;
47     }
48     for (i = 0; i < 10; i++) dtable[52 + i] = '0' + i;
49     dtable[62] = '+'; dtable[63] = '/'; dtable_mode = 1 ;
50     return ;
51 }
52 
53 /*----------------------------------------------------------------------*/
54 
load_decode_table(void)55 static void load_decode_table(void)
56 {
57     int i;
58     if( dtable_mode == 2 ) return ;
59     for (i = 0  ; i < 255 ; i++) dtable[i] = 0x80;             /* bad */
60     for (i = 'A'; i <= 'Z'; i++) dtable[i] =  0 + (i - 'A');
61     for (i = 'a'; i <= 'z'; i++) dtable[i] = 26 + (i - 'a');
62     for (i = '0'; i <= '9'; i++) dtable[i] = 52 + (i - '0');
63     dtable['+'] = 62; dtable['/'] = 63; dtable['='] = 0; dtable_mode = 2 ;
64     return ;
65 }
66 
67 #define B64_encode3(a,b,c,w,x,y,z)                 \
68      ( w = dtable[(a)>>2]                      ,   \
69        x = dtable[((a & 3) << 4) | (b >> 4)]   ,   \
70        y = dtable[((b & 0xF) << 2) | (c >> 6)] ,   \
71        z = dtable[c & 0x3F]                     )
72 
73 #define B64_encode2(a,b,w,x,y,z)                   \
74      ( B64_encode3(a,b,0,w,x,y,z) , z = '=' )
75 
76 #define B64_encode1(a,w,x,y,z)                     \
77      ( B64_encode3(a,0,0,w,x,y,z) , y=z = '=' )
78 
79 #define B64_decode4(w,x,y,z,a,b,c)                 \
80      ( a = (dtable[w] << 2) | (dtable[x] >> 4) ,   \
81        b = (dtable[x] << 4) | (dtable[y] >> 2) ,   \
82        c = (dtable[y] << 6) | dtable[z]         )
83 
84 #define B64_decode_count(w,x,y,z)                  \
85      ( ((w)=='='||(x)=='=') ? 0                    \
86                             : ((y)=='=') ? 1       \
87                             : ((z)=='=') ? 2 : 3 )
88 
89 /*----------------------------------------------------------------------
90    Convert base64 encoding to a binary array
91 
92    Inputs: nb64 = number of bytes in b64
93             b64 = array of base64 encoding bytes
94                   values not in the base64 encoding set will be skipped
95 
96    Outputs: *nbin = number of binary bytes [*nbin==0 flags an error]
97              *bin = pointer to newly malloc()-ed space with bytes
98 ------------------------------------------------------------------------*/
99 
B64_to_binary(int nb64,byte * b64,int * nbin,byte ** bin)100 void B64_to_binary( int nb64 , byte * b64 , int * nbin , byte ** bin )
101 {
102    int ii,jj , nn ;
103    byte a,b,c , w,x,y,z ;
104 
105    /*- sanity checks -*/
106 
107    if( nbin == NULL || bin == NULL ) return ;
108 
109    if( nb64 < 4 || b64 == NULL ){ *nbin = 0 ; *bin = NULL ; return ; }
110 
111    *bin = (byte *) malloc(sizeof(byte)*(2+3*nb64/4)) ;
112    if( *bin == NULL ){ *nbin = 0 ; return ; }
113 
114    /*- some work -*/
115 
116    load_decode_table() ;
117    for( ii=jj=0 ; ii < nb64 ; ){  /* scan inputs, skipping bad characters */
118       w = b64[ii++] ;
119       while( !B64_goodchar(w) && ii < nb64 ) w = b64[ii++] ;
120       x = (ii < nb64) ? b64[ii++] : '=' ;
121       while( !B64_goodchar(x) && ii < nb64 ) x = b64[ii++] ;
122       y = (ii < nb64) ? b64[ii++] : '=' ;
123       while( !B64_goodchar(y) && ii < nb64 ) y = b64[ii++] ;
124       z = (ii < nb64) ? b64[ii++] : '=' ;
125       while( !B64_goodchar(z) && ii < nb64 ) z = b64[ii++] ;
126 
127       B64_decode4(w,x,y,z,a,b,c) ;
128 
129       if( z == '=' ){                        /* got to the end? */
130          nn = B64_decode_count(w,x,y,z) ;    /* see how many to save */
131          if( nn > 0 ) (*bin)[jj++] = a ;
132          if( nn > 1 ) (*bin)[jj++] = b ;
133          break ;
134       }
135 
136       /* not at the end => save all 3 outputs */
137 
138       (*bin)[jj++] = a ; (*bin)[jj++] = b ; (*bin)[jj++] = c ;
139    }
140 
141    *bin  = (byte *) realloc( *bin , sizeof(byte)*jj ) ;
142    *nbin = jj ;
143    return ;
144 }
145 
146 /*----------------------------------------------------------------------
147    Convert binary array to base64 encoding
148 
149    Inputs: nbin = number of bytes in bin
150             bin = array of binary bytes to encode
151 
152    Outputs: *nb64 = number of base64 bytes [*nb64==0 flags an error]
153              *b64 = pointer to newly malloc()-ed space with bytes
154 
155    The output array (*b64) line length can be set by
156       B64_set_linelen(n)
157    where n is from 16 to 76.  The default is 72.  Note, however, that
158    encoded bytes will always be written out in groups of 4.
159    The output array line separator can be the LF character only (Unix)
160    or the CR-LF combination (DOS, etc.).  This is controlled by
161       B64_set_crlf(n)
162    where n=1 for LF, n=2 for CR-LF.  The default is LF.  The output
163    array will be terminated with a line separator.  There will be
164    no ASCII NUL character at the end of *b64 -- that is, the output
165    is not a C string.
166 ------------------------------------------------------------------------*/
167 
B64_to_base64(int nbin,byte * bin,int * nb64,byte ** b64)168 void B64_to_base64( int nbin , byte * bin , int * nb64 , byte ** b64 )
169 {
170    int ii,jj , nn,n3 ;
171    byte a,b,c , w,x,y,z ;
172 
173    /*- sanity checks -*/
174 
175    if( nb64 == NULL || b64 == NULL ) return ;
176    if( nbin <= 0    || bin == NULL ){ *nb64 = 0 ; *b64 = NULL ; return ; }
177 
178    nn   = (4.0*(linelen+ncrlf+1.0)/(3.0*linelen))*nbin + 256 ;
179    *b64 = (byte *) malloc(sizeof(byte)*nn) ;
180    if( *b64 == NULL ){ *nb64 = 0 ; return ; }
181 
182    /*- do blocks of 3 -*/
183 
184    load_encode_table() ;
185    n3 = (nbin/3)*3 ;
186    for( nn=jj=ii=0 ; ii < n3 ; ){
187       a = bin[ii++] ; b = bin[ii++] ; c = bin[ii++] ;
188       B64_encode3(a,b,c,w,x,y,z) ;
189       (*b64)[jj++] = w ;
190       (*b64)[jj++] = x ;
191       (*b64)[jj++] = y ;
192       (*b64)[jj++] = z ;
193       nn += 4 ; if( nn >= linelen ){
194                    if( ncrlf == 2 ) (*b64)[jj++] = B64_EOL1 ;
195                    (*b64)[jj++] = B64_EOL2 ;
196                    nn = 0 ;
197                 }
198    }
199 
200    /*- do the leftovers, if any (1 or 2 bytes) -*/
201 
202    if( ii < nbin ){
203       if( ii == nbin-2 )
204          B64_encode2(bin[ii],bin[ii+1],w,x,y,z) ;
205       else
206          B64_encode1(bin[ii],w,x,y,z) ;
207 
208       (*b64)[jj++] = w ;
209       (*b64)[jj++] = x ;
210       (*b64)[jj++] = y ;
211       (*b64)[jj++] = z ; nn += 4 ;
212    }
213 
214    if( nn > 0 ){
215       if( ncrlf == 2 ) (*b64)[jj++] = B64_EOL1 ;
216       (*b64)[jj++] = B64_EOL2 ;
217    }
218 
219    *b64  = (byte *) realloc( *b64 , sizeof(byte)*jj ) ;
220    *nb64 = jj ;
221    return ;
222 }
223