File Coverage

Email-Address-XS.xs
Criterion Covered Total %
statement 315 360 87.5
branch 277 392 70.6
condition n/a
subroutine n/a
pod n/a
total 592 752 78.7


line stmt bran cond sub pod time code
1             /* Copyright (c) 2015-2017 by Pali */
2              
3             #define PERL_NO_GET_CONTEXT
4             #include "EXTERN.h"
5             #include "perl.h"
6             #include "XSUB.h"
7              
8             #include "dovecot-parser.h"
9              
10             /* Perl pre 5.6.1 support */
11             #if PERL_VERSION < 6 || (PERL_VERSION == 6 && PERL_SUBVERSION < 1)
12             #define BROKEN_SvPVutf8
13             #endif
14              
15             /* Perl pre 5.7.2 support */
16             #ifndef SvPV_nomg
17             #define WITHOUT_SvPV_nomg
18             #endif
19              
20             /* Perl pre 5.8.0 support */
21             #ifndef UTF8_IS_INVARIANT
22             #define UTF8_IS_INVARIANT(c) (((U8)c) < 0x80)
23             #endif
24              
25             /* Perl pre 5.10.1 support */
26             #ifndef newSVpvn_utf8
27             static SV *newSVpvn_utf8(pTHX_ const char *str, STRLEN len, U32 utf8) {
28             SV *sv = newSVpvn(str, len);
29             if (utf8) SvUTF8_on(sv);
30             return sv;
31             }
32             #define newSVpvn_utf8(str, len, utf8) newSVpvn_utf8(aTHX_ str, len, utf8)
33             #endif
34              
35             /* Perl pre 5.13.1 support */
36             #ifndef warn_sv
37             #define warn_sv(scalar) warn("%s", SvPV_nolen(scalar))
38             #endif
39             #ifndef croak_sv
40             #define croak_sv(scalar) croak("%s", SvPV_nolen(scalar))
41             #endif
42              
43             /* Perl pre 5.15.4 support */
44             #ifndef sv_derived_from_pvn
45             #define sv_derived_from_pvn(scalar, name, len, flags) sv_derived_from(scalar, name)
46             #endif
47              
48             /* Exported i_panic function for other C files */
49 0           void i_panic(const char *format, ...)
50             {
51             dTHX;
52             va_list args;
53              
54 0           va_start(args, format);
55 0           vcroak(format, &args);
56             va_end(args);
57             }
58              
59 23           static void append_carp_shortmess(pTHX_ SV *scalar)
60             {
61 23           dSP;
62             int count;
63              
64 23           ENTER;
65 23           SAVETMPS;
66 23 50         PUSHMARK(SP);
67              
68 23           count = call_pv("Carp::shortmess", G_SCALAR);
69              
70 23           SPAGAIN;
71              
72 23 50         if (count > 0)
73 23           sv_catsv(scalar, POPs);
74              
75 23           PUTBACK;
76 23 50         FREETMPS;
77 23           LEAVE;
78 23           }
79              
80             #define CARP_WARN false
81             #define CARP_DIE true
82 23           static void carp(bool fatal, const char *format, ...)
83             {
84             dTHX;
85             va_list args;
86             SV *scalar;
87              
88 23           va_start(args, format);
89 23           scalar = sv_2mortal(vnewSVpvf(format, &args));
90 23           va_end(args);
91              
92 23           append_carp_shortmess(aTHX_ scalar);
93              
94 23 50         if (!fatal)
95 23           warn_sv(scalar);
96             else
97 0           croak_sv(scalar);
98 23           }
99              
100 411           static bool string_contains_nul(const char *str, STRLEN len)
101             {
102 411           return (memchr(str, 0, len + 1) != str + len);
103             }
104              
105 216           static bool string_needs_utf8_upgrade(const char *str, STRLEN len)
106             {
107             STRLEN i;
108              
109 2701 100         for (i = 0; i < len; ++i)
110 2491 100         if (!UTF8_IS_INVARIANT(str[i]))
111 6           return true;
112              
113 210           return false;
114             }
115              
116 1061           static const char *get_perl_scalar_value(pTHX_ SV *scalar, STRLEN *len, bool utf8, bool nomg)
117             {
118             const char *string;
119              
120             #ifndef WITHOUT_SvPV_nomg
121 1061 100         if (!nomg)
122 698 100         SvGETMAGIC(scalar);
    50          
123              
124 1061 100         if (!SvOK(scalar))
    50          
    50          
125 280           return NULL;
126              
127 781 100         string = SvPV_nomg(scalar, *len);
128             #else
129             COP cop;
130              
131             if (!SvGMAGICAL(scalar) && !SvOK(scalar))
132             return NULL;
133              
134             /* Temporary turn off all warnings because SvPV can throw uninitialized warning */
135             cop = *PL_curcop;
136             cop.cop_warnings = pWARN_NONE;
137              
138             ENTER;
139             SAVEVPTR(PL_curcop);
140             PL_curcop = &cop;
141              
142             string = SvPV(scalar, *len);
143              
144             LEAVE;
145              
146             if (SvGMAGICAL(scalar) && !SvOK(scalar))
147             return NULL;
148             #endif
149              
150 781 100         if (utf8 && !SvUTF8(scalar) && string_needs_utf8_upgrade(string, *len)) {
    100          
    100          
151 6           scalar = sv_2mortal(newSVpvn(string, *len));
152             #ifdef BROKEN_SvPVutf8
153             sv_utf8_upgrade(scalar);
154             *len = SvCUR(scalar);
155             return SvPVX(scalar);
156             #else
157 6 50         return SvPVutf8(scalar, *len);
158             #endif
159             }
160              
161 775           return string;
162             }
163              
164 335           static const char *get_perl_scalar_string_value(pTHX_ SV *scalar, STRLEN *len, const char *name, bool utf8)
165             {
166             const char *string;
167              
168 335           string = get_perl_scalar_value(aTHX_ scalar, len, utf8, false);
169 335 100         if (!string) {
170 4           carp(CARP_WARN, "Use of uninitialized value for %s", name);
171 4           *len = 0;
172 4           return "";
173             }
174              
175 331           return string;
176             }
177              
178 584           static SV *get_perl_hash_scalar(pTHX_ HV *hash, const char *key)
179             {
180             I32 klen;
181             SV **scalar_ptr;
182              
183 584           klen = strlen(key);
184              
185 584 50         if (!hv_exists(hash, key, klen))
186 0           return NULL;
187              
188 584           scalar_ptr = hv_fetch(hash, key, klen, 0);
189 584 50         if (!scalar_ptr)
190 0           return NULL;
191              
192 584           return *scalar_ptr;
193             }
194              
195 292           static const char *get_perl_hash_value(pTHX_ HV *hash, const char *key, STRLEN *len, bool utf8, bool *taint)
196             {
197             SV *scalar;
198              
199 292           scalar = get_perl_hash_scalar(aTHX_ hash, key);
200 292 50         if (!scalar)
201 0           return NULL;
202              
203 292 100         if (!*taint && SvTAINTED(scalar))
    100          
    100          
204 8           *taint = true;
205              
206 292           return get_perl_scalar_value(aTHX_ scalar, len, utf8, true);
207             }
208              
209 300           static void set_perl_hash_value(pTHX_ HV *hash, const char *key, const char *value, bool utf8, bool taint)
210             {
211             I32 klen;
212             SV *scalar;
213              
214 300           klen = strlen(key);
215              
216 300 100         if (value)
217 228           scalar = newSVpv(value, 0);
218             else
219 72           scalar = newSV(0);
220              
221 300 100         if (utf8 && value)
    50          
222 5           sv_utf8_decode(scalar);
223              
224 300 100         if (taint)
225 5 50         SvTAINTED_on(scalar);
226              
227 300           (void)hv_store(hash, key, klen, scalar, 0);
228 300           }
229              
230 29           static HV *get_perl_class_from_perl_cv(pTHX_ CV *cv)
231             {
232             GV *gv;
233             HV *class;
234              
235 29           class = NULL;
236 29           gv = CvGV(cv);
237              
238 29 50         if (gv)
239 29           class = GvSTASH(gv);
240              
241 29 50         if (!class)
242 0           class = CvSTASH(cv);
243              
244 29 50         if (!class)
245 0           class = PL_curstash;
246              
247 29 50         if (!class)
248 0           carp(CARP_DIE, "Cannot retrieve class");
249              
250 29           return class;
251             }
252              
253 14           static HV *get_perl_class_from_perl_scalar(pTHX_ SV *scalar)
254             {
255             HV *class;
256             STRLEN class_len;
257             const char *class_name;
258              
259 14           class_name = get_perl_scalar_string_value(aTHX_ scalar, &class_len, "class", true);
260              
261 14 50         if (class_len == 0) {
262 0           carp(CARP_WARN, "Explicit blessing to '' (assuming package main)");
263 0           class_name = "main";
264             }
265              
266 14           class = gv_stashpvn(class_name, class_len, GV_ADD | SVf_UTF8);
267 14 50         if (!class)
268 0           carp(CARP_DIE, "Cannot retrieve class %s", class_name);
269              
270 14           return class;
271             }
272              
273 43           static HV *get_perl_class_from_perl_scalar_or_cv(pTHX_ SV *scalar, CV *cv)
274             {
275 43 100         if (scalar)
276 14           return get_perl_class_from_perl_scalar(aTHX_ scalar);
277             else
278 29           return get_perl_class_from_perl_cv(aTHX_ cv);
279             }
280              
281 165           static bool is_class_object(pTHX_ SV *class, SV *object)
282             {
283 165           dSP;
284             SV *sv;
285             bool ret;
286             int count;
287              
288 165 100         if (!sv_isobject(object))
289 8           return false;
290              
291 157           ENTER;
292 157           SAVETMPS;
293              
294 157 50         PUSHMARK(SP);
295 157 50         EXTEND(SP, 2);
296              
297 157 50         XPUSHs(sv_2mortal(newSVsv(object)));
298 157 50         XPUSHs(sv_2mortal(newSVsv(class)));
299              
300 157           PUTBACK;
301              
302 157           count = call_method("isa", G_SCALAR);
303              
304 157           SPAGAIN;
305              
306 157 50         if (count > 0) {
307 157           sv = POPs;
308 157 50         ret = SvTRUE(sv);
    50          
    0          
    50          
    0          
    0          
    50          
    50          
    50          
    100          
    50          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
309             } else {
310 0           ret = false;
311             }
312              
313 157           PUTBACK;
314 157 50         FREETMPS;
315 157           LEAVE;
316              
317 157           return ret;
318             }
319              
320 14           static void fill_element_message(char *buffer, size_t len, I32 index1, I32 index2)
321             {
322             static const char message[] = "Element at index ";
323              
324 14 50         if (len < 10 || buffer[0])
    50          
325 0           return;
326              
327 14 50         if (len+10+1+10 < sizeof(message)) {
328 0           buffer[0] = 0;
329 0           return;
330             }
331              
332 14 50         if (index2 == -1) {
333 14           strcpy(buffer, "Argument");
334 14           return;
335             }
336              
337 0           memcpy(buffer, message, sizeof(message));
338              
339 0 0         if (index1 == -1)
340 0           sprintf(buffer+sizeof(message)-1, "%d", (int)index2);
341             else
342 0           sprintf(buffer+sizeof(message)-1, "%d/%d", (int)index1, (int)index2);
343             }
344              
345 154           static HV* get_object_hash_from_perl_array(pTHX_ AV *array, I32 index1, I32 index2, SV *class, bool warn)
346             {
347             SV *scalar;
348             SV *object;
349             SV **object_ptr;
350 154           char buffer[40] = { 0 };
351              
352             #ifdef WITHOUT_SvPV_nomg
353             warn = true;
354             #endif
355              
356 154 100         object_ptr = av_fetch(array, (index2 == -1 ? 0 : index2), 0);
357 154 50         if (!object_ptr) {
358 0 0         if (warn) {
359 0           fill_element_message(buffer, sizeof(buffer), index1, index2);
360 0           carp(CARP_WARN, "%s is NULL", buffer);
361             }
362 0           return NULL;
363             }
364              
365 154           object = *object_ptr;
366 154 100         if (!is_class_object(aTHX_ class, object)) {
367 8 100         if (warn) {
368 4           fill_element_message(buffer, sizeof(buffer), index1, index2);
369 4 50         carp(CARP_WARN, "%s is not %s object", buffer, SvPV_nolen(class));
370             }
371 8           return NULL;
372             }
373              
374 146           scalar = SvRV(object);
375 146 50         if (SvTYPE(scalar) != SVt_PVHV) {
376 0 0         if (warn) {
377 0           fill_element_message(buffer, sizeof(buffer), index1, index2);
378 0           carp(CARP_WARN, "%s is not HASH reference", buffer);
379             }
380 0           return NULL;
381             }
382              
383 154           return (HV *)scalar;
384              
385             }
386              
387 77           static void message_address_add_from_perl_array(pTHX_ struct message_address **first_address, struct message_address **last_address, bool utf8, bool *taint, AV *array, I32 index1, I32 index2, SV *class)
388             {
389             HV *hash;
390             const char *name;
391             const char *mailbox;
392             const char *domain;
393             const char *comment;
394             STRLEN name_len;
395             STRLEN mailbox_len;
396             STRLEN domain_len;
397             STRLEN comment_len;
398 77           char buffer[40] = { 0 };
399              
400 77           hash = get_object_hash_from_perl_array(aTHX_ array, index1, index2, class, false);
401 77 100         if (!hash)
402 10           return;
403              
404 73           name = get_perl_hash_value(aTHX_ hash, "phrase", &name_len, utf8, taint);
405 73           mailbox = get_perl_hash_value(aTHX_ hash, "user", &mailbox_len, utf8, taint);
406 73           domain = get_perl_hash_value(aTHX_ hash, "host", &domain_len, utf8, taint);
407 73           comment = get_perl_hash_value(aTHX_ hash, "comment", &comment_len, utf8, taint);
408              
409              
410 73 100         if (name && string_contains_nul(name, name_len)) {
    100          
411 1           fill_element_message(buffer, sizeof(buffer), index1, index2);
412 1           carp(CARP_WARN, "%s contains nul character in phrase", buffer);
413             }
414              
415 73 100         if (mailbox && string_contains_nul(mailbox, mailbox_len)) {
    100          
416 1           fill_element_message(buffer, sizeof(buffer), index1, index2);
417 1           carp(CARP_WARN, "%s contains nul character in user portion of address", buffer);
418             }
419              
420 73 100         if (domain && string_contains_nul(domain, domain_len)) {
    100          
421 1           fill_element_message(buffer, sizeof(buffer), index1, index2);
422 1           carp(CARP_WARN, "%s contains nul character in host portion of address", buffer);
423             }
424              
425 73 100         if (comment && string_contains_nul(comment, comment_len)) {
    100          
426 1           fill_element_message(buffer, sizeof(buffer), index1, index2);
427 1           carp(CARP_WARN, "%s contains nul character in comment", buffer);
428             }
429              
430 73 100         if (mailbox && !mailbox[0])
    100          
431 1           mailbox = NULL;
432              
433 73 100         if (domain && !domain[0])
    100          
434 1           domain = NULL;
435              
436 73 100         if (!mailbox && !domain) {
    50          
437 6           fill_element_message(buffer, sizeof(buffer), index1, index2);
438 6           carp(CARP_WARN, "%s contains empty address", buffer);
439 6           return;
440             }
441              
442 67 50         if (!mailbox) {
443 0           fill_element_message(buffer, sizeof(buffer), index1, index2);
444 0           carp(CARP_WARN, "%s contains empty user portion of address", buffer);
445 0           mailbox = "";
446             }
447              
448 67 50         if (!domain) {
449 0           fill_element_message(buffer, sizeof(buffer), index1, index2);
450 0           carp(CARP_WARN, "%s contains empty host portion of address", buffer);
451 0           domain = "";
452             }
453              
454 67           message_address_add(first_address, last_address, name, NULL, mailbox, domain, comment);
455             }
456              
457 142           static AV *get_perl_array_from_scalar(SV *scalar, const char *group_name, bool warn)
458             {
459             SV *scalar_ref;
460              
461             #ifdef WITHOUT_SvPV_nomg
462             warn = true;
463             #endif
464              
465 142 50         if (scalar && !SvROK(scalar)) {
    100          
466 4 100         if (warn)
467 2           carp(CARP_WARN, "Value for group '%s' is not reference", group_name);
468 4           return NULL;
469             }
470              
471 138           scalar_ref = SvRV(scalar);
472              
473 138 50         if (!scalar_ref || SvTYPE(scalar_ref) != SVt_PVAV) {
    50          
474 0 0         if (warn)
475 0           carp(CARP_WARN, "Value for group '%s' is not ARRAY reference", group_name);
476 0           return NULL;
477             }
478              
479 138           return (AV *)scalar_ref;
480             }
481              
482 71           static void message_address_add_from_perl_group(pTHX_ struct message_address **first_address, struct message_address **last_address, bool utf8, bool *taint, SV *scalar_group, SV *scalar_list, I32 index1, SV *class)
483             {
484             I32 len;
485             I32 index2;
486             AV *array;
487             STRLEN group_len;
488             const char *group_name;
489              
490 71           group_name = get_perl_scalar_value(aTHX_ scalar_group, &group_len, utf8, true);
491 71           array = get_perl_array_from_scalar(scalar_list, group_name, false);
492 71 100         len = array ? (av_len(array) + 1) : 0;
493              
494 71 100         if (index1 == -1 && group_name)
    100          
495 10           index1 = 0;
496              
497 71 100         if (group_name && string_contains_nul(group_name, group_len))
    50          
498 0           carp(CARP_WARN, "Group name '%s' contains nul character", group_name);
499              
500 71 100         if (group_name)
501 20           message_address_add(first_address, last_address, NULL, NULL, group_name, NULL, NULL);
502              
503 148 100         for (index2 = 0; index2 < len; ++index2)
504 77 100         message_address_add_from_perl_array(aTHX_ first_address, last_address, utf8, taint, array, index1, ((index1 == -1 && len == 1) ? -1 : index2), class);
    100          
505              
506 71 100         if (group_name)
507 20           message_address_add(first_address, last_address, NULL, NULL, NULL, NULL, NULL);
508              
509 71 100         if (!*taint && SvTAINTED(scalar_group))
    100          
    50          
510 0           *taint = true;
511 71           }
512              
513             #ifndef WITHOUT_SvPV_nomg
514 71           static bool perl_group_needs_utf8(pTHX_ SV *scalar_group, SV *scalar_list, I32 index1, SV *class)
515             {
516             I32 len;
517             I32 index2;
518             SV *scalar;
519             HV *hash;
520             AV *array;
521             STRLEN len_na;
522             bool utf8;
523             const char *group_name;
524             const char **hash_key_ptr;
525              
526             static const char *hash_keys[] = { "phrase", "user", "host", "comment", NULL };
527              
528 71           utf8 = false;
529              
530 71           group_name = get_perl_scalar_value(aTHX_ scalar_group, &len_na, false, false);
531 71 100         if (SvUTF8(scalar_group))
532 1           utf8 = true;
533              
534 71 100         if (index1 == -1 && group_name)
    100          
535 10           index1 = 0;
536              
537 71           array = get_perl_array_from_scalar(scalar_list, group_name, true);
538 71 100         len = array ? (av_len(array) + 1) : 0;
539              
540 148 100         for (index2 = 0; index2 < len; ++index2) {
541 77 100         hash = get_object_hash_from_perl_array(aTHX_ array, index1, ((index1 == -1 && len == 1) ? -1 : index2), class, true);
    100          
542 77 100         if (!hash)
543 4           continue;
544 365 100         for (hash_key_ptr = hash_keys; *hash_key_ptr; ++hash_key_ptr) {
545 292           scalar = get_perl_hash_scalar(aTHX_ hash, *hash_key_ptr);
546 292 50         if (scalar && get_perl_scalar_value(aTHX_ scalar, &len_na, false, false) && SvUTF8(scalar))
    100          
    100          
547 14           utf8 = true;
548             }
549             }
550              
551 71           return utf8;
552             }
553             #endif
554              
555 41           static int count_address_groups(struct message_address *first_address)
556             {
557             int count;
558             bool in_group;
559             struct message_address *address;
560              
561 41           count = 0;
562 41           in_group = false;
563              
564 119 100         for (address = first_address; address; address = address->next) {
565 78 100         if (!address->domain)
566 18           in_group = !in_group;
567 78 100         if (in_group)
568 19           continue;
569 59           ++count;
570             }
571              
572 41           return count;
573             }
574              
575 81           static bool get_next_perl_address_group(pTHX_ struct message_address **address, SV **group_scalar, SV **addresses_scalar, HV *class, bool utf8, bool taint)
576             {
577             HV *hash;
578             SV *object;
579             SV *hash_ref;
580             bool in_group;
581             AV *addresses_array;
582              
583 81 100         if (!*address)
584 41           return false;
585              
586 40           in_group = !(*address)->domain;
587              
588 40 100         if (in_group && (*address)->mailbox)
    50          
589 9           *group_scalar = sv_2mortal(newSVpv((*address)->mailbox, 0));
590             else
591 31           *group_scalar = sv_newmortal();
592              
593 40 100         if (utf8 && in_group && (*address)->mailbox)
    50          
    50          
594 1           sv_utf8_decode(*group_scalar);
595              
596 40 100         if (taint)
597 1 50         SvTAINTED_on(*group_scalar);
598              
599 40           addresses_array = newAV();
600 40           *addresses_scalar = sv_2mortal(newRV_noinc((SV *)addresses_array));
601              
602 40 100         if (in_group)
603 9           *address = (*address)->next;
604              
605 100 100         while (*address && (*address)->domain) {
    100          
606 60           hash = newHV();
607              
608 60           set_perl_hash_value(aTHX_ hash, "phrase", (*address)->name, utf8, taint);
609 60 50         set_perl_hash_value(aTHX_ hash, "user", ( (*address)->mailbox && (*address)->mailbox[0] ) ? (*address)->mailbox : NULL, utf8, taint);
    100          
610 60 50         set_perl_hash_value(aTHX_ hash, "host", ( (*address)->domain && (*address)->domain[0] ) ? (*address)->domain : NULL, utf8, taint);
    100          
611 60           set_perl_hash_value(aTHX_ hash, "comment", (*address)->comment, utf8, taint);
612 60           set_perl_hash_value(aTHX_ hash, "original", (*address)->original, utf8, taint);
613              
614 60 100         if ((*address)->invalid_syntax)
615 4           (void)hv_store(hash, "invalid", sizeof("invalid")-1, newSViv(1), 0);
616              
617 60           hash_ref = newRV_noinc((SV *)hash);
618 60           object = sv_bless(hash_ref, class);
619              
620 60           av_push(addresses_array, object);
621              
622 60           *address = (*address)->next;
623             }
624              
625 40 100         if (in_group && *address)
    50          
626 9           *address = (*address)->next;
627              
628 40           return true;
629             }
630              
631              
632             MODULE = Email::Address::XS PACKAGE = Email::Address::XS
633              
634             PROTOTYPES: DISABLE
635              
636             void
637             format_email_groups(...)
638             PREINIT:
639             I32 i;
640             bool utf8;
641             bool taint;
642             char *string;
643             struct message_address *first_address;
644             struct message_address *last_address;
645             SV *string_scalar;
646             INPUT:
647             SV *this_class = sv_2mortal(newSVpvn_utf8("$Package", sizeof("$Package")-1, 1));
648             INIT:
649 64 100         if (items % 2 == 1) {
650 1           carp(CARP_WARN, "Odd number of elements in argument list");
651 1           XSRETURN_UNDEF;
652             }
653             PPCODE:
654 63           first_address = NULL;
655 63           last_address = NULL;
656 63           taint = false;
657             #ifndef WITHOUT_SvPV_nomg
658 63           utf8 = false;
659 134 100         for (i = 0; i < items; i += 2)
660 71 100         if (perl_group_needs_utf8(aTHX_ ST(i), ST(i+1), (items == 2 ? -1 : i), this_class))
    100          
