Skip to content

Commit

Permalink
complete pod documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
peczenyj committed Dec 4, 2023
1 parent a322e05 commit b04abae
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 54 deletions.
89 changes: 61 additions & 28 deletions lib/GDPR/IAB/TCFv2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -49,41 +49,42 @@ sub Parse {

if ( $self->_is_vendor_consent_range_encoding ) {
( $vendor_consents, $legitimate_interest_start ) =
$self->_parseRangeSection( $self->max_vendor_id, 230 );
$self->_parseRangeSection( $self->max_vendor_id_consent, 230 );
}
else {
( $vendor_consents, $legitimate_interest_start ) =
$self->_parseBitField( $self->max_vendor_id, 230 );
$self->_parseBitField( $self->max_vendor_id_consent, 230 );
}

$self->{vendor_consents} = $vendor_consents;

my $legitimate_interest_max_vendor =
get_uint16( $self->{data}, $legitimate_interest_start );

$self->{legitimate_interest_max_vendor} = $legitimate_interest_max_vendor;

croak
"invalid consent data: no legitimate interest start position (got $legitimate_interest_start +16 but @{[ length( $self->{data} ) ]})"
if $legitimate_interest_start + 16 > length( $self->{data} );

my $is_vendor_legitimate_interest_range = is_set( $data, $legitimate_interest_start + 16 );

$self->{legitimate_interest_start} = $legitimate_interest_start + 17;
my $is_vendor_legitimate_interest_range =
is_set( $data, $legitimate_interest_start + 16 );

my $vendor_legitimate_interests;
my $pub_restrict_start;

if ( $is_vendor_legitimate_interest_range ) {
if ($is_vendor_legitimate_interest_range) {
( $vendor_legitimate_interests, $pub_restrict_start ) =
$self->_parseRangeSection(
$legitimate_interest_max_vendor,
$self->{legitimate_interest_start}
$self->max_vendor_id_legitimate_interest,
$legitimate_interest_start + 17
);
}
else {
( $vendor_legitimate_interests, $pub_restrict_start ) =
$self->_parseBitField(
$legitimate_interest_max_vendor,
$self->{legitimate_interest_start}
$self->max_vendor_id_legitimate_interest,
$legitimate_interest_start + 17
);
}

Expand Down Expand Up @@ -218,22 +219,28 @@ sub publisher_country_code {
return get_char6_sequence( $self->{data}, 201, 2 );
}

sub max_vendor_id {
sub max_vendor_id_consent {
my $self = shift;

return get_uint16( $self->{data}, 213 );
}

sub max_vendor_id_legitimate_interest {
my $self = shift;

return $self->{legitimate_interest_max_vendor};
}

sub vendor_consent {
my ( $self, $id ) = @_;

return $self->{vendor_consents}->vendor_consent($id);
return $self->{vendor_consents}->contains($id);
}

sub vendor_legitimate_interest {
my ( $self, $id ) = @_;

return $self->{vendor_legitimate_interests}->vendor_consent($id);
return $self->{vendor_legitimate_interests}->contains($id);
}

sub _is_vendor_consent_range_encoding {
Expand All @@ -245,12 +252,6 @@ sub _is_vendor_consent_range_encoding {
sub _parseRangeSection {
my ( $self, $vendor_bits_required, $start_bit ) = @_;

my $data_size = length( $self->{data} );

croak
"a BitField for vendor consent strings using RangeSections require at least 31 bytes. Got $data_size"
if $data_size < 32;

my $range_section = GDPR::IAB::TCFv2::RangeSection->new(
data => $self->{data},
start_bit => $start_bit,
Expand All @@ -263,15 +264,6 @@ sub _parseRangeSection {
sub _parseBitField {
my ( $self, $vendor_bits_required, $start_bit ) = @_;

my $data_size = length( $self->{data} );

# add 7 to force rounding to next integer value
my $bytes_required = ( $vendor_bits_required + $start_bit + 7 ) / 8;

croak
"a BitField for $vendor_bits_required requires a consent string of $bytes_required bytes. This consent string had $data_size"
if $data_size < $bytes_required;

my $bitfield = GDPR::IAB::TCFv2::BitField->new(
data => $self->{data},
start_bit => $start_bit,
Expand Down Expand Up @@ -382,6 +374,47 @@ Version of the GVL used to create this TC String.
The user's consent value for each Purpose established on the legal basis of consent.
my $ok = $instance->is_purpose_consent_allowed(1);
=head2 is_purpose_legitimate_interest_allowed
The user's consent value for each Purpose established on the legal basis of legitimate interest.
my $ok = $instance->is_purpose_legitimate_interest_allowed(1);
=head2 purpose_one_treatment
CMPs can use the PublisherCC field to indicate the legal jurisdiction the publisher is under to help vendors determine whether the vendor needs consent for Purpose 1.
Returns true if Purpose 1 was NOT disclosed at all.
Returns false if Purpose 1 was disclosed commonly as consent as expected by the L<Policies|https://iabeurope.eu/iab-europe-transparency-consent-framework-policies/>.
=head2 publisher_country_code
Two-letter L<ISO 639-1|https://en.wikipedia.org/wiki/ISO_639-1> language code of the country that determines legislation of reference.
Commonly, this corresponds to the country in which the publisher’s business entity is established.
=head2 max_vendor_id_consent
The maximum Vendor ID that is represented in the following bit field or range encoding.
Because this section can be a variable length, this indicates the last ID of the section so that a decoder will know when it has reached the end.
=head2 vendor_consent
The consent value for each Vendor ID
=head2 max_vendor_id_legitimate_interest
The maximum Vendor ID that is represented in the following bit field or range encoding.
Because this section can be a variable length, this indicates the last ID of the section so that a decoder will know when it has reached the end.
=head2 vendor_legitimate_interest
The legitimate interest value for each Vendor ID
=head1 FUNCTIONS
=head2 looksLikeIsConsentVersion2
Expand Down
51 changes: 50 additions & 1 deletion lib/GDPR/IAB/TCFv2/BitField.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ sub new {
my $vendor_bits_required = $args{vendor_bits_required}
or croak "missing 'vendor_bits_required'";

my $data_size = length($data);

# add 7 to force rounding to next integer value
my $bytes_required = ( $vendor_bits_required + $start_bit + 7 ) / 8;

croak
"a BitField for $vendor_bits_required requires a consent string of $bytes_required bytes. This consent string had $data_size"
if $data_size < $bytes_required;

my $self = {
data => substr( $data, $start_bit ),
vendor_bits_required => $vendor_bits_required,
Expand All @@ -31,7 +40,7 @@ sub max_vendor_id {
return $self->{vendor_bits_required};
}

sub vendor_consent {
sub contains {
my ( $self, $id ) = @_;

croak "invalid vendor id $id: must be positive integer bigger than 0"
Expand All @@ -43,3 +52,43 @@ sub vendor_consent {
}

1;
__END__
=head1 NAME
GDPR::IAB::TCFv2::BitField - Transparency & Consent String version 2 bitfield parser
=head1 SYNOPSIS
my $data = unpack "B*", decode_base64url('tcf v2 consent string base64 encoded');
my $max_vendor_id_consent = << get 16 bits from $data offset 213 >>
my $bit_field = GDPR::IAB::TCFv2::BitField->new(
data => $data,
start_bit => 230, # offset for vendor consents
vendor_bits_required => $max_vendor_id_consent
);
if $bit_field->contains(284) { ... }
=head1 CONSTRUCTOR
Receive 3 parameters: data (as sequence of bits), start bit offset and vendor bits required (max vendor id).
Will die if any parameter is missing.
Will die if data does not contain all bits required.
=head1 METHODS
=head2 contains
Return the vendor id bit status (if enable or not) from the bit field.
Will return false if id is bigger than max vendor id.
my $ok = $bit_field->contains(284);
=head2 max_vendor_id
Returns the max vendor id.
79 changes: 68 additions & 11 deletions lib/GDPR/IAB/TCFv2/BitUtils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use base qw<Exporter>;

our @EXPORT_OK = qw<is_set
get_uint6
get_uint8
get_char6
get_char6_sequence
get_uint12
Expand All @@ -22,7 +21,6 @@ our %EXPORT_TAGS = (
all => [
qw<is_set
get_uint6
get_uint8
get_char6
get_char6_sequence
get_uint12
Expand All @@ -48,15 +46,6 @@ sub get_uint6 {
);
}

sub get_uint8 {
my ( $data, $offset ) = @_;

return unpack(
"C",
_get_bits_with_padding( $data, 8, $offset, 8 )
);
}

sub get_char6 {
my ( $data, $offset ) = @_;

Expand Down Expand Up @@ -110,3 +99,71 @@ sub _get_bits_with_padding {
}

1;
__END__
=head1 NAME
GDPR::IAB::TCFv2::BitUtils - utilities functions to manage bits
=head1 SYNOPSIS
use GDPR::IAB::TCFv2::BitUtils qw<get_uint16>;
my $data = unpack "B*", decode_base64url('tcf v2 consent string base64 encoded');
my $max_vendor_id_consent = get_uint16($data, 213);
=head1 FUNCTIONS
=head2 is_set
Receive two parameters: data and bit offset.
Will return true if the bit present on bit offset is 1.
my $is_service_specific = is_set( $data, 138 );
=head2 get_uint6
Receive two parameters: data and bit offset.
Will fetch 6 bits from data since bit offset and convert it an unsigned int.
my $version = get_uint6( $data, 0 );
=head2 get_char6
Similar to L<GDPR::IAB::TCFv2::BitUtils::get_uint6> but perform increment the value with the ascii value of "A" letter and convert to a character.
=head2 get_char6_sequence
Receives the data, bit offset and sequence size n.
Returns a string of size n by concantenating L<GDPR::IAB::TCFv2::BitUtils::get_char6> calls.
my $consent_language = get_char6_sequence($data, 108, 2) # returns two letter country encoded as ISO_639-1
=head2 get_uint12
Receives the data and bit offset.
Will fetch 12 bits from data since bit offset and convert it an unsigned int (short).
my $cmp_id = get_uint12( $data, 78 );
=head2 get_uint16
Receives the data and bit offset.
Will fetch 16 bits from data since bit offset and convert it an unsigned int (short).
my $max_vendor_id_consent = get_uint16( $data, 213 );
=head2 get_uint36
Receives the data and bit offset.
Will fetch 36 bits from data since bit offset and convert it an unsigned int (long).
my $deciseconds = get_uint36( $data, 6 );
my $created = $deciseconds/2;
30 changes: 30 additions & 0 deletions lib/GDPR/IAB/TCFv2/RangeConsent.pm
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,33 @@ sub contains {
}

1;
__END__
=head1 NAME
GDPR::IAB::TCFv2::RangeConsent - Transparency & Consent String version 2 range consent pair
=head1 SYNOPSIS
my $range = GDPR::IAB::TCFv2::RangeConsent->new(
start => 10,
end => 20,
);
die "ops" unless $range->contains(15);
=head1 CONSTRUCTOR
Receive 2 parameters: start and end.
Will die if any parameter is missing.
Will die if start is bigger than end.
=head1 METHODS
=head2 contains
Return true if the id is present on the range [start, end]
my $ok = $range->contains(15);
Loading

0 comments on commit b04abae

Please sign in to comment.