Search code examples
arraysperlpolicyqmailmail-sender

Verify if email sender has permission to send to a certain address


I am forced to implement a sender policy in qmail.

I have a file describing the policies:

[email protected]:*@domain1.com,[email protected],[email protected]
[email protected]:*@*
[email protected]:

This file describes the following situation:

[email protected] can send messages to anyone having the address defined on domain1.com, to [email protected] and to [email protected]
[email protected] can send messages to anyone
[email protected] can not send messages at all

The letter case should be ignored.

I have to implement those restrictions in qmail, using qmailqueue and PERL (Mail::Qmail::Queue::Message) but I have no knowledge of PERL at all.

Any help is sincerely appreciated.


Solution

  • but I have no knowledge of PERL at all.

    Then the solution is

    • to hire someone who does, or
    • to start learning. See tag wiki.

    (As an aside, the language is called Perl, and the interpreter used to run Perl programs is called perl. Other capitalizations are a bit frowned upon).


    Should you decide to try solving this problem on your own, here are a few pointers:

    The problem can be decomposed into subproblems:

    • Parsing the rule file. This is rather easy with the split function.

      The function takes zero to three arguments: regex, string, limit. If the limit is omitted, the number of resulting fragments is arbitrary. If the string is omitted, then the $_ special variable is used. If the regex is omitted, it will be treated as if the special value " " was given: Splits on all whitespace, and removes leading whitespace.

      To parse a line, the following will have to be done:

      • Removing the trailing newline with chomp,
      • Splitting the line on : into sender and rule string, with at most two fragments,
      • Splitting the rule string on , into single rules,
      • Splitting each rule at @ into user and domain part.

      The rules can then be saved in a database or a Perl data structure.

    • Matching rules given a pair of sender and receiver addresses. In case of a database, this could be done with a nice SQL query. In case of a Perl data structure, you would:

      • retrieve all rules for the sender's address
      • Iterate through all rules: Match the user and domain parts and return true once both match.
      • Else, return false

      Matching a rule against an address will first check if the rule part (user/domain) is a *. Else, the parts are tested for equality. Both the user and domain part have to match for the whole rule to match.

    • Choosing a good Perl data structure: To make our life easy, all email addresses and rules are only handled in their normalized form. This means e.g. lowercase. For each object in the above algorithm, one could define a seperate class. This is probably undesirable. Instead,

      • Each rule can be represented as a 2-valued array reference
      • The rule list for a sender could be represented as an array reference of rules.
      • The association between the sender address and receiver rules can be made through a hash.

    Code examples

    Parsing a line

    where the line is given in $_, and a hashref $rules_for_sender is in scope

    chomp;
    my ($sender, $rules) = map lc, split /:/, $_, 2;
    my @rules = map [split /@/], split /,/, $rules;
    $rules_for_sender->{$sender} = \@rules;
    

    Iterating through all rules for a sender

    for my $rule (@{ $rules->{$sender} } {
      my ($rule_user, $rule_domain) = @$rule;
      ...; # do stuff here
    }
    

    The rest…

    …is almost trivial if you remember

    • to normalize correctly,
    • that use strict; use warnings; helps you find many errors, and
    • that string comparision is done with the eq operator.