661 5           utf8 = true;
662             #else
663             utf8 = true;
664             #endif
665 134 100         for (i = 0; i < items; i += 2)
666 71 100         message_address_add_from_perl_group(aTHX_ &first_address, &last_address, utf8, &taint, ST(i), ST(i+1), (items == 2 ? -1 : i), this_class);
667 63           message_address_write(&string, first_address);
668 63           message_address_free(&first_address);
669 63           string_scalar = sv_2mortal(newSVpv(string, 0));
670 63           string_free(string);
671 63 100         if (utf8)
672 4           sv_utf8_decode(string_scalar);
673 63 100         if (taint)
674 8 50         SvTAINTED_on(string_scalar);
675 63 50         EXTEND(SP, 1);
676 63           PUSHs(string_scalar);
677              
678             void
679             parse_email_groups(...)
680             PREINIT:
681             SV *string_scalar;
682             SV *class_scalar;
683             int count;
684             HV *hv_class;
685             SV *group_scalar;
686             SV *addresses_scalar;
687             bool utf8;
688             bool taint;
689             STRLEN input_len;
690             const char *input;
691             const char *class_name;
692             struct message_address *address;
693             struct message_address *first_address;
694             INPUT:
695             const char *this_class_name = "$Package";
696             STRLEN this_class_len = sizeof("$Package")-1;
697             INIT:
698 43 50         string_scalar = items >= 1 ? ST(0) : &PL_sv_undef;
699 43 100         class_scalar = items >= 2 ? ST(1) : NULL;
700 43           input = get_perl_scalar_string_value(aTHX_ string_scalar, &input_len, "string", false);
701 43           utf8 = SvUTF8(string_scalar);
702 43 100         taint = SvTAINTED(string_scalar);
    100          
