ff29dfc2afa02e5ba4c65e0327c5b9198bfb099e
[app-fonbot-daemon.git] / lib / App / FonBot / Plugin / Email.pm
1 package App::FonBot::Plugin::Email;
2
3 our $VERSION = '0.000_5';
4
5 use v5.14;
6 use strict;
7 use warnings;
8
9 use Apache2::Authen::Passphrase qw/pwcheck/;
10 use Email::Sender::Simple qw/sendmail/;
11 use Email::Simple;
12 use Email::MIME;
13 use Linux::Inotify2 qw/IN_MOVED_TO/;
14 use File::Slurp qw/read_file/;
15 use POE;
16 use Log::Log4perl qw//;
17
18 use File::Glob qw/bsd_glob GLOB_NOSORT/;
19 use Text::ParseWords qw/shellwords/;
20
21 use App::FonBot::Plugin::Common;
22 use App::FonBot::Plugin::Config qw/$email_batch_seconds $email_from $email_subject/;
23
24 ##################################################
25
26 my $log=Log::Log4perl->get_logger(__PACKAGE__);
27
28 my %queues;
29 my %queue_alarms;
30 my $session;
31 my $inotify;
32
33 sub init{
34 return unless $email_from && $email_subject;
35 $log->info('initializing '.__PACKAGE__);
36 $session = POE::Session->create(
37 inline_states => {
38 _start => \&email_start,
39 send_message => \&email_send_message,
40 flush_queue => \&email_flush_queue,
41 inotify_readable => sub{
42 $_[HEAP]{inotify}->poll
43 },
44 shutdown => sub{
45 $_[KERNEL]->select_read($inotify)
46 }
47 },
48 );
49 }
50
51 sub fini{
52 $log->info('finishing '.__PACKAGE__);
53 POE::Kernel->post($session, 'shutdown')
54 }
55
56
57 sub email_handle_new{
58 for my $file (bsd_glob 'Maildir/new/*', GLOB_NOSORT) {
59 my $email=Email::MIME->new(scalar read_file $file);
60
61 #untaint $file
62 $file =~ /^(.*)$/;
63 $file = $1;
64
65 unlink $file;
66 return unless defined $email;
67
68 my $replyto=$email->header('From');
69 return unless defined $replyto;
70
71 my ($user,$password)=split ' ', $email->header('Subject'), 2;
72 chomp $password;
73
74 $log->debug("Processing email from $user");
75
76 eval { pwcheck $user, $password };
77 if ($@) {
78 $log->debug("Incorrect credentials in email subject from user $user. Exception: $@");
79 POE::Kernel->yield(send_message => $replyto, "Incorrect credentials");
80 return
81 }
82
83 $ok_user_addresses{"$user EMAIL $replyto"}=1;
84
85 my $process_email_part = sub {
86 local *__ANON__ = "process_email_part"; #Name this sub. See http://www.perlmonks.org/?node_id=304883
87
88 my $part=$_[0];
89 return unless $part->content_type =~ /text\/plain/;
90
91 my @lines=split '\n', $part->body;
92
93 for my $line (@lines) {
94 last if $line =~ /^--/;
95 $log->debug("Command received via email from $user: $line");
96 sendmsg $user, undef, "EMAIL '$replyto'", shellwords $line
97 }
98 };
99
100 $email->walk_parts($process_email_part);
101 }
102 }
103
104 sub email_start{
105 $_[KERNEL]->alias_set('EMAIL');
106 $_[HEAP]{inotify} = Linux::Inotify2->new;
107 $_[HEAP]{inotify}->watch('Maildir/new/',IN_MOVED_TO,\&email_handle_new);
108
109 open $inotify,'<&=',$_[HEAP]{inotify}->fileno;
110 $_[KERNEL]->select_read($inotify, 'inotify_readable');
111 email_handle_new
112 }
113
114 sub email_send_message{
115 my ($address, $message) = @_[ARG0,ARG1];
116
117 $queues{$address}.=$message."\n";
118 if (defined $queue_alarms{$address}) {
119 $_[KERNEL]->delay_adjust($queue_alarms{$address}, $email_batch_seconds)
120 } else {
121 $queue_alarms{$address}=$_[KERNEL]->delay_set(flush_queue => $email_batch_seconds, $address)
122 }
123 }
124
125 sub email_flush_queue{
126 my ($queue) = $_[ARG0];
127 return unless exists $queues{$queue};
128
129 my $email=Email::Simple->create(
130 header => [
131 From => $email_from,
132 To => $queue,
133 Subject => $email_subject,
134 ],
135 body => $queues{$queue}
136 );
137
138 delete $queues{$queue};
139 delete $queue_alarms{$queue};
140
141 $log->debug("Sending email to $queue");
142
143 eval {
144 sendmail $email
145 } or $log->error("Could not send email: " . $@->message);
146 }
147
148 1;
149 __END__
150
151 =encoding utf-8
152
153 =head1 NAME
154
155 App::FonBot::Plugin::Email - FonBot plugin for receiving commands and sending messages through emails
156
157 =head1 SYNOPSIS
158
159 use App::FonBot::Plugin::Email;
160 App::FonBot::Plugin::Email->init;
161 ...
162 App::FonBot::Plugin::Email->fini;
163
164 =head1 DESCRIPTION
165
166 This FonBot plugin provides email receiving/sending features to B<fonbotd>. Emails are read from F<Maildir/> and are sent through C<Email::Sender::Simple>.
167
168 =head1 CONFIGURATION VARIABLES
169
170 These are the L<App::FonBot::Plugin::Config> configuration variables used in this module
171
172 =over
173
174 =item C<$email_batch_seconds>
175
176 When receiving an email send request, C<App::FonBot::Plugin::Email> waits this many seconds for further email send requests to the same email address. The timer is reset for each email send request. When the timer expires, all pending send requests are batched and sent as one email.
177
178 =item C<$email_from>
179
180 C<From:> header of all emails sent by this plugin. If not set the email plugin is disabled.
181
182 =item C<$email_subject>
183
184 C<Subject:> header of all emails sent by this plugin. If not set the email plugin is disabled.
185
186 =back
187
188 =head1 AUTHOR
189
190 Marius Gavrilescu C<< marius@ieval.ro >>
191
192 =head1 COPYRIGHT AND LICENSE
193
194 Copyright 2013 Marius Gavrilescu
195
196 This file is part of fonbotd.
197
198 fonbotd is free software: you can redistribute it and/or modify
199 it under the terms of the GNU Affero General Public License as published by
200 the Free Software Foundation, either version 3 of the License, or
201 (at your option) any later version.
202
203 fonbotd is distributed in the hope that it will be useful,
204 but WITHOUT ANY WARRANTY; without even the implied warranty of
205 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
206 GNU Affero General Public License for more details.
207
208 You should have received a copy of the GNU Affero General Public License
209 along with fonbotd. If not, see <http://www.gnu.org/licenses/>
This page took 0.031629 seconds and 3 git commands to generate.