File Coverage

cpan/MIME-Base64/Base64.xs
Criterion Covered Total %
statement 168 169 99.4
branch n/a
condition n/a
subroutine n/a
total 168 169 99.4


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