# Advanced Playground

## Discarding messages

If you want to make the client think a message has been regularily accepted,
but in real you delete it or send it to `/dev/null`, ..., use something
like the following plugin and load it before your default queue plugin.

    sub hook_queue {
      my ($self, $transaction) = @_;
      if ($transaction->notes('discard_mail')) {
        my $msg_id = $transaction->header->get('Message-Id') || '';
        $msg_id =~ s/[\r\n].*//s;
        return(OK, "Queued! $msg_id");
      }
      return(DECLINED);
    }

## Changing return values

This is an example how to use the `isa_plugin` method.

The `rcpt_ok_maxrelay` plugin wraps the `rcpt_ok` plugin. The `rcpt_ok`
plugin checks the `rcpthosts` and `morercpthosts` config files for
domains, which we accept mail for. If not found it tells the
client that relaying is not allowed. Clients which are marked as
`relay clients` are excluded from this rule. This plugin counts the
number of unsuccessfull relaying attempts and drops the connection if
too many were made.

The optional parameter `MAX_RELAY_ATTEMPTS` configures this plugin to drop
the connection after `MAX_RELAY_ATTEMPTS` unsuccessful relaying attempts.
Set to `0` to disable, default is `5`.

Note: Do not load both (`rcpt_ok` and `rcpt_ok_maxrelay`). This plugin
should be configured to run `last`, like `rcpt_ok`.

    use Qpsmtpd::DSN;

    sub init {
      my ($self, $qp, @args) = @_;
      die "too many arguments"
        if @args > 1;
      $self->{_count_relay_max} = defined $args[0] ? $args[0] : 5;
      $self->isa_plugin("rcpt_ok");
    }

    sub hook_rcpt {
      my ($self, $transaction, $recipient) = @_;
      my ($rc, @msg) = $self->SUPER::hook_rcpt($transaction, $recipient);

      return ($rc, @msg)
         unless (($rc == DENY) and $self->{_count_relay_max});

      my $count =
        ($self->connection->notes('count_relay_attempts') || 0) + 1;
      $self->connection->notes('count_relay_attempts', $count);

      return ($rc, @msg) unless ($count > $self->{_count_relay_max});
      return Qpsmtpd::DSN->relaying_denied(DENY_DISCONNECT,
              "Too many relaying attempts");
    }

## Results of other hooks

If we're in a transaction, the results of a callback are stored in

    $self->transaction->notes($code->{name})->{"hook_$hook"}->{return}

If we're in a connection, store things in the connection notes instead.