703 43           hv_class = get_perl_class_from_perl_scalar_or_cv(aTHX_ class_scalar, cv);
704 43 100         if (class_scalar && !sv_derived_from_pvn(class_scalar, this_class_name, this_class_len, SVf_UTF8)) {
    100          
705 2 50         class_name = HvNAME(hv_class);
    50          
    50          
    0          
    50          
    50          
706 2 50         carp(CARP_WARN, "Class %s is not derived from %s", (class_name ? class_name : "(unknown)"), this_class_name);
707 2           XSRETURN_EMPTY;
708             }
709             PPCODE:
710 41           first_address = message_address_parse(input, input_len, UINT_MAX, false);
711 41           count = count_address_groups(first_address);
712 41 50         EXTEND(SP, count * 2);
    50          
713 41           address = first_address;
714 81 100         while (get_next_perl_address_group(aTHX_ &address, &group_scalar, &addresses_scalar, hv_class, utf8, taint)) {
715 40           PUSHs(group_scalar);
716 40           PUSHs(addresses_scalar);
717             }
718 41           message_address_free(&first_address);
719              
720             void
721             compose_address(...)
722             PREINIT:
723             char *string;
724             const char *mailbox;
725             const char *domain;
726             STRLEN mailbox_len;
727             STRLEN domain_len;
728             bool utf8;
729             bool taint;
730             SV *mailbox_scalar;
731             SV *domain_scalar;
732             SV *string_scalar;
733             INIT:
734 93 50         mailbox_scalar = items >= 1 ? ST(0) : &PL_sv_undef;
735 93 50         domain_scalar = items >= 2 ? ST(1) : &PL_sv_undef;
736 93           mailbox = get_perl_scalar_string_value(aTHX_ mailbox_scalar, &mailbox_len, "mailbox", true);
737 93           domain = get_perl_scalar_string_value(aTHX_ domain_scalar, &domain_len, "domain", true);
738 93 50         utf8 = (SvUTF8(mailbox_scalar) || SvUTF8(domain_scalar));
    50          
