Mail-DMARC

 view release on metacpan or  search on metacpan

lib/Mail/DMARC/Report/Store/SQL.pm  view on Meta::CPAN

sub _negate_arg( $self, $val ) {
    if ( substr( $val, 0, 1 ) eq '!' ) {
        return ( '!=', substr( $val, 1 ) );
    }
    return ( '=', $val );
}

sub next_todo($self) {

    if ( !exists $self->{_todo_list} ) {
        $self->{_todo_list}
            = $self->query( $self->grammar->select_todo_query, [ $self->time ] );
        return if !$self->{_todo_list};
    }

    my $next_todo = shift @{ $self->{_todo_list} };
    if ( !$next_todo ) {
        delete $self->{_todo_list};
        return;
    }

    my $agg = Mail::DMARC::Report::Aggregate->new();
    $self->populate_agg_metadata( \$agg, \$next_todo );

    my $pp = $self->get_report_policy_published( $next_todo->{rid} );
    $pp->{domain} = $next_todo->{from_domain};
    $agg->policy_published( Mail::DMARC::Policy->new(%$pp) );

    $self->populate_agg_records( \$agg, $next_todo->{rid} );
    return $agg;
}

sub retrieve_todo( $self, @args ) {

    # this method extracts the data from the SQL tables and populates a
    # list of Aggregate report objects with them.
    my $reports
        = $self->query( $self->grammar->select_todo_query, [ $self->time ] );
    my @reports_todo;
    return \@reports_todo if !@$reports;

    foreach my $report ( @{$reports} ) {

        my $agg = Mail::DMARC::Report::Aggregate->new();
        $self->populate_agg_metadata( \$agg, \$report );

        my $pp = $self->get_report_policy_published( $report->{rid} );
        $pp->{domain} = $report->{from_domain};
        $agg->policy_published( Mail::DMARC::Policy->new(%$pp) );

        $self->populate_agg_records( \$agg, $report->{rid} );
        push @reports_todo, $agg;
    }
    return \@reports_todo;
}

sub delete_report( $self, $report_id = undef ) {
    $report_id or croak "missing report ID";
    print "deleting report $report_id\n" if $self->verbose;

    # deletes with FK don't cascade in SQLite? Clean each table manually
    my $rows    = $self->query( $self->grammar->report_record_id, [$report_id] );
    my @row_ids = map { $_->{id} } @$rows;

    if (@row_ids) {
        foreach my $table (
            qw/ report_record_spf report_record_dkim report_record_reason /)
        {
            print "deleting $table rows " . join( ',', @row_ids ) . "\n"
                if $self->verbose;
            eval {
                $self->query( $self->grammar->delete_from_where_record_in($table),
                    \@row_ids );
            };

            # warn $@ if $@;
        }
    }
    foreach my $table (qw/ report_policy_published report_record report_error /) {
        print "deleting $table rows for report $report_id\n" if $self->verbose;
        eval {
            $self->query( $self->grammar->delete_from_where_report($table),
                [$report_id] );
        };

        # warn $@ if $@;
    }

    # In MySQL, where FK constraints DO cascade, this is the only query needed
    $self->query( $self->grammar->delete_report, [$report_id] );
    return 1;
}

sub get_domain_id( $self, $domain ) {
    croak "missing domain calling " . ( caller(0) )[3] if !$domain;
    my $r = $self->query( $self->grammar->select_domain_id, [$domain] );
    if ( $r && @$r ) {
        return $r->[0]{id};
    }
    return $self->query( $self->grammar->insert_domain, [$domain] );
}

sub get_author_id( $self, $meta ) {
    croak "missing author name" if !$meta->org_name;
    my $r = $self->query( $self->grammar->select_author_id, [ $meta->org_name ] );
    if ( $r && @$r ) {
        return $r->[0]{id};
    }
    carp "missing email" if !$meta->email;
    return $self->query( $self->grammar->insert_author,
        [ $meta->org_name, $meta->email, $meta->extra_contact_info ] );
}

sub get_report_id( $self, $aggr ) {

    my $meta = $aggr->metadata;
    my $pol  = $aggr->policy_published;

    # check if report exists
    my $author_id   = $self->get_author_id($meta)          or croak;
    my $from_dom_id = $self->get_domain_id( $pol->domain ) or croak;

    my $ids;
    if ( $meta->report_id ) {

        # reports arriving via the wire will have an author ID & report ID
        $ids = $self->query( $self->grammar->select_report_id,
            [ $meta->report_id, $author_id ] );
    }
    else {
        # Reports submitted by our local MTA will not have a report ID
        # They aggregate on the From domain, where the DMARC policy was discovered
        $ids = $self->query(
            $self->grammar->select_id_with_end,
            [ $from_dom_id, $self->time, $author_id ]
        );
    }

    if (@$ids) {    # report already exists
        return $self->{report_id} = $ids->[0]{id};
    }

    my $rid = $self->{report_id} = $self->query( $self->grammar->insert_report,
        [ $from_dom_id, $meta->begin, $meta->end, $author_id, $meta->uuid ] )
        or return;

    $self->insert_policy_published( $rid, $pol );
    return $rid;
}



( run in 3.115 seconds using v1.01-cache-2.11-cpan-df04353d9ac )