780 lines
18 KiB
Plaintext
780 lines
18 KiB
Plaintext
/* Copyright (c) 2015-2018 by Pali <pali@cpan.org> */
|
|
|
|
#define PERL_NO_GET_CONTEXT
|
|
#include "EXTERN.h"
|
|
#include "perl.h"
|
|
#include "XSUB.h"
|
|
|
|
#include "dovecot-parser.h"
|
|
|
|
/* Perl pre 5.6.1 support */
|
|
#if PERL_VERSION < 6 || (PERL_VERSION == 6 && PERL_SUBVERSION < 1)
|
|
#define BROKEN_SvPVutf8
|
|
#endif
|
|
|
|
/* Perl pre 5.7.2 support */
|
|
#ifndef SvPV_nomg
|
|
#define WITHOUT_SvPV_nomg
|
|
#endif
|
|
|
|
/* Perl pre 5.8.0 support */
|
|
#ifndef UTF8_IS_INVARIANT
|
|
#define UTF8_IS_INVARIANT(c) (((U8)c) < 0x80)
|
|
#endif
|
|
|
|
/* Perl pre 5.9.5 support */
|
|
#ifndef SVfARG
|
|
#define SVfARG(p) ((void*)(p))
|
|
#endif
|
|
|
|
/* Perl pre 5.13.1 support */
|
|
#ifndef warn_sv
|
|
#define warn_sv(scalar) warn("%" SVf, SVfARG(scalar))
|
|
#endif
|
|
#ifndef croak_sv
|
|
#define croak_sv(scalar) croak("%" SVf, SVfARG(scalar))
|
|
#endif
|
|
|
|
/* Perl pre 5.15.4 support */
|
|
#ifndef sv_derived_from_pvn
|
|
#define sv_derived_from_pvn(scalar, name, len, flags) sv_derived_from(scalar, name)
|
|
#endif
|
|
|
|
/* Exported i_panic function for other C files */
|
|
void i_panic(const char *format, ...)
|
|
{
|
|
dTHX;
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
vcroak(format, &args);
|
|
va_end(args);
|
|
}
|
|
|
|
static void append_carp_shortmess(pTHX_ SV *scalar)
|
|
{
|
|
dSP;
|
|
int count;
|
|
|
|
ENTER;
|
|
SAVETMPS;
|
|
PUSHMARK(SP);
|
|
|
|
count = call_pv("Carp::shortmess", G_SCALAR);
|
|
|
|
SPAGAIN;
|
|
|
|
if (count > 0)
|
|
sv_catsv(scalar, POPs);
|
|
|
|
PUTBACK;
|
|
FREETMPS;
|
|
LEAVE;
|
|
}
|
|
|
|
#define CARP_WARN false
|
|
#define CARP_DIE true
|
|
static void carp(bool fatal, const char *format, ...)
|
|
{
|
|
dTHX;
|
|
va_list args;
|
|
SV *scalar;
|
|
|
|
va_start(args, format);
|
|
scalar = sv_2mortal(vnewSVpvf(format, &args));
|
|
va_end(args);
|
|
|
|
append_carp_shortmess(aTHX_ scalar);
|
|
|
|
if (!fatal)
|
|
warn_sv(scalar);
|
|
else
|
|
croak_sv(scalar);
|
|
}
|
|
|
|
static bool string_needs_utf8_upgrade(const char *str, STRLEN len)
|
|
{
|
|
STRLEN i;
|
|
|
|
for (i = 0; i < len; ++i)
|
|
if (!UTF8_IS_INVARIANT(str[i]))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static const char *get_perl_scalar_value(pTHX_ SV *scalar, STRLEN *len, bool utf8, bool nomg)
|
|
{
|
|
const char *string;
|
|
|
|
#ifndef WITHOUT_SvPV_nomg
|
|
if (!nomg)
|
|
SvGETMAGIC(scalar);
|
|
|
|
if (!SvOK(scalar))
|
|
return NULL;
|
|
|
|
string = SvPV_nomg(scalar, *len);
|
|
#else
|
|
COP cop;
|
|
|
|
if (!SvGMAGICAL(scalar) && !SvOK(scalar))
|
|
return NULL;
|
|
|
|
/* Temporary turn off all warnings because SvPV can throw uninitialized warning */
|
|
cop = *PL_curcop;
|
|
cop.cop_warnings = pWARN_NONE;
|
|
|
|
ENTER;
|
|
SAVEVPTR(PL_curcop);
|
|
PL_curcop = &cop;
|
|
|
|
string = SvPV(scalar, *len);
|
|
|
|
LEAVE;
|
|
|
|
if (SvGMAGICAL(scalar) && !SvOK(scalar))
|
|
return NULL;
|
|
#endif
|
|
|
|
if (utf8 && !SvUTF8(scalar) && string_needs_utf8_upgrade(string, *len)) {
|
|
scalar = sv_2mortal(newSVpvn(string, *len));
|
|
#ifdef BROKEN_SvPVutf8
|
|
sv_utf8_upgrade(scalar);
|
|
*len = SvCUR(scalar);
|
|
return SvPVX(scalar);
|
|
#else
|
|
return SvPVutf8(scalar, *len);
|
|
#endif
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
static const char *get_perl_scalar_string_value(pTHX_ SV *scalar, STRLEN *len, const char *name, bool utf8)
|
|
{
|
|
const char *string;
|
|
|
|
string = get_perl_scalar_value(aTHX_ scalar, len, utf8, false);
|
|
if (!string) {
|
|
carp(CARP_WARN, "Use of uninitialized value for %s", name);
|
|
*len = 0;
|
|
return "";
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
static SV *get_perl_hash_scalar(pTHX_ HV *hash, const char *key)
|
|
{
|
|
I32 klen;
|
|
SV **scalar_ptr;
|
|
|
|
klen = strlen(key);
|
|
|
|
if (!hv_exists(hash, key, klen))
|
|
return NULL;
|
|
|
|
scalar_ptr = hv_fetch(hash, key, klen, 0);
|
|
if (!scalar_ptr)
|
|
return NULL;
|
|
|
|
return *scalar_ptr;
|
|
}
|
|
|
|
static const char *get_perl_hash_value(pTHX_ HV *hash, const char *key, STRLEN *len, bool utf8, bool *taint)
|
|
{
|
|
SV *scalar;
|
|
|
|
scalar = get_perl_hash_scalar(aTHX_ hash, key);
|
|
if (!scalar)
|
|
return NULL;
|
|
|
|
if (!*taint && SvTAINTED(scalar))
|
|
*taint = true;
|
|
|
|
return get_perl_scalar_value(aTHX_ scalar, len, utf8, true);
|
|
}
|
|
|
|
static void set_perl_hash_value(pTHX_ HV *hash, const char *key, const char *value, STRLEN len, bool utf8, bool taint)
|
|
{
|
|
I32 klen;
|
|
SV *scalar;
|
|
|
|
klen = strlen(key);
|
|
|
|
if (!len && value && value[0])
|
|
value = NULL;
|
|
|
|
if (value)
|
|
scalar = newSVpvn(value, len);
|
|
else
|
|
scalar = newSV(0);
|
|
|
|
if (utf8 && value)
|
|
sv_utf8_decode(scalar);
|
|
|
|
if (taint)
|
|
SvTAINTED_on(scalar);
|
|
|
|
(void)hv_store(hash, key, klen, scalar, 0);
|
|
}
|
|
|
|
static HV *get_perl_class_from_perl_cv(pTHX_ CV *cv)
|
|
{
|
|
GV *gv;
|
|
HV *class;
|
|
|
|
class = NULL;
|
|
gv = CvGV(cv);
|
|
|
|
if (gv)
|
|
class = GvSTASH(gv);
|
|
|
|
if (!class)
|
|
class = CvSTASH(cv);
|
|
|
|
if (!class)
|
|
class = PL_curstash;
|
|
|
|
if (!class)
|
|
carp(CARP_DIE, "Cannot retrieve class");
|
|
|
|
return class;
|
|
}
|
|
|
|
static HV *get_perl_class_from_perl_scalar(pTHX_ SV *scalar)
|
|
{
|
|
HV *class;
|
|
STRLEN class_len;
|
|
const char *class_name;
|
|
|
|
class_name = get_perl_scalar_string_value(aTHX_ scalar, &class_len, "class", true);
|
|
|
|
if (class_len == 0) {
|
|
carp(CARP_WARN, "Explicit blessing to '' (assuming package main)");
|
|
class_name = "main";
|
|
class_len = strlen(class_name);
|
|
}
|
|
|
|
class = gv_stashpvn(class_name, class_len, GV_ADD | SVf_UTF8);
|
|
if (!class)
|
|
carp(CARP_DIE, "Cannot retrieve class %" SVf, SVfARG(scalar));
|
|
|
|
return class;
|
|
}
|
|
|
|
static HV *get_perl_class_from_perl_scalar_or_cv(pTHX_ SV *scalar, CV *cv)
|
|
{
|
|
if (scalar)
|
|
return get_perl_class_from_perl_scalar(aTHX_ scalar);
|
|
else
|
|
return get_perl_class_from_perl_cv(aTHX_ cv);
|
|
}
|
|
|
|
static bool is_class_object(pTHX_ SV *class, const char *class_name, STRLEN class_len, SV *object)
|
|
{
|
|
dSP;
|
|
SV *sv;
|
|
bool ret;
|
|
int count;
|
|
|
|
if (!sv_isobject(object))
|
|
return false;
|
|
|
|
ENTER;
|
|
SAVETMPS;
|
|
|
|
PUSHMARK(SP);
|
|
EXTEND(SP, 2);
|
|
|
|
if (class) {
|
|
sv = newSVsv(class);
|
|
} else {
|
|
sv = newSVpvn(class_name, class_len);
|
|
SvUTF8_on(sv);
|
|
}
|
|
|
|
PUSHs(sv_2mortal(newSVsv(object)));
|
|
PUSHs(sv_2mortal(sv));
|
|
|
|
PUTBACK;
|
|
|
|
count = call_method("isa", G_SCALAR);
|
|
|
|
SPAGAIN;
|
|
|
|
if (count > 0) {
|
|
sv = POPs;
|
|
ret = SvTRUE(sv);
|
|
} else {
|
|
ret = false;
|
|
}
|
|
|
|
PUTBACK;
|
|
FREETMPS;
|
|
LEAVE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void fill_element_message(char *buffer, size_t len, I32 index1, I32 index2)
|
|
{
|
|
static const char message[] = "Element at index ";
|
|
|
|
if (len < 10 || buffer[0])
|
|
return;
|
|
|
|
if (len+10+1+10 < sizeof(message)) {
|
|
buffer[0] = 0;
|
|
return;
|
|
}
|
|
|
|
if (index2 == -1) {
|
|
strcpy(buffer, "Argument");
|
|
return;
|
|
}
|
|
|
|
memcpy(buffer, message, sizeof(message));
|
|
|
|
if (index1 == -1)
|
|
sprintf(buffer+sizeof(message)-1, "%d", (int)index2);
|
|
else
|
|
sprintf(buffer+sizeof(message)-1, "%d/%d", (int)index1, (int)index2);
|
|
}
|
|
|
|
static HV* get_object_hash_from_perl_array(pTHX_ AV *array, I32 index1, I32 index2, const char *class_name, STRLEN class_len, bool warn)
|
|
{
|
|
SV *scalar;
|
|
SV *object;
|
|
SV **object_ptr;
|
|
char buffer[40] = { 0 };
|
|
|
|
#ifdef WITHOUT_SvPV_nomg
|
|
warn = true;
|
|
#endif
|
|
|
|
object_ptr = av_fetch(array, (index2 == -1 ? 0 : index2), 0);
|
|
if (!object_ptr) {
|
|
if (warn) {
|
|
fill_element_message(buffer, sizeof(buffer), index1, index2);
|
|
carp(CARP_WARN, "%s is NULL", buffer);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
object = *object_ptr;
|
|
if (!is_class_object(aTHX_ NULL, class_name, class_len, object)) {
|
|
if (warn) {
|
|
fill_element_message(buffer, sizeof(buffer), index1, index2);
|
|
carp(CARP_WARN, "%s is not %s object", buffer, class_name);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
scalar = SvRV(object);
|
|
if (SvTYPE(scalar) != SVt_PVHV) {
|
|
if (warn) {
|
|
fill_element_message(buffer, sizeof(buffer), index1, index2);
|
|
carp(CARP_WARN, "%s is not HASH reference", buffer);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return (HV *)scalar;
|
|
|
|
}
|
|
|
|
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, const char *class_name, STRLEN class_len)
|
|
{
|
|
HV *hash;
|
|
const char *name;
|
|
const char *mailbox;
|
|
const char *domain;
|
|
const char *comment;
|
|
STRLEN name_len;
|
|
STRLEN mailbox_len;
|
|
STRLEN domain_len;
|
|
STRLEN comment_len;
|
|
char buffer[40] = { 0 };
|
|
|
|
hash = get_object_hash_from_perl_array(aTHX_ array, index1, index2, class_name, class_len, false);
|
|
if (!hash)
|
|
return;
|
|
|
|
name = get_perl_hash_value(aTHX_ hash, "phrase", &name_len, utf8, taint);
|
|
mailbox = get_perl_hash_value(aTHX_ hash, "user", &mailbox_len, utf8, taint);
|
|
domain = get_perl_hash_value(aTHX_ hash, "host", &domain_len, utf8, taint);
|
|
comment = get_perl_hash_value(aTHX_ hash, "comment", &comment_len, utf8, taint);
|
|
|
|
if (domain && !domain[0] && domain_len == 0)
|
|
domain = NULL;
|
|
|
|
if (!mailbox && !domain) {
|
|
fill_element_message(buffer, sizeof(buffer), index1, index2);
|
|
carp(CARP_WARN, "%s contains empty address", buffer);
|
|
return;
|
|
}
|
|
|
|
if (!mailbox) {
|
|
fill_element_message(buffer, sizeof(buffer), index1, index2);
|
|
carp(CARP_WARN, "%s contains empty user portion of address", buffer);
|
|
return;
|
|
}
|
|
|
|
if (!domain) {
|
|
fill_element_message(buffer, sizeof(buffer), index1, index2);
|
|
carp(CARP_WARN, "%s contains empty host portion of address", buffer);
|
|
return;
|
|
}
|
|
|
|
message_address_add(first_address, last_address, name, name_len, NULL, 0, mailbox, mailbox_len, domain, domain_len, comment, comment_len);
|
|
}
|
|
|
|
static AV *get_perl_array_from_scalar(SV *scalar, const char *group_name, bool warn)
|
|
{
|
|
SV *scalar_ref;
|
|
|
|
#ifdef WITHOUT_SvPV_nomg
|
|
warn = true;
|
|
#endif
|
|
|
|
if (scalar && !SvROK(scalar)) {
|
|
if (warn)
|
|
carp(CARP_WARN, "Value for group '%s' is not reference", group_name);
|
|
return NULL;
|
|
}
|
|
|
|
scalar_ref = SvRV(scalar);
|
|
|
|
if (!scalar_ref || SvTYPE(scalar_ref) != SVt_PVAV) {
|
|
if (warn)
|
|
carp(CARP_WARN, "Value for group '%s' is not ARRAY reference", group_name);
|
|
return NULL;
|
|
}
|
|
|
|
return (AV *)scalar_ref;
|
|
}
|
|
|
|
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, const char *class_name, STRLEN class_len)
|
|
{
|
|
I32 len;
|
|
I32 index2;
|
|
AV *array;
|
|
STRLEN group_len;
|
|
const char *group_name;
|
|
|
|
group_name = get_perl_scalar_value(aTHX_ scalar_group, &group_len, utf8, true);
|
|
array = get_perl_array_from_scalar(scalar_list, group_name, false);
|
|
len = array ? (av_len(array) + 1) : 0;
|
|
|
|
if (index1 == -1 && group_name)
|
|
index1 = 0;
|
|
|
|
if (group_name)
|
|
message_address_add(first_address, last_address, NULL, 0, NULL, 0, group_name, group_len, NULL, 0, NULL, 0);
|
|
|
|
for (index2 = 0; index2 < len; ++index2)
|
|
message_address_add_from_perl_array(aTHX_ first_address, last_address, utf8, taint, array, index1, ((index1 == -1 && len == 1) ? -1 : index2), class_name, class_len);
|
|
|
|
if (group_name)
|
|
message_address_add(first_address, last_address, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
|
|
|
|
if (!*taint && SvTAINTED(scalar_group))
|
|
*taint = true;
|
|
}
|
|
|
|
#ifndef WITHOUT_SvPV_nomg
|
|
static bool perl_group_needs_utf8(pTHX_ SV *scalar_group, SV *scalar_list, I32 index1, const char *class_name, STRLEN class_len)
|
|
{
|
|
I32 len;
|
|
I32 index2;
|
|
SV *scalar;
|
|
HV *hash;
|
|
AV *array;
|
|
STRLEN len_na;
|
|
bool utf8;
|
|
const char *group_name;
|
|
const char **hash_key_ptr;
|
|
|
|
static const char *hash_keys[] = { "phrase", "user", "host", "comment", NULL };
|
|
|
|
utf8 = false;
|
|
|
|
group_name = get_perl_scalar_value(aTHX_ scalar_group, &len_na, false, false);
|
|
if (SvUTF8(scalar_group))
|
|
utf8 = true;
|
|
|
|
if (index1 == -1 && group_name)
|
|
index1 = 0;
|
|
|
|
array = get_perl_array_from_scalar(scalar_list, group_name, true);
|
|
len = array ? (av_len(array) + 1) : 0;
|
|
|
|
for (index2 = 0; index2 < len; ++index2) {
|
|
hash = get_object_hash_from_perl_array(aTHX_ array, index1, ((index1 == -1 && len == 1) ? -1 : index2), class_name, class_len, true);
|
|
if (!hash)
|
|
continue;
|
|
for (hash_key_ptr = hash_keys; *hash_key_ptr; ++hash_key_ptr) {
|
|
scalar = get_perl_hash_scalar(aTHX_ hash, *hash_key_ptr);
|
|
if (scalar && get_perl_scalar_value(aTHX_ scalar, &len_na, false, false) && SvUTF8(scalar))
|
|
utf8 = true;
|
|
}
|
|
}
|
|
|
|
return utf8;
|
|
}
|
|
#endif
|
|
|
|
static int count_address_groups(struct message_address *first_address)
|
|
{
|
|
int count;
|
|
bool in_group;
|
|
struct message_address *address;
|
|
|
|
count = 0;
|
|
in_group = false;
|
|
|
|
for (address = first_address; address; address = address->next) {
|
|
if (!address->domain)
|
|
in_group = !in_group;
|
|
if (in_group)
|
|
continue;
|
|
++count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static bool get_next_perl_address_group(pTHX_ struct message_address **address, SV **group_scalar, SV **addresses_scalar, HV *class, bool utf8, bool taint)
|
|
{
|
|
HV *hash;
|
|
SV *object;
|
|
SV *hash_ref;
|
|
bool in_group;
|
|
AV *addresses_array;
|
|
|
|
if (!*address)
|
|
return false;
|
|
|
|
in_group = !(*address)->domain;
|
|
|
|
if (in_group && (*address)->mailbox)
|
|
*group_scalar = sv_2mortal(newSVpvn((*address)->mailbox, (*address)->mailbox_len));
|
|
else
|
|
*group_scalar = sv_newmortal();
|
|
|
|
if (utf8 && in_group && (*address)->mailbox)
|
|
sv_utf8_decode(*group_scalar);
|
|
|
|
if (taint)
|
|
SvTAINTED_on(*group_scalar);
|
|
|
|
addresses_array = newAV();
|
|
*addresses_scalar = sv_2mortal(newRV_noinc((SV *)addresses_array));
|
|
|
|
if (in_group)
|
|
*address = (*address)->next;
|
|
|
|
while (*address && (*address)->domain) {
|
|
hash = newHV();
|
|
|
|
set_perl_hash_value(aTHX_ hash, "phrase", (*address)->name, (*address)->name_len, utf8, taint);
|
|
set_perl_hash_value(aTHX_ hash, "user", ( (*address)->mailbox && (*address)->mailbox[0] ) ? (*address)->mailbox : NULL, (*address)->mailbox_len, utf8, taint);
|
|
set_perl_hash_value(aTHX_ hash, "host", ( (*address)->domain && (*address)->domain[0] ) ? (*address)->domain : NULL, (*address)->domain_len, utf8, taint);
|
|
set_perl_hash_value(aTHX_ hash, "comment", (*address)->comment, (*address)->comment_len, utf8, taint);
|
|
set_perl_hash_value(aTHX_ hash, "original", (*address)->original, (*address)->original_len, utf8, taint);
|
|
|
|
if ((*address)->invalid_syntax)
|
|
(void)hv_store(hash, "invalid", sizeof("invalid")-1, newSViv(1), 0);
|
|
|
|
hash_ref = newRV_noinc((SV *)hash);
|
|
object = sv_bless(hash_ref, class);
|
|
|
|
av_push(addresses_array, object);
|
|
|
|
*address = (*address)->next;
|
|
}
|
|
|
|
if (in_group && *address)
|
|
*address = (*address)->next;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
MODULE = Email::Address::XS PACKAGE = Email::Address::XS
|
|
|
|
PROTOTYPES: DISABLE
|
|
|
|
void
|
|
format_email_groups(...)
|
|
PREINIT:
|
|
I32 i;
|
|
bool utf8;
|
|
bool taint;
|
|
char *string;
|
|
size_t string_len;
|
|
struct message_address *first_address;
|
|
struct message_address *last_address;
|
|
SV *string_scalar;
|
|
INPUT:
|
|
const char *this_class_name = "$Package";
|
|
STRLEN this_class_len = sizeof("$Package")-1;
|
|
INIT:
|
|
if (items % 2 == 1) {
|
|
carp(CARP_WARN, "Odd number of elements in argument list");
|
|
XSRETURN_UNDEF;
|
|
}
|
|
PPCODE:
|
|
first_address = NULL;
|
|
last_address = NULL;
|
|
taint = false;
|
|
#ifndef WITHOUT_SvPV_nomg
|
|
utf8 = false;
|
|
for (i = 0; i < items; i += 2)
|
|
if (perl_group_needs_utf8(aTHX_ ST(i), ST(i+1), (items == 2 ? -1 : i), this_class_name, this_class_len))
|
|
utf8 = true;
|
|
#else
|
|
utf8 = true;
|
|
#endif
|
|
for (i = 0; i < items; i += 2)
|
|
message_address_add_from_perl_group(aTHX_ &first_address, &last_address, utf8, &taint, ST(i), ST(i+1), (items == 2 ? -1 : i), this_class_name, this_class_len);
|
|
message_address_write(&string, &string_len, first_address);
|
|
message_address_free(&first_address);
|
|
string_scalar = sv_2mortal(newSVpvn(string, string_len));
|
|
string_free(string);
|
|
if (utf8)
|
|
sv_utf8_decode(string_scalar);
|
|
if (taint)
|
|
SvTAINTED_on(string_scalar);
|
|
EXTEND(SP, 1);
|
|
PUSHs(string_scalar);
|
|
|
|
void
|
|
parse_email_groups(...)
|
|
PREINIT:
|
|
SV *string_scalar;
|
|
SV *class_scalar;
|
|
int count;
|
|
HV *hv_class;
|
|
SV *group_scalar;
|
|
SV *addresses_scalar;
|
|
bool utf8;
|
|
bool taint;
|
|
STRLEN input_len;
|
|
const char *input;
|
|
struct message_address *address;
|
|
struct message_address *first_address;
|
|
INPUT:
|
|
const char *this_class_name = "$Package";
|
|
STRLEN this_class_len = sizeof("$Package")-1;
|
|
INIT:
|
|
string_scalar = items >= 1 ? ST(0) : &PL_sv_undef;
|
|
class_scalar = items >= 2 ? ST(1) : NULL;
|
|
input = get_perl_scalar_string_value(aTHX_ string_scalar, &input_len, "string", false);
|
|
utf8 = SvUTF8(string_scalar);
|
|
taint = SvTAINTED(string_scalar);
|
|
hv_class = get_perl_class_from_perl_scalar_or_cv(aTHX_ class_scalar, cv);
|
|
if (class_scalar && !sv_derived_from_pvn(class_scalar, this_class_name, this_class_len, SVf_UTF8)) {
|
|
carp(CARP_WARN, "Class %" SVf " is not derived from %s", SVfARG(class_scalar), this_class_name);
|
|
XSRETURN_EMPTY;
|
|
}
|
|
PPCODE:
|
|
first_address = message_address_parse(input, input_len, UINT_MAX, MESSAGE_ADDRESS_PARSE_FLAG_NON_STRICT_DOTS_AS_INVALID);
|
|
count = count_address_groups(first_address);
|
|
EXTEND(SP, count * 2);
|
|
address = first_address;
|
|
while (get_next_perl_address_group(aTHX_ &address, &group_scalar, &addresses_scalar, hv_class, utf8, taint)) {
|
|
PUSHs(group_scalar);
|
|
PUSHs(addresses_scalar);
|
|
}
|
|
message_address_free(&first_address);
|
|
|
|
void
|
|
compose_address(...)
|
|
PREINIT:
|
|
char *string;
|
|
const char *mailbox;
|
|
const char *domain;
|
|
size_t string_len;
|
|
STRLEN mailbox_len;
|
|
STRLEN domain_len;
|
|
bool mailbox_utf8;
|
|
bool domain_utf8;
|
|
bool utf8;
|
|
bool taint;
|
|
SV *mailbox_scalar;
|
|
SV *domain_scalar;
|
|
SV *string_scalar;
|
|
INIT:
|
|
mailbox_scalar = items >= 1 ? ST(0) : &PL_sv_undef;
|
|
domain_scalar = items >= 2 ? ST(1) : &PL_sv_undef;
|
|
mailbox = get_perl_scalar_string_value(aTHX_ mailbox_scalar, &mailbox_len, "mailbox", false);
|
|
domain = get_perl_scalar_string_value(aTHX_ domain_scalar, &domain_len, "domain", false);
|
|
mailbox_utf8 = SvUTF8(mailbox_scalar);
|
|
domain_utf8 = SvUTF8(domain_scalar);
|
|
utf8 = (mailbox_utf8 || domain_utf8);
|
|
if (utf8 && !mailbox_utf8)
|
|
mailbox = get_perl_scalar_value(aTHX_ mailbox_scalar, &mailbox_len, true, true);
|
|
if (utf8 && !domain_utf8)
|
|
domain = get_perl_scalar_value(aTHX_ domain_scalar, &domain_len, true, true);
|
|
taint = (SvTAINTED(mailbox_scalar) || SvTAINTED(domain_scalar));
|
|
PPCODE:
|
|
compose_address(&string, &string_len, mailbox, mailbox_len, domain, domain_len);
|
|
string_scalar = sv_2mortal(newSVpvn(string, string_len));
|
|
string_free(string);
|
|
if (utf8)
|
|
sv_utf8_decode(string_scalar);
|
|
if (taint)
|
|
SvTAINTED_on(string_scalar);
|
|
EXTEND(SP, 1);
|
|
PUSHs(string_scalar);
|
|
|
|
void
|
|
split_address(...)
|
|
PREINIT:
|
|
const char *string;
|
|
char *mailbox;
|
|
char *domain;
|
|
STRLEN string_len;
|
|
size_t mailbox_len;
|
|
size_t domain_len;
|
|
bool utf8;
|
|
bool taint;
|
|
SV *string_scalar;
|
|
SV *mailbox_scalar;
|
|
SV *domain_scalar;
|
|
INIT:
|
|
string_scalar = items >= 1 ? ST(0) : &PL_sv_undef;
|
|
string = get_perl_scalar_string_value(aTHX_ string_scalar, &string_len, "string", false);
|
|
utf8 = SvUTF8(string_scalar);
|
|
taint = SvTAINTED(string_scalar);
|
|
PPCODE:
|
|
split_address(string, string_len, &mailbox, &mailbox_len, &domain, &domain_len);
|
|
mailbox_scalar = mailbox ? sv_2mortal(newSVpvn(mailbox, mailbox_len)) : sv_newmortal();
|
|
domain_scalar = domain ? sv_2mortal(newSVpvn(domain, domain_len)) : sv_newmortal();
|
|
string_free(mailbox);
|
|
string_free(domain);
|
|
if (utf8) {
|
|
sv_utf8_decode(mailbox_scalar);
|
|
sv_utf8_decode(domain_scalar);
|
|
}
|
|
if (taint) {
|
|
SvTAINTED_on(mailbox_scalar);
|
|
SvTAINTED_on(domain_scalar);
|
|
}
|
|
EXTEND(SP, 2);
|
|
PUSHs(mailbox_scalar);
|
|
PUSHs(domain_scalar);
|
|
|
|
bool
|
|
is_obj(...)
|
|
PREINIT:
|
|
SV *class = items >= 1 ? ST(0) : &PL_sv_undef;
|
|
SV *object = items >= 2 ? ST(1) : &PL_sv_undef;
|
|
CODE:
|
|
RETVAL = is_class_object(aTHX_ class, NULL, 0, object);
|
|
OUTPUT:
|
|
RETVAL
|