File Coverage

Base64.xs
Criterion Covered Total %
statement 194 195 99.4
branch 196 258 75.9
condition n/a
subroutine n/a
pod n/a
total 390 453 86.0


line stmt bran cond sub pod time code
1             /*
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             #define PERL_NO_GET_CONTEXT /* we want efficiency */
29             #include "EXTERN.h"
30             #include "perl.h"
31             #include "XSUB.h"
32              
33             #define MAX_LINE 76 /* size of encoded lines */
34              
35             static const char basis_64[] =
36             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
37              
38             #define XX 255 /* illegal base64 char */
39             #define EQ 254 /* padding */
40             #define INVALID XX
41              
42             static const unsigned char index_64[256] = {
43             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
44             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
45             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
46             52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,EQ,XX,XX,
47             XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
48             15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
49             XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
50             41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
51              
52             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
53             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
54             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
55             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
56             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
57             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
58             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
59             XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
60             };
61              
62             #ifdef SvPVbyte
63             # if PERL_REVISION == 5 && PERL_VERSION < 7
64             /* SvPVbyte does not work in perl-5.6.1, borrowed version for 5.7.3 */
65             # undef SvPVbyte
66             # define SvPVbyte(sv, lp) \
67             ((SvFLAGS(sv) & (SVf_POK|SVf_UTF8)) == (SVf_POK) \
68             ? ((lp = SvCUR(sv)), SvPVX(sv)) : my_sv_2pvbyte(aTHX_ sv, &lp))
69             static char *
70             my_sv_2pvbyte(pTHX_ register SV *sv, STRLEN *lp)
71             {
72             sv_utf8_downgrade(sv,0);
73             return SvPV(sv,*lp);
74             }
75             # endif
76             #else
77             # define SvPVbyte SvPV
78             #endif
79              
80             #ifndef isXDIGIT
81             # define isXDIGIT isxdigit
82             #endif
83              
84             #ifndef NATIVE_TO_ASCII
85             # define NATIVE_TO_ASCII(ch) (ch)
86             #endif
87              
88             MODULE = MIME::Base64 PACKAGE = MIME::Base64
89              
90             SV*
91             encode_base64(sv,...)
92             SV* sv
93             PROTOTYPE: $;$
94              
95             PREINIT:
96             char *str; /* string to encode */
97             SSize_t len; /* length of the string */
98             const char*eol;/* the end-of-line sequence to use */
99             STRLEN eollen; /* length of the EOL sequence */
100             char *r; /* result string */
101             STRLEN rlen; /* length of result string */
102             unsigned char c1, c2, c3;
103             int chunk;
104             U32 had_utf8;
105              
106             CODE:
107             #if PERL_REVISION == 5 && PERL_VERSION >= 6
108 331           had_utf8 = SvUTF8(sv);
109 331           sv_utf8_downgrade(sv, FALSE);
110             #endif
111 330 50         str = SvPV(sv, rlen); /* SvPV(sv, len) gives warning for signed len */
112 330           len = (SSize_t)rlen;
113              
114             /* set up EOL from the second argument if present, default to "\n" */
115 330 100         if (items > 1 && SvOK(ST(1))) {
    50          
    0          
    0          
116 278 50         eol = SvPV(ST(1), eollen);
117             } else {
118 52           eol = "\n";
119 52           eollen = 1;
120             }
121              
122             /* calculate the length of the result */
123 330           rlen = (len+2) / 3 * 4; /* encoded bytes */
124 330 100         if (rlen) {
125             /* add space for EOL */
126 329           rlen += ((rlen-1) / MAX_LINE + 1) * eollen;
127             }
128              
129             /* allocate a result buffer */
130 330 100         RETVAL = newSV(rlen ? rlen : 1);
131 330           SvPOK_on(RETVAL);
132 330           SvCUR_set(RETVAL, rlen);
133 330           r = SvPVX(RETVAL);
134              
135             /* encode */
136 1992 100         for (chunk=0; len > 0; len -= 3, chunk++) {
137 1662 100         if (chunk == (MAX_LINE/4)) {
138 46           const char *c = eol;
139 46           const char *e = eol + eollen;
140 89 100         while (c < e)
141 43           *r++ = *c++;
142 46           chunk = 0;
143             }
144 1662           c1 = *str++;
145 1662 100         c2 = len > 1 ? *str++ : '\0';
146 1662           *r++ = basis_64[c1>>2];
147 1662           *r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
148 1662 100         if (len > 2) {
149 1362           c3 = *str++;
150 1362           *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
151 1362           *r++ = basis_64[c3 & 0x3F];
152 300 100         } else if (len == 2) {
153 22           *r++ = basis_64[(c2 & 0xF) << 2];
154 22           *r++ = '=';
155             } else { /* len == 1 */
156 278           *r++ = '=';
157 278           *r++ = '=';
158             }
159             }
160 330 100         if (rlen) {
161             /* append eol to the result string */
162 329           const char *c = eol;
163 329           const char *e = eol + eollen;
164 381 100         while (c < e)
165 52           *r++ = *c++;
166             }
167 330           *r = '\0'; /* every SV in perl should be NUL-terminated */
168             #if PERL_REVISION == 5 && PERL_VERSION >= 6
169 330 100         if (had_utf8)
170 1           sv_utf8_upgrade(sv);
171             #endif
172              
173             OUTPUT:
174             RETVAL
175              
176             SV*
177             decode_base64(sv)
178             SV* sv
179             PROTOTYPE: $
180              
181             PREINIT:
182             STRLEN len;
183 290 100         register unsigned char *str = (unsigned char*)SvPV(sv, len);
184 290           unsigned char const* end = str + len;
185             char *r;
186             unsigned char c[4];
187              
188             CODE:
189             {
190             /* always enough, but might be too much */
191 290           STRLEN rlen = len * 3 / 4;
192 290 100         RETVAL = newSV(rlen ? rlen : 1);
193             }
194 290           SvPOK_on(RETVAL);
195 290           r = SvPVX(RETVAL);
196              
197 393 100         while (str < end) {
198 381           int i = 0;
199             do {
200 1519           unsigned char uc = index_64[NATIVE_TO_ASCII(*str++)];
201 1519 100         if (uc != INVALID)
202 1513           c[i++] = uc;
203              
204 1519 100         if (str == end) {
205 285 100         if (i < 4) {
206 5 100         if (i < 2) goto thats_it;
207 3 100         if (i == 2) c[2] = EQ;
208 3           c[3] = EQ;
209             }
210 283           break;
211             }
212 1234 100         } while (i < 4);
213            
214 379 100         if (c[0] == EQ || c[1] == EQ) {
    100          
215             break;
216             }
217             /* printf("c0=%d,c1=%d,c2=%d,c3=%d\n", c[0],c[1],c[2],c[3]);*/
218              
219 377           *r++ = (c[0] << 2) | ((c[1] & 0x30) >> 4);
220              
221 377 100         if (c[2] == EQ)
222 262           break;
223 115           *r++ = ((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2);
224              
225 115 100         if (c[3] == EQ)
226 12           break;
227 103           *r++ = ((c[2] & 0x03) << 6) | c[3];
228             }
229              
230             thats_it:
231 290           SvCUR_set(RETVAL, r - SvPVX(RETVAL));
232 290           *r = '\0';
233              
234             OUTPUT:
235             RETVAL
236              
237             int
238             encoded_base64_length(sv,...)
239             SV* sv
240             PROTOTYPE: $;$
241              
242             PREINIT:
243             SSize_t len; /* length of the string */
244             STRLEN eollen; /* length of the EOL sequence */
245             U32 had_utf8;
246              
247             CODE:
248             #if PERL_REVISION == 5 && PERL_VERSION >= 6
249 63           had_utf8 = SvUTF8(sv);
250 63           sv_utf8_downgrade(sv, FALSE);
251             #endif
252 63           len = SvCUR(sv);
253             #if PERL_REVISION == 5 && PERL_VERSION >= 6
254 63 50         if (had_utf8)
255 0           sv_utf8_upgrade(sv);
256             #endif
257              
258 63 100         if (items > 1 && SvOK(ST(1))) {
    50          
    0          
    0          
259 6           eollen = SvCUR(ST(1));
260             } else {
261 57           eollen = 1;
262             }
263              
264 63           RETVAL = (len+2) / 3 * 4; /* encoded bytes */
265 63 100         if (RETVAL) {
266 61           RETVAL += ((RETVAL-1) / MAX_LINE + 1) * eollen;
267             }
268              
269             OUTPUT:
270             RETVAL
271              
272             int
273             decoded_base64_length(sv)
274             SV* sv
275             PROTOTYPE: $
276              
277             PREINIT:
278             STRLEN len;
279 66 50         register unsigned char *str = (unsigned char*)SvPV(sv, len);
280 66           unsigned char const* end = str + len;
281 66           int i = 0;
282              
283             CODE:
284 66           RETVAL = 0;
285 5296 100         while (str < end) {
286 5269           unsigned char uc = index_64[NATIVE_TO_ASCII(*str++)];
287 5269 100         if (uc == INVALID)
288 63           continue;
289 5206 100         if (uc == EQ)
290 39           break;
291 5167 100         if (i++) {
292 3858           RETVAL++;
293 3858 100         if (i == 4)
294 1266           i = 0;
295             }
296             }
297              
298             OUTPUT:
299             RETVAL
300              
301              
302             MODULE = MIME::Base64 PACKAGE = MIME::QuotedPrint
303              
304             #ifdef EBCDIC
305             #define qp_isplain(c) ((c) == '\t' || ((!isprint(c) && (c) != '=')))
306             #else
307             #define qp_isplain(c) ((c) == '\t' || (((c) >= ' ' && (c) <= '~') && (c) != '='))
308             #endif
309              
310             SV*
311             encode_qp(sv,...)
312             SV* sv
313             PROTOTYPE: $;$$
314              
315             PREINIT:
316             const char *eol;
317             STRLEN eol_len;
318             int binary;
319             STRLEN sv_len;
320             STRLEN linelen;
321             char *beg;
322             char *end;
323             char *p;
324             char *p_beg;
325             STRLEN p_len;
326             U32 had_utf8;
327              
328             CODE:
329             #if PERL_REVISION == 5 && PERL_VERSION >= 6
330 50           had_utf8 = SvUTF8(sv);
331 50           sv_utf8_downgrade(sv, FALSE);
332             #endif
333             /* set up EOL from the second argument if present, default to "\n" */
334 48 100         if (items > 1 && SvOK(ST(1))) {
    100          
    50          
    50          
335 2 50         eol = SvPV(ST(1), eol_len);
336             } else {
337 46           eol = "\n";
338 46           eol_len = 1;
339             }
340              
341 48 100         binary = (items > 2 && SvTRUE(ST(2)));
    50          
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
342              
343 48 50         beg = SvPV(sv, sv_len);
344 48           end = beg + sv_len;
345              
346 48           RETVAL = newSV(sv_len + 1);
347 48           sv_setpv(RETVAL, "");
348 48           linelen = 0;
349              
350 48           p = beg;
351             while (1) {
352 286           p_beg = p;
353              
354             /* skip past as much plain text as possible */
355 3683 100         while (p < end && qp_isplain(*p)) {
    100          
    100          
    100          
    100          
356 3397           p++;
357             }
358 286 100         if (p == end || *p == '\n') {
    100          
359             /* whitespace at end of line must be encoded */
360 119 100         while (p > p_beg && (*(p - 1) == '\t' || *(p - 1) == ' '))
    100          
    100          
361 30           p--;
362             }
363              
364 286           p_len = p - p_beg;
365 286 100         if (p_len) {
366             /* output plain text (with line breaks) */
367 62 100         if (eol_len) {
368 81 100         while (p_len > MAX_LINE - 1 - linelen) {
369 20           STRLEN len = MAX_LINE - 1 - linelen;
370 20           sv_catpvn(RETVAL, p_beg, len);
371 20           p_beg += len;
372 20           p_len -= len;
373 20           sv_catpvn(RETVAL, "=", 1);
374 20           sv_catpvn(RETVAL, eol, eol_len);
375 20           linelen = 0;
376             }
377             }
378 62 50         if (p_len) {
379 62           sv_catpvn(RETVAL, p_beg, p_len);
380 62           linelen += p_len;
381             }
382             }
383              
384 286 100         if (p == end) {
385 48           break;
386             }
387 238 100         else if (*p == '\n' && eol_len && !binary) {
    100          
    100          
388 20 100         if (linelen == 1 && SvCUR(RETVAL) > eol_len + 1 && (SvEND(RETVAL)-eol_len)[-2] == '=') {
    50          
    50          
389             /* fixup useless soft linebreak */
390 2           (SvEND(RETVAL)-eol_len)[-2] = SvEND(RETVAL)[-1];
391 2           SvCUR_set(RETVAL, SvCUR(RETVAL) - 1);
392             }
393             else {
394 18           sv_catpvn(RETVAL, eol, eol_len);
395             }
396 20           p++;
397 20           linelen = 0;
398             }
399             else {
400             /* output escaped char (with line breaks) */
401             assert(p < end);
402 218 100         if (eol_len && linelen > MAX_LINE - 4 && !(linelen == MAX_LINE - 3 && p + 1 < end && p[1] == '\n' && !binary)) {
    100          
    100          
    100          
    100          
    50          
403 16           sv_catpvn(RETVAL, "=", 1);
404 16           sv_catpvn(RETVAL, eol, eol_len);
405 16           linelen = 0;
406             }
407 218           sv_catpvf(RETVAL, "=%02X", (unsigned char)*p);
408 218           p++;
409 218           linelen += 3;
410             }
411              
412             /* optimize reallocs a bit */
413 238 100         if (SvLEN(RETVAL) > 80 && SvLEN(RETVAL) - SvCUR(RETVAL) < 3) {
    100          
414 2           STRLEN expected_len = (SvCUR(RETVAL) * sv_len) / (p - beg);
415 2 50         SvGROW(RETVAL, expected_len);
    50          
416             }
417 238           }
418              
419 48 100         if (SvCUR(RETVAL) && eol_len && linelen) {
    100          
    100          
420 31           sv_catpvn(RETVAL, "=", 1);
421 31           sv_catpvn(RETVAL, eol, eol_len);
422             }
423             #if PERL_REVISION == 5 && PERL_VERSION >= 6
424 48 100         if (had_utf8)
425 1           sv_utf8_upgrade(sv);
426             #endif
427              
428             OUTPUT:
429             RETVAL
430              
431             SV*
432             decode_qp(sv)
433             SV* sv
434             PROTOTYPE: $
435              
436             PREINIT:
437             STRLEN len;
438 52 50         char *str = SvPVbyte(sv, len);
439 52           char const* end = str + len;
440             char *r;
441 52           char *whitespace = 0;
442              
443             CODE:
444 52 100         RETVAL = newSV(len ? len : 1);
445 52           SvPOK_on(RETVAL);
446 52           r = SvPVX(RETVAL);
447 3253 100         while (str < end) {
448 3201 100         if (*str == ' ' || *str == '\t') {
    100          
449 125 100         if (!whitespace)
450 116           whitespace = str;
451 125           str++;
452             }
453 3076 100         else if (*str == '\r' && (str + 1) < end && str[1] == '\n') {
    50          
    50          
454 7           str++;
455             }
456 3069 100         else if (*str == '\n') {
457 34           whitespace = 0;
458 34           *r++ = *str++;
459             }
460             else {
461 3035 100         if (whitespace) {
462 223 100         while (whitespace < str) {
463 113           *r++ = *whitespace++;
464             }
465 110           whitespace = 0;
466             }
467 3035 100         if (*str == '=') {
468 169 100         if ((str + 2) < end && isXDIGIT(str[1]) && isXDIGIT(str[2])) {
    100          
    50          
469             char buf[3];
470 54           str++;
471 54           buf[0] = *str++;
472 54           buf[1] = *str++;
473 54           buf[2] = '\0';
474 54           *r++ = (char)strtol(buf, 0, 16);
475             }
476             else {
477             /* look for soft line break */
478 61           char *p = str + 1;
479 76 50         while (p < end && (*p == ' ' || *p == '\t'))
    100          
    100          
480 15           p++;
481 61 50         if (p < end && *p == '\n')
    100          
482 56           str = p + 1;
483 5 50         else if ((p + 1) < end && *p == '\r' && *(p + 1) == '\n')
    100          
    50          
484 3           str = p + 2;
485             else
486 115           *r++ = *str++; /* give up */
487             }
488             }
489             else {
490 2920           *r++ = *str++;
491             }
492             }
493             }
494 52 100         if (whitespace) {
495 3 100         while (whitespace < str) {
496 2           *r++ = *whitespace++;
497             }
498             }
499 52           *r = '\0';
500 52           SvCUR_set(RETVAL, r - SvPVX(RETVAL));
501              
502             OUTPUT:
503             RETVAL
504              
505              
506             MODULE = MIME::Base64 PACKAGE = MIME::Base64