Add rough, untested bounce parsing.
[p5sagit/Promulger.git] / lib / Promulger / Dispatch.pm
CommitLineData
f5baca29 1package Promulger::Dispatch;
9e4ca637 2use Moo;
9e4ca637 3use autodie ':all';
018a0a65 4use Scalar::Util 'blessed';
d16aba7a 5use Try::Tiny;
f5baca29 6
8ad89cb2 7use Email::Address;
b179831b 8use Email::MIME;
9e4ca637 9use Email::Sender::Simple ();
9f8395b9 10# XXX not yet -- apeiron, 2010-06-25
11#use Mail::Verp;
f5baca29 12
21baaef0 13use Promulger::Config;
d16aba7a 14use Promulger::List;
21baaef0 15
9e4ca637 16has transport => (
17 is => 'rw',
18 isa => sub {
19 my $proto = $_[0];
20 blessed $proto and
21 $proto->can('does') and
22 $proto->does('Email::Sender::Transport')
23 or die "transport must do Email::Sender::Transport role";
24 },
25 default => sub {
018a0a65 26 my $config = Promulger::Config->config;
27 my $class;
28 if($class = $config->{mailer}) {
29 if($class !~ /::/) {
30 $class = "Email::Sender::Transport::${class}";
31 }
32 } else {
33 $class = 'Email::Sender::Transport::Sendmail';
34 }
35 Class::MOP::load_class($class);
36 $class->new;
9e4ca637 37 },
38);
39
4a69a584 40sub dispatch {
41 my ($self, $message) = @_;
21baaef0 42 my $config = Promulger::Config->config;
f5baca29 43
b179831b 44 my $email = Email::MIME->new($message);
f7a55eac 45 my $recipient = $email->header('To');
9e4ca637 46 my $local_user = $self->user_for_address($recipient);
f7a55eac 47 my $sender = $email->header('From');
48 my $subject = $email->header('Subject');
f5baca29 49
8ad89cb2 50 my $list = Promulger::List->resolve($local_user);
f5baca29 51 unless($list) {
9e4ca637 52 $self->reject($recipient, $sender);
8ad89cb2 53 return;
f5baca29 54 }
55
8ad89cb2 56 if($local_user =~ /-request$/) {
9e4ca637 57 $self->handle_request($list, $sender, $local_user, $subject, $config);
8ad89cb2 58 return;
f5baca29 59 }
60
61 # they don't have a request for us, so they want to post a message
9e4ca637 62 $self->post_message($list, $email, $config);
8ad89cb2 63 return;
f5baca29 64}
65
4a69a584 66sub handle_request {
67 my ($self, $list, $sender, $recipient, $subject) = @_;
9e4ca637 68 my $sender_address = $self->bare_address($sender);
8ad89cb2 69 if($subject =~ /^\s*subscribe/i) {
70 $list->subscribe($sender_address)
9e4ca637 71 or $self->already_subscribed($list, $recipient, $sender_address);
8ad89cb2 72 } elsif($subject =~ /^\s*unsubscribe/i) {
73 $list->unsubscribe($sender_address)
9e4ca637 74 or $self->not_subscribed($list, $recipient, $sender_address);
f5baca29 75 }
76}
77
d16aba7a 78# XXX this needs to be better -- apeiron, 2012-07-18
79sub handle_bounce {
80 my ($self, $raw_message) = @_;
81 my $message = Email::MIME->new($raw_message);
82 require Mail::DeliveryStatus::BounceParser;
83 my $bounce;
84 my $recipient = $message->header('To');
85 my $recipient_address = Email::Address->parse($recipient);
86 try {
87 my $bounce = Mail::DeliveryStatus::BounceParser->new($message);
88 } catch {
89 my $domain = $recipient_address->host;
90 my $redirect = Email::MIME->create(
91 header_str => [
92 From => $message->header('From'),
93 To => "postmaster@${domain}",
94 Subject => $message->header('Subject'),
95 ],
96 body_str => $message->body_str,
97 );
98 $self->send_message($redirect);
99 };
100 my @addresses = $bounce->addresses;
101 my $list = Promulger::List->resolve($recipient_address->address);
102 for my $addr (@addresses) {
103 my $a = Email::Address->parse($addr);
104 my $raw_address = $a->address;
105 $list->unsubscribe($raw_address);
106 }
107}
108
4a69a584 109sub post_message {
110 my ($self, $list, $email, $config) = @_;
f7a55eac 111 my $sender = $email->header('From');
9e4ca637 112 my $sender_address = $self->bare_address($sender);
f7a55eac 113 my $recipient = $email->header('To');
f5baca29 114
8ad89cb2 115 unless($list->accept_posts_from($sender_address) && $list->active) {
9e4ca637 116 $self->reject($recipient, $sender);
8ad89cb2 117 return;
118 }
f5baca29 119
f5baca29 120 # XXX no MIME or other fancy handling for now -- apeiron, 2010-03-13
b179831b 121 my $body = $email->body_str;
8ad89cb2 122 for my $subscriber (keys %{$list->subscribers}) {
f7a55eac 123 # my $verped_from = Mail::Verp->encode($recipient, $subscriber);
124
f5baca29 125 # XXX we let the MTA create the message-id for us for now -- apeiron,
126 # 2010-03-13
b179831b 127 my $new_message = Email::MIME->create(
f5baca29 128 header => [
e9b4d3bc 129 From => $sender_address,
130 To => $subscriber,
f7a55eac 131 Subject => $email->header('Subject'),
e9b4d3bc 132 'Reply-to' => $recipient,
f5baca29 133 ],
134 body => $body,
135 );
136 # XXX no queuing or job distribution for now beyond what the MTA provides
137 # -- apeiron, 2010-03-13
9e4ca637 138 $self->send_message($new_message);
f5baca29 139 }
140}
141
4a69a584 142sub send_message {
143 my ($self, $message) = @_;
9e4ca637 144 Email::Sender::Simple::sendmail(
f7a55eac 145 $message,
146 {
9e4ca637 147 transport => $self->transport,
f7a55eac 148 }
149 );
150}
151
21baaef0 152# XXX make this actually not suck -- apeiron, 2010-03-13
4a69a584 153sub reject {
154 my ($self, $recipient, $sender) = @_;
b179831b 155 my $email = Email::MIME->create(
21baaef0 156 header => [
157 From => $recipient,
158 To => $sender,
159 Subject => 'Rejected',
160 ],
161 body => <<BODY,
162Sorry, your message to $recipient has been denied.
163BODY
164 );
9e4ca637 165 $self->send_message($email);
21baaef0 166}
167
4a69a584 168sub not_subscribed {
169 my ($self, $list, $recipient, $sender) = @_;
b179831b 170 my $email = Email::MIME->create(
8ad89cb2 171 # XXX need admin address
21baaef0 172 header => [
8ad89cb2 173 From => $recipient,
21baaef0 174 To => $sender,
175 Subject => 'Not subscribed',
176 ],
177 body => <<BODY,
8ad89cb2 178Sorry, you are not subscribed to $list.
21baaef0 179BODY
180 );
9e4ca637 181 $self->send_message($email);
21baaef0 182}
183
4a69a584 184sub already_subscribed {
185 my ($self, $list, $recipient, $sender) = @_;
b179831b 186 my $email = Email::MIME->create(
21baaef0 187 header => [
8ad89cb2 188 From => $recipient,
21baaef0 189 To => $sender,
190 Subject => 'Already subscribed',
191 ],
192 body => <<BODY,
8ad89cb2 193Sorry, you are already subscribed to $list.
21baaef0 194BODY
195 );
9e4ca637 196 $self->send_message($email);
21baaef0 197}
f5baca29 198
4a69a584 199sub bare_address {
200 my ($self, $full_addr) = @_;
8ad89cb2 201 my ($addr_obj) = Email::Address->parse($full_addr);
202 return $addr_obj->address;
203}
204
4a69a584 205sub user_for_address {
206 my ($self, $full_addr) = @_;
8ad89cb2 207 my ($addr_obj) = Email::Address->parse($full_addr);
208 return $addr_obj->user;
209}
210
f5baca29 211'http://www.shadowcat.co.uk/blog/matt-s-trout/oh-subdispatch-oh-subdispatch/';