Eliminated duplicate "Rule: "
[clearscm.git] / maps / bin / maps
1 #!/usr/bin/perl
2
3 =pod
4
5 =head1 NAME $RCSfile: maps,v $
6
7 This script filters mail based on the files nulllist, blacklist and whitelist. 
8 Input is an email message. This script extracts the From line and then parses 
9 the email address. If the email is from a sender who should be /dev/null'ed 
10 (e.g. bounce messages from mail daemons) the message will be discarded. If the
11 sender is on the blacklist then a message is sent back informing the sender that
12 he's been blacklisted. If the sender is on the white list then the email is 
13 appended to the mail drop file. Otherwise a message is sent back informing the
14 sender that in order to successfully send email the sender must register for the
15 permission to do so, along with a URL that allows the sender to sign up.
16
17 =head1 VERSION
18
19 =over
20
21 =item Author
22
23 Andrew DeFaria <Andrew@DeFaria.com>
24
25 =item Revision
26
27 $Revision: 1.1 $
28
29 =item Created:
30
31 Fri Nov 29 14:17:21  2002
32
33 =item Modified:
34
35 $Date: 2013/06/12 14:05:47 $
36
37 =back
38
39 =head1 SYNOPSIS
40
41  Usage maps: [-u|ser <username>] [-ve|rbose] [-deb|ug] [-e|xecute]
42
43  Where:
44    -u|ser <username>: Set context to this username
45
46    -v|erbose:         Be verbose
47    -de|bug:           Output debug messages
48
49    -[no]e|xecute:     Set execute mode.
50    -f|ile <file>:     File to use as a test message
51
52 # (c) Copyright 2000-2021, Andrew@DeFaria.com, all rights reserved.
53
54 =cut 
55
56 use strict;
57 use warnings;
58
59 use Getopt::Long;
60 use Pod::Usage;
61 use Email::Valid;
62 use FindBin;
63 use File::Temp qw (tempfile);
64 use Net::Domain qw (hostdomain);
65
66 use lib "$FindBin::Bin/../lib";
67 use lib "$FindBin::Bin/../../lib";
68
69 use MAPS;
70 use MAPSLog;
71
72 use Display;
73 use Logger;
74 use Utils;
75
76 my %opts = (
77   execute => 1,
78 );
79
80 my $userid = $ENV{USER};
81
82 my $log;
83
84 # For some reason I'm not parsing messages correctly but it only seems to
85 # happen when the message is piped in from the MTA. This routine will
86 # temporarily save the messages in a file.
87 sub SaveStdin() {
88   # Generate tempfile
89   my $msgfile = tempfile ();
90
91   # Read STDIN and write it out to the tempfile
92   while (<STDIN>) {
93     print $msgfile $_;
94   } # while
95
96   # Seek to the start of the file (Note if we closed it, it would be deleted)
97   seek $msgfile, 0, 0;
98
99   # Return the filehandle
100   return $msgfile;
101 } # SaveStdin
102
103 sub ValidDomainUser($) {
104   my ($sender) = @_;
105
106   my ($username, $domainname);
107
108   if ($sender =~ /(.*)\@(.*)/) {
109     $username   = $1;
110     $domainname = $2;
111   } else {
112     return 1;
113   } # if
114
115   return 1 if $domainname ne hostdomain;
116
117   # Let BICE email come through
118   return 1 if $username eq "bice";
119
120   my $uid = getpwnam $username;
121
122   return defined $uid ? 1 : 0;
123 } # ValidDomainUser
124
125 sub formatRule($) {
126   my ($rec) = @_;
127
128   return "$rec->{pattern}\@$rec->{domain}:$rec->{sequence}/$rec->{hit_count}";
129 } # formatRule
130
131 sub ProcessMsgs ($$$) {
132   my ($msgfile, $username, $user_email) = @_;
133
134   return unless $opts{execute};
135
136   while (!eof $msgfile) {
137     my ($sender, $sender_long, $reply_to, $subject, $data) = ReadMsg ($msgfile);
138
139     my ($onlist, $rec, $sequence, $hit_count);
140
141     # Algorithm change: We now first check to see if the sender is not found
142     # in the message and skip it if so. Then we handle if we are the sender
143     # and that the from address is formatted properly. Spammers often use 
144     # the senders email address (i.e. andrew@defaria.com) as their from address
145     # so we check "Andrew DeFaria <Andrew@DeFaria.com>", which they have never
146     # forged. This catches a lot of spam actually.
147     #
148     # Next we check to see if the sender is on our whitelist. If so then we let
149     # them in. This allows us to say whitelist josephrosenberg@hotmail.com while
150     # still nulllisting all of the other hotmail.com spammers.
151     #
152     # Next we process blacklisted people as they are also of high priority.
153     #
154     # Then we process nulllist people.
155     #
156     # Finally, we handle return processing
157
158     # Discard any email with an invalid email address
159     next unless Email::Valid->address($sender);
160
161     if ($sender eq $user_email and
162             (lc ($sender_long) !~ lc ("\"$username\" <$user_email>") and
163              lc ($sender_long) !~ lc ("$username <$user_email>"))) {
164       $log->msg("Nulllisting message from sender ($sender_long) pretending to be $user_email");
165
166       Nulllist $sender;
167
168       next;
169     } # if
170
171     # Check whitelist:
172     ($onlist, $rec) = OnWhitelist $sender;
173
174     if ($onlist) {
175       if (ValidDomainUser $sender) {
176         $log->msg("Whitelisting $sender - Rule: " . formatRule($rec));
177
178         Whitelist $sender, $data, $rec->{sequence}, $rec->{hit_count};
179       } else {
180         $log->msg("Sender ($sender) from this domain but user not found");
181
182         Nulllist $sender;
183       } # if
184
185       next;
186     } # if
187
188     # Check blacklist:
189     ($onlist, $rec) = OnBlacklist $sender;
190
191     if ($onlist) {
192       $log->msg("Blacklisting $sender - Rule: " . formatRule($rec));
193
194       Blacklist(
195         userid    => $userid,
196         sender    => $sender,
197         sequence  => $rec->{sequence},
198         hit_count => $rec->{hit_count},
199         data      => $data,
200       );
201
202       next;
203     } # if 
204
205     # Check nulllist:
206     ($onlist, $rec) = OnNulllist $sender;
207
208     if ($onlist) {
209       $log->msg("Nulllisting $sender - Rule: " . formatRule($rec));
210
211       Nulllist $sender, $rec->{sequence}, $rec->{hit_count};
212
213       next;
214     } # if
215
216     # Return processing:
217     $log->msg("Returning message from $sender");
218
219     ReturnMsg(
220       userid   => $userid,
221       sender   => $sender,
222       reply_to => $reply_to,
223       subject  => $subject,
224       data     => $data,
225     );
226   } # while
227 } # ProcessMsgs
228
229 # Main
230 GetOptions(
231   \%opts,
232   'user=s',
233   'verbose',
234   'debug',
235   'execute!',
236 ) or pod2usage;
237
238 $log = Logger->new(
239   path        => '/var/local/log',
240   timestamped => 'yes',
241   append      => 'yes',
242 );
243
244 my $msgfile;
245
246 if ($ARGV[0] and $ARGV[0] ne "") {
247   open $msgfile, '<', $ARGV[0];
248
249   unless ($msgfile) {
250     $log->err("Unable to open file ($ARGV[0]): $!");
251     exit 1;
252   } # if
253 } else {
254   $msgfile = SaveStdin;
255 } # if 
256
257 # Get user
258 FindUser(userid => $userid);
259
260 my $user = GetUser;
261
262 ProcessMsgs $msgfile, $user->{name}, lc $user->{email};
263
264 exit 0;