This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Move MIME::Base64 from ext/ to cpan/
[perl5.git] / cpan / MIME-Base64 / Base64.xs
1 /* $Id$
2
3 Copyright 1997-2004 Gisle Aas
4
5 This library is free software; you can redistribute it and/or
6 modify it under the same terms as Perl itself.
7
8
9 The tables and some of the code that used to be here was borrowed from
10 metamail, which comes with this message:
11
12   Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
13
14   Permission to use, copy, modify, and distribute this material
15   for any purpose and without fee is hereby granted, provided
16   that the above copyright notice and this permission notice
17   appear in all copies, and that the name of Bellcore not be
18   used in advertising or publicity pertaining to this
19   material without the specific, prior written permission
20   of an authorized representative of Bellcore.  BELLCORE
21   MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
22   OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
23   WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
24
25 */
26
27
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 #define PERL_NO_GET_CONTEXT     /* we want efficiency */
32 #include "EXTERN.h"
33 #include "perl.h"
34 #include "XSUB.h"
35 #ifdef __cplusplus
36 }
37 #endif
38
39 #ifndef PATCHLEVEL
40 #    include <patchlevel.h>
41 #    if !(defined(PERL_VERSION) || (SUBVERSION > 0 && defined(PATCHLEVEL)))
42 #        include <could_not_find_Perl_patchlevel.h>
43 #    endif
44 #endif
45
46 #if PATCHLEVEL <= 4 && !defined(PL_dowarn)
47    #define PL_dowarn dowarn
48 #endif
49
50 #ifdef G_WARN_ON
51    #define DOWARN (PL_dowarn & G_WARN_ON)
52 #else
53    #define DOWARN PL_dowarn
54 #endif
55
56
57 #define MAX_LINE  76 /* size of encoded lines */
58
59 static const char basis_64[] =
60    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
61
62 #define XX      255     /* illegal base64 char */
63 #define EQ      254     /* padding */
64 #define INVALID XX
65
66 static const unsigned char index_64[256] = {
67     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
68     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
69     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
70     52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,EQ,XX,XX,
71     XX, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
72     15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
73     XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
74     41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
75
76     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
77     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
78     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
79     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
80     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
81     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
82     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
83     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
84 };
85
86 #ifdef SvPVbyte
87 #   if PERL_REVISION == 5 && PERL_VERSION < 7
88        /* SvPVbyte does not work in perl-5.6.1, borrowed version for 5.7.3 */
89 #       undef SvPVbyte
90 #       define SvPVbyte(sv, lp) \
91           ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \
92            ? ((lp = SvCUR(sv)), SvPVX(sv)) : my_sv_2pvbyte(aTHX_ sv, &lp))
93        static char *
94        my_sv_2pvbyte(pTHX_ register SV *sv, STRLEN *lp)
95        {   
96            sv_utf8_downgrade(sv,0);
97            return SvPV(sv,*lp);
98        }
99 #   endif
100 #else
101 #   define SvPVbyte SvPV
102 #endif
103
104 #ifndef isXDIGIT
105 #   define isXDIGIT isxdigit
106 #endif
107
108 #ifndef NATIVE_TO_ASCII
109 #   define NATIVE_TO_ASCII(ch) (ch)
110 #endif
111
112 MODULE = MIME::Base64           PACKAGE = MIME::Base64
113
114 SV*
115 encode_base64(sv,...)
116         SV* sv
117         PROTOTYPE: $;$
118
119         PREINIT:
120         char *str;     /* string to encode */
121         SSize_t len;   /* length of the string */
122         char *eol;     /* the end-of-line sequence to use */
123         STRLEN eollen; /* length of the EOL sequence */
124         char *r;       /* result string */
125         STRLEN rlen;   /* length of result string */
126         unsigned char c1, c2, c3;
127         int chunk;
128
129         CODE:
130 #if PERL_REVISION == 5 && PERL_VERSION >= 6
131         sv_utf8_downgrade(sv, FALSE);
132 #endif
133         str = SvPV(sv, rlen); /* SvPV(sv, len) gives warning for signed len */
134         len = (SSize_t)rlen;
135
136         /* set up EOL from the second argument if present, default to "\n" */
137         if (items > 1 && SvOK(ST(1))) {
138             eol = SvPV(ST(1), eollen);
139         } else {
140             eol = "\n";
141             eollen = 1;
142         }
143
144         /* calculate the length of the result */
145         rlen = (len+2) / 3 * 4;  /* encoded bytes */
146         if (rlen) {
147             /* add space for EOL */
148             rlen += ((rlen-1) / MAX_LINE + 1) * eollen;
149         }
150
151         /* allocate a result buffer */
152         RETVAL = newSV(rlen ? rlen : 1);
153         SvPOK_on(RETVAL);       
154         SvCUR_set(RETVAL, rlen);
155         r = SvPVX(RETVAL);
156
157         /* encode */
158         for (chunk=0; len > 0; len -= 3, chunk++) {
159             if (chunk == (MAX_LINE/4)) {
160                 char *c = eol;
161                 char *e = eol + eollen;
162                 while (c < e)
163                     *r++ = *c++;
164                 chunk = 0;
165             }
166             c1 = *str++;
167             c2 = len > 1 ? *str++ : '\0';
168             *r++ = basis_64[c1>>2];
169             *r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
170             if (len > 2) {
171                 c3 = *str++;
172                 *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
173                 *r++ = basis_64[c3 & 0x3F];
174             } else if (len == 2) {
175                 *r++ = basis_64[(c2 & 0xF) << 2];
176                 *r++ = '=';
177             } else { /* len == 1 */
178                 *r++ = '=';
179                 *r++ = '=';
180             }
181         }
182         if (rlen) {
183             /* append eol to the result string */
184             char *c = eol;
185             char *e = eol + eollen;
186             while (c < e)
187                 *r++ = *c++;
188         }
189         *r = '\0';  /* every SV in perl should be NUL-terminated */
190
191         OUTPUT:
192         RETVAL
193
194 SV*
195 decode_base64(sv)
196         SV* sv
197         PROTOTYPE: $
198
199         PREINIT:
200         STRLEN len;
201         register unsigned char *str = (unsigned char*)SvPVbyte(sv, len);
202         unsigned char const* end = str + len;
203         char *r;
204         unsigned char c[4];
205
206         CODE:
207         {
208             /* always enough, but might be too much */
209             STRLEN rlen = len * 3 / 4;
210             RETVAL = newSV(rlen ? rlen : 1);
211         }
212         SvPOK_on(RETVAL);
213         r = SvPVX(RETVAL);
214
215         while (str < end) {
216             int i = 0;
217             do {
218                 unsigned char uc = index_64[NATIVE_TO_ASCII(*str++)];
219                 if (uc != INVALID)
220                     c[i++] = uc;
221
222                 if (str == end) {
223                     if (i < 4) {
224                         if (i && DOWARN)
225                             warn("Premature end of base64 data");
226                         if (i < 2) goto thats_it;
227                         if (i == 2) c[2] = EQ;
228                         c[3] = EQ;
229                     }
230                     break;
231                 }
232             } while (i < 4);
233         
234             if (c[0] == EQ || c[1] == EQ) {
235                 if (DOWARN) warn("Premature padding of base64 data");
236                 break;
237             }
238             /* printf("c0=%d,c1=%d,c2=%d,c3=%d\n", c[0],c[1],c[2],c[3]);*/
239
240             *r++ = (c[0] << 2) | ((c[1] & 0x30) >> 4);
241
242             if (c[2] == EQ)
243                 break;
244             *r++ = ((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2);
245
246             if (c[3] == EQ)
247                 break;
248             *r++ = ((c[2] & 0x03) << 6) | c[3];
249         }
250
251       thats_it:
252         SvCUR_set(RETVAL, r - SvPVX(RETVAL));
253         *r = '\0';
254
255         OUTPUT:
256         RETVAL
257
258
259 MODULE = MIME::Base64           PACKAGE = MIME::QuotedPrint
260
261 #ifdef EBCDIC
262 #define qp_isplain(c) ((c) == '\t' || ((!isprint(c) && (c) != '=')))
263 #else
264 #define qp_isplain(c) ((c) == '\t' || (((c) >= ' ' && (c) <= '~') && (c) != '='))
265 #endif
266
267 SV*
268 encode_qp(sv,...)
269         SV* sv
270         PROTOTYPE: $;$$
271
272         PREINIT:
273         char *eol;
274         STRLEN eol_len;
275         int binary;
276         STRLEN sv_len;
277         STRLEN linelen;
278         char *beg;
279         char *end;
280         char *p;
281         char *p_beg;
282         STRLEN p_len;
283
284         CODE:
285 #if PERL_REVISION == 5 && PERL_VERSION >= 6
286         sv_utf8_downgrade(sv, FALSE);
287 #endif
288         /* set up EOL from the second argument if present, default to "\n" */
289         if (items > 1 && SvOK(ST(1))) {
290             eol = SvPV(ST(1), eol_len);
291         } else {
292             eol = "\n";
293             eol_len = 1;
294         }
295
296         binary = (items > 2 && SvTRUE(ST(2)));
297
298         beg = SvPV(sv, sv_len);
299         end = beg + sv_len;
300
301         RETVAL = newSV(sv_len + 1);
302         sv_setpv(RETVAL, "");
303         linelen = 0;
304
305         p = beg;
306         while (1) {
307             p_beg = p;
308
309             /* skip past as much plain text as possible */
310             while (p < end && qp_isplain(*p)) {
311                 p++;
312             }
313             if (p == end || *p == '\n') {
314                 /* whitespace at end of line must be encoded */
315                 while (p > p_beg && (*(p - 1) == '\t' || *(p - 1) == ' '))
316                     p--;
317             }
318
319             p_len = p - p_beg;
320             if (p_len) {
321                 /* output plain text (with line breaks) */
322                 if (eol_len) {
323                     STRLEN max_last_line = (p == end || *p == '\n')
324                                               ? MAX_LINE         /* .......\n */
325                                               : ((p + 1) == end || *(p + 1) == '\n')
326                                                 ? MAX_LINE - 3   /* ....=XX\n */
327                                                 : MAX_LINE - 4;  /* ...=XX=\n */
328                     while (p_len + linelen > max_last_line) {
329                         STRLEN len = MAX_LINE - 1 - linelen;
330                         if (len > p_len)
331                             len = p_len;
332                         sv_catpvn(RETVAL, p_beg, len);
333                         p_beg += len;
334                         p_len -= len;
335                         sv_catpvn(RETVAL, "=", 1);
336                         sv_catpvn(RETVAL, eol, eol_len);
337                         linelen = 0;
338                     }
339                 }
340                 if (p_len) {
341                     sv_catpvn(RETVAL, p_beg, p_len);
342                     linelen += p_len;
343                 }
344             }
345
346             if (p == end) {
347                 break;
348             }
349             else if (*p == '\n' && eol_len && !binary) {
350                 sv_catpvn(RETVAL, eol, eol_len);
351                 p++;
352                 linelen = 0;
353             }
354             else {
355                 /* output escaped char (with line breaks) */
356                 assert(p < end);
357                 if (eol_len && linelen > MAX_LINE - 4) {
358                     sv_catpvn(RETVAL, "=", 1);
359                     sv_catpvn(RETVAL, eol, eol_len);
360                     linelen = 0;
361                 }
362                 sv_catpvf(RETVAL, "=%02X", (unsigned char)*p);
363                 p++;
364                 linelen += 3;
365             }
366
367             /* optimize reallocs a bit */
368             if (SvLEN(RETVAL) > 80 && SvLEN(RETVAL) - SvCUR(RETVAL) < 3) {
369                 STRLEN expected_len = (SvCUR(RETVAL) * sv_len) / (p - beg);
370                 SvGROW(RETVAL, expected_len);
371             }
372         }
373
374         if (SvCUR(RETVAL) && eol_len && linelen) {
375             sv_catpvn(RETVAL, "=", 1);
376             sv_catpvn(RETVAL, eol, eol_len);
377         }
378
379         OUTPUT:
380         RETVAL
381
382 SV*
383 decode_qp(sv)
384         SV* sv
385         PROTOTYPE: $
386
387         PREINIT:
388         STRLEN len;
389         char *str = SvPVbyte(sv, len);
390         char const* end = str + len;
391         char *r;
392         char *whitespace = 0;
393
394         CODE:
395         RETVAL = newSV(len ? len : 1);
396         SvPOK_on(RETVAL);
397         r = SvPVX(RETVAL);
398         while (str < end) {
399             if (*str == ' ' || *str == '\t') {
400                 if (!whitespace)
401                     whitespace = str;
402                 str++;
403             }
404             else if (*str == '\r' && (str + 1) < end && str[1] == '\n') {
405                 str++;
406             }
407             else if (*str == '\n') {
408                 whitespace = 0;
409                 *r++ = *str++;
410             }
411             else {
412                 if (whitespace) {
413                     while (whitespace < str) {
414                         *r++ = *whitespace++;
415                     }
416                     whitespace = 0;
417                 }
418                 if (*str == '=') {
419                     if ((str + 2) < end && isXDIGIT(str[1]) && isXDIGIT(str[2])) {
420                         char buf[3];
421                         str++;
422                         buf[0] = *str++;
423                         buf[1] = *str++;
424                         buf[2] = '\0';
425                         *r++ = (char)strtol(buf, 0, 16);
426                     }
427                     else {
428                         /* look for soft line break */
429                         char *p = str + 1;
430                         while (p < end && (*p == ' ' || *p == '\t'))
431                             p++;
432                         if (p < end && *p == '\n')
433                             str = p + 1;
434                         else if ((p + 1) < end && *p == '\r' && *(p + 1) == '\n')
435                             str = p + 2;
436                         else
437                             *r++ = *str++; /* give up */
438                     }
439                 }
440                 else {
441                     *r++ = *str++;
442                 }
443             }
444         }
445         if (whitespace) {
446             while (whitespace < str) {
447                 *r++ = *whitespace++;
448             }
449         }
450         *r = '\0';
451         SvCUR_set(RETVAL, r - SvPVX(RETVAL));
452
453         OUTPUT:
454         RETVAL
455
456
457 MODULE = MIME::Base64           PACKAGE = MIME::Base64