739 93 100         taint = (SvTAINTED(mailbox_scalar) || SvTAINTED(domain_scalar));
    50          
    100          
    50          
740 93 50         if (string_contains_nul(mailbox, mailbox_len))
741 0           carp(CARP_WARN, "Nul character in user portion of address");
742 93 50         if (string_contains_nul(domain, domain_len))
743 0           carp(CARP_WARN, "Nul character in host portion of address");
744             PPCODE:
745 93           compose_address(&string, mailbox, domain);
746 93           string_scalar = sv_2mortal(newSVpv(string, 0));
747 93           string_free(string);
748 93 50         if (utf8)
749 0           sv_utf8_decode(string_scalar);
750 93 100         if (taint)
751 7 50         SvTAINTED_on(string_scalar);
752 93 50         EXTEND(SP, 1);
753 93           PUSHs(string_scalar);
754              
755             void
756             split_address(...)
757             PREINIT:
758             const char *string;
759             char *mailbox;
760             char *domain;
761             STRLEN string_len;
762             bool utf8;
763             bool taint;
764             SV *string_scalar;
765             SV *mailbox_scalar;
766             SV *domain_scalar;
767             INIT:
768 92 50         string_scalar = items >= 1 ? ST(0) : &PL_sv_undef;
769 92           string = get_perl_scalar_string_value(aTHX_ string_scalar, &string_len, "string", false);
770 92           utf8 = SvUTF8(string_scalar);
771 92 100         taint = SvTAINTED(string_scalar);
    50          
772             PPCODE:
773 92           split_address(string, string_len, &mailbox, &domain);
774 92 100         mailbox_scalar = mailbox ? sv_2mortal(newSVpv(mailbox, 0)) : sv_newmortal();
775 92 100         domain_scalar = domain ? sv_2mortal(newSVpv(domain, 0)) : sv_newmortal();
776 92           string_free(mailbox);
777 92           string_free(domain);
778 92 50         if (utf8) {
779 0           sv_utf8_decode(mailbox_scalar);
780 0           sv_utf8_decode(domain_scalar);
781             }
782 92 100         if (taint) {
783 2 50         SvTAINTED_on(mailbox_scalar);
784 2 50         SvTAINTED_on(domain_scalar);
785             }
786 92 50         EXTEND(SP, 2);
787 92           PUSHs(mailbox_scalar);
788 92           PUSHs(domain_scalar);
789              
790             bool
791             is_obj(...)
792             PREINIT:
793 11 50         SV *class = items >= 1 ? ST(0) : &PL_sv_undef;
794 11 50         SV *object = items >= 2 ? ST(1) : &PL_sv_undef;
795             CODE:
796 11           RETVAL = is_class_object(aTHX_ class, object);
797             OUTPUT:
798             RETVAL