Blame


1 0fd75c45 2022-07-30 mischa #!/usr/bin/perl -w
2 0fd75c45 2022-07-30 mischa #
3 0fd75c45 2022-07-30 mischa # Virtual Vacation 3.1
4 0fd75c45 2022-07-30 mischa # by Mischa Peters <mischa at high5 dot net>
5 0fd75c45 2022-07-30 mischa # Copyright (c) 2002 - 2005 High5!
6 0fd75c45 2022-07-30 mischa # License Info: http://www.postfixadmin.com/?file=LICENSE.TXT
7 0fd75c45 2022-07-30 mischa #
8 0fd75c45 2022-07-30 mischa # Additions:
9 0fd75c45 2022-07-30 mischa # 2004/07/13 David Osborn <ossdev at daocon.com>
10 0fd75c45 2022-07-30 mischa # strict, processes domain level aliases, more
11 0fd75c45 2022-07-30 mischa # subroutines, send reply from original to address
12 0fd75c45 2022-07-30 mischa #
13 0fd75c45 2022-07-30 mischa # 2004/11/09 David Osborn <ossdev at daocon.com>
14 0fd75c45 2022-07-30 mischa # Added syslog support
15 0fd75c45 2022-07-30 mischa # Slightly better logging which includes messageid
16 0fd75c45 2022-07-30 mischa # Avoid infinite loops with domain aliases
17 0fd75c45 2022-07-30 mischa #
18 0fd75c45 2022-07-30 mischa use DBI;
19 0fd75c45 2022-07-30 mischa use strict;
20 0fd75c45 2022-07-30 mischa
21 0fd75c45 2022-07-30 mischa my $db_type = 'mysql';
22 0fd75c45 2022-07-30 mischa my $db_host = 'localhost';
23 0fd75c45 2022-07-30 mischa my $db_user = 'postfixadmin';
24 0fd75c45 2022-07-30 mischa my $db_pass = 'postfixadmin';
25 0fd75c45 2022-07-30 mischa my $db_name = 'postfix';
26 0fd75c45 2022-07-30 mischa my $sendmail = "/usr/sbin/sendmail";
27 0fd75c45 2022-07-30 mischa my $logfile = ""; # specify a file name here for example: vacation.log
28 0fd75c45 2022-07-30 mischa my $debugfile = ""; # sepcify a file name here for example: vacation.debug
29 0fd75c45 2022-07-30 mischa my $syslog = 0; # 1 if log entries should be sent to syslog
30 0fd75c45 2022-07-30 mischa
31 0fd75c45 2022-07-30 mischa my $dbh = DBI->connect("DBI:$db_type:$db_name:$db_host", "$db_user", "$db_pass", { RaiseError => 1 });
32 0fd75c45 2022-07-30 mischa
33 0fd75c45 2022-07-30 mischa # used to detect infinite address lookup loops
34 0fd75c45 2022-07-30 mischa my $loopcount=0;
35 0fd75c45 2022-07-30 mischa
36 0fd75c45 2022-07-30 mischa sub do_query {
37 0fd75c45 2022-07-30 mischa my ($query) = @_;
38 0fd75c45 2022-07-30 mischa my $sth = $dbh->prepare($query) or die "Can't prepare $query: $dbh->errstr\n";
39 0fd75c45 2022-07-30 mischa $sth->execute or die "Can't execute the query: $sth->errstr";
40 0fd75c45 2022-07-30 mischa return $sth;
41 0fd75c45 2022-07-30 mischa }
42 0fd75c45 2022-07-30 mischa
43 0fd75c45 2022-07-30 mischa sub do_debug {
44 0fd75c45 2022-07-30 mischa my ($in1, $in2, $in3, $in4, $in5, $in6) = @_;
45 0fd75c45 2022-07-30 mischa if ( $debugfile ) {
46 0fd75c45 2022-07-30 mischa my $date;
47 0fd75c45 2022-07-30 mischa open (DEBUG, ">> $debugfile") or die ("Unable to open debug file");
48 0fd75c45 2022-07-30 mischa chop ($date = `date "+%Y/%m/%d %H:%M:%S"`);
49 0fd75c45 2022-07-30 mischa print DEBUG "====== $date ======\n";
50 0fd75c45 2022-07-30 mischa printf DEBUG "%s | %s | %s | %s | %s | %s\n", $in1, $in2, $in3, $in4, $in5, $in6;
51 0fd75c45 2022-07-30 mischa close (DEBUG);
52 0fd75c45 2022-07-30 mischa }
53 0fd75c45 2022-07-30 mischa }
54 0fd75c45 2022-07-30 mischa
55 0fd75c45 2022-07-30 mischa sub do_cache {
56 0fd75c45 2022-07-30 mischa my ($to, $from) = @_;
57 0fd75c45 2022-07-30 mischa my $query = qq{SELECT cache FROM vacation WHERE email='$to' AND FIND_IN_SET('$from',cache)};
58 0fd75c45 2022-07-30 mischa my $sth = do_query ($query);
59 0fd75c45 2022-07-30 mischa my $rv = $sth->rows;
60 0fd75c45 2022-07-30 mischa if ($rv == 0) {
61 0fd75c45 2022-07-30 mischa $query = qq{UPDATE vacation SET cache=CONCAT(cache,',','$from') WHERE email='$to'};
62 0fd75c45 2022-07-30 mischa $sth = do_query ($query);
63 0fd75c45 2022-07-30 mischa }
64 0fd75c45 2022-07-30 mischa return $rv;
65 0fd75c45 2022-07-30 mischa }
66 0fd75c45 2022-07-30 mischa
67 0fd75c45 2022-07-30 mischa sub do_log {
68 0fd75c45 2022-07-30 mischa my ($messageid, $to, $from, $subject) = @_;
69 0fd75c45 2022-07-30 mischa my $date;
70 0fd75c45 2022-07-30 mischa if ( $syslog ) {
71 0fd75c45 2022-07-30 mischa open (SYSLOG, "|/usr/bin/logger -p mail.info -t Vacation") or die ("Unable to open logger");
72 0fd75c45 2022-07-30 mischa printf SYSLOG "Orig-To: %s From: %s MessageID: %s Subject: %s", $to, $from, $messageid, $subject;
73 0fd75c45 2022-07-30 mischa close (SYSLOG);
74 0fd75c45 2022-07-30 mischa }
75 0fd75c45 2022-07-30 mischa if ( $logfile ) {
76 0fd75c45 2022-07-30 mischa open (LOG, ">> $logfile") or die ("Unable to open log file");
77 0fd75c45 2022-07-30 mischa chop ($date = `date "+%Y/%m/%d %H:%M:%S"`);
78 0fd75c45 2022-07-30 mischa print LOG "$date: To: $to From: $from Subject: $subject MessageID: $messageid \n";
79 0fd75c45 2022-07-30 mischa close (LOG);
80 0fd75c45 2022-07-30 mischa }
81 0fd75c45 2022-07-30 mischa }
82 0fd75c45 2022-07-30 mischa
83 0fd75c45 2022-07-30 mischa sub do_mail {
84 0fd75c45 2022-07-30 mischa my ($from, $to, $subject, $body) = @_;
85 0fd75c45 2022-07-30 mischa open (MAIL, "| $sendmail -t -f $from") or die ("Unable to open sendmail");
86 0fd75c45 2022-07-30 mischa print MAIL "From: $from\n";
87 0fd75c45 2022-07-30 mischa print MAIL "To: $to\n";
88 0fd75c45 2022-07-30 mischa print MAIL "Subject: $subject\n";
89 0fd75c45 2022-07-30 mischa print MAIL "X-Loop: Postfix Admin Virtual Vacation\n\n";
90 0fd75c45 2022-07-30 mischa print MAIL "$body";
91 0fd75c45 2022-07-30 mischa close (MAIL);
92 0fd75c45 2022-07-30 mischa }
93 0fd75c45 2022-07-30 mischa
94 0fd75c45 2022-07-30 mischa sub find_real_address {
95 0fd75c45 2022-07-30 mischa my ($email) = @_;
96 0fd75c45 2022-07-30 mischa if (++$loopcount > 20) {
97 0fd75c45 2022-07-30 mischa do_log ("find_real_address loop!", "currently: $email", "ERROR", "ERROR");
98 0fd75c45 2022-07-30 mischa print ("possible infinite loop in find_real_address for <$email>. Check for alias loop\n");
99 0fd75c45 2022-07-30 mischa exit 1;
100 0fd75c45 2022-07-30 mischa }
101 0fd75c45 2022-07-30 mischa my $realemail;
102 0fd75c45 2022-07-30 mischa my $query = qq{SELECT email FROM vacation WHERE email='$email' and active=1};
103 0fd75c45 2022-07-30 mischa my $sth = do_query ($query);
104 0fd75c45 2022-07-30 mischa my $rv = $sth->rows;
105 0fd75c45 2022-07-30 mischa
106 0fd75c45 2022-07-30 mischa # Recipient has vacation
107 0fd75c45 2022-07-30 mischa if ($rv == 1) {
108 0fd75c45 2022-07-30 mischa $realemail = $email;
109 0fd75c45 2022-07-30 mischa
110 0fd75c45 2022-07-30 mischa } else {
111 0fd75c45 2022-07-30 mischa $query = qq{SELECT goto FROM alias WHERE address='$email'};
112 0fd75c45 2022-07-30 mischa $sth = do_query ($query);
113 0fd75c45 2022-07-30 mischa $rv = $sth->rows;
114 0fd75c45 2022-07-30 mischa
115 0fd75c45 2022-07-30 mischa # Recipient is an alias, check if mailbox has vacation
116 0fd75c45 2022-07-30 mischa if ($rv == 1) {
117 0fd75c45 2022-07-30 mischa my @row = $sth->fetchrow_array;
118 0fd75c45 2022-07-30 mischa my $alias = $row[0];
119 0fd75c45 2022-07-30 mischa $query = qq{SELECT email FROM vacation WHERE email='$alias' and active=1};
120 0fd75c45 2022-07-30 mischa $sth = do_query ($query);
121 0fd75c45 2022-07-30 mischa $rv = $sth->rows;
122 0fd75c45 2022-07-30 mischa
123 0fd75c45 2022-07-30 mischa # Alias has vacation
124 0fd75c45 2022-07-30 mischa if ($rv == 1) {
125 0fd75c45 2022-07-30 mischa $realemail = $alias;
126 0fd75c45 2022-07-30 mischa }
127 0fd75c45 2022-07-30 mischa
128 0fd75c45 2022-07-30 mischa # We still have to look for domain level aliases...
129 0fd75c45 2022-07-30 mischa } else {
130 0fd75c45 2022-07-30 mischa my ($user, $domain) = split(/@/, $email);
131 0fd75c45 2022-07-30 mischa $query = qq{SELECT goto FROM alias WHERE address='\@$domain'};
132 0fd75c45 2022-07-30 mischa $sth = do_query ($query);
133 0fd75c45 2022-07-30 mischa $rv = $sth->rows;
134 0fd75c45 2022-07-30 mischa
135 0fd75c45 2022-07-30 mischa # The receipient has a domain level alias
136 0fd75c45 2022-07-30 mischa if ($rv == 1) {
137 0fd75c45 2022-07-30 mischa my @row = $sth->fetchrow_array;
138 0fd75c45 2022-07-30 mischa my $wildcard_dest = $row[0];
139 0fd75c45 2022-07-30 mischa my ($wilduser, $wilddomain) = split(/@/, $wildcard_dest);
140 0fd75c45 2022-07-30 mischa
141 0fd75c45 2022-07-30 mischa # Check domain alias
142 0fd75c45 2022-07-30 mischa if ($wilduser) {
143 0fd75c45 2022-07-30 mischa ($rv, $realemail) = find_real_address ($wildcard_dest);
144 0fd75c45 2022-07-30 mischa } else {
145 0fd75c45 2022-07-30 mischa my $new_email = $user . '@' . $wilddomain;
146 0fd75c45 2022-07-30 mischa ($rv, $realemail) = find_real_address ($new_email);
147 0fd75c45 2022-07-30 mischa }
148 0fd75c45 2022-07-30 mischa }
149 0fd75c45 2022-07-30 mischa }
150 0fd75c45 2022-07-30 mischa }
151 0fd75c45 2022-07-30 mischa return ($rv, $realemail);
152 0fd75c45 2022-07-30 mischa }
153 0fd75c45 2022-07-30 mischa
154 0fd75c45 2022-07-30 mischa sub send_vacation_email {
155 0fd75c45 2022-07-30 mischa my ($email, $orig_subject, $orig_from, $orig_to, $orig_messageid) = @_;
156 0fd75c45 2022-07-30 mischa my $query = qq{SELECT subject,body FROM vacation WHERE email='$email'};
157 0fd75c45 2022-07-30 mischa my $sth = do_query ($query);
158 0fd75c45 2022-07-30 mischa my $rv = $sth->rows;
159 0fd75c45 2022-07-30 mischa if ($rv == 1) {
160 0fd75c45 2022-07-30 mischa my @row = $sth->fetchrow_array;
161 0fd75c45 2022-07-30 mischa if (do_cache ($email, $orig_from)) { return; }
162 0fd75c45 2022-07-30 mischa do_debug ("[SEND RESPONSE] for $orig_messageid:\n", "FROM: $email (orig_to: $orig_to)\n", "TO: $orig_from\n", "SUBJECT: $orig_subject\n", "VACATION SUBJECT: $row[0]\n", "VACATION BODY: $row[1]\n");
163 0fd75c45 2022-07-30 mischa do_mail ($orig_to, $orig_from, $row[0], $row[1]);
164 0fd75c45 2022-07-30 mischa do_log ($orig_messageid, $orig_to, $orig_from, $orig_subject);
165 0fd75c45 2022-07-30 mischa }
166 0fd75c45 2022-07-30 mischa
167 0fd75c45 2022-07-30 mischa }
168 0fd75c45 2022-07-30 mischa
169 0fd75c45 2022-07-30 mischa ########################### main #################################
170 0fd75c45 2022-07-30 mischa
171 0fd75c45 2022-07-30 mischa my ($from, $to, $cc, $subject, $messageid);
172 0fd75c45 2022-07-30 mischa
173 0fd75c45 2022-07-30 mischa # Take headers apart
174 0fd75c45 2022-07-30 mischa while (<STDIN>) {
175 0fd75c45 2022-07-30 mischa last if (/^$/);
176 0fd75c45 2022-07-30 mischa if (/^from:\s+(.*)\n$/i) { $from = $1; }
177 0fd75c45 2022-07-30 mischa if (/^to:\s+(.*)\n$/i) { $to = $1; }
178 0fd75c45 2022-07-30 mischa if (/^cc:\s+(.*)\n$/i) { $cc = $1; }
179 0fd75c45 2022-07-30 mischa if (/^subject:\s+(.*)\n$/i) { $subject = $1; }
180 0fd75c45 2022-07-30 mischa if (/^message-id:\s+(.*)\n$/i) { $messageid = $1; }
181 0fd75c45 2022-07-30 mischa if (/^precedence:\s+(bulk|list|junk)/i) { exit (0); }
182 0fd75c45 2022-07-30 mischa if (/^x-loop:\s+postfix\ admin\ virtual\ vacation/i) { exit (0); }
183 0fd75c45 2022-07-30 mischa }
184 0fd75c45 2022-07-30 mischa
185 0fd75c45 2022-07-30 mischa # If either From: or To: are not set, exit
186 0fd75c45 2022-07-30 mischa if (!$from || !$to) { exit (0); }
187 0fd75c45 2022-07-30 mischa
188 0fd75c45 2022-07-30 mischa $from = lc ($from);
189 0fd75c45 2022-07-30 mischa
190 0fd75c45 2022-07-30 mischa # Check if it's an obvious sender, exit
191 0fd75c45 2022-07-30 mischa if ($from =~ /([\w\-.%]+\@[\w.-]+)/) { $from = $1; }
192 0fd75c45 2022-07-30 mischa if ($from eq "" || $from =~ /^owner-|-(request|owner)\@|^(mailer-daemon|postmaster)\@/i) { exit (0); }
193 0fd75c45 2022-07-30 mischa
194 0fd75c45 2022-07-30 mischa # Strip To: and Cc: and push them in array
195 0fd75c45 2022-07-30 mischa my @strip_cc_array;
196 0fd75c45 2022-07-30 mischa my @strip_to_array = split(/, */, lc ($to) );
197 0fd75c45 2022-07-30 mischa if (defined $cc) { @strip_cc_array = split(/, */, lc ($cc) ); }
198 0fd75c45 2022-07-30 mischa push (@strip_to_array, @strip_cc_array);
199 0fd75c45 2022-07-30 mischa
200 0fd75c45 2022-07-30 mischa my @search_array;
201 0fd75c45 2022-07-30 mischa
202 0fd75c45 2022-07-30 mischa # Strip email address from headers
203 0fd75c45 2022-07-30 mischa for (@strip_to_array) {
204 0fd75c45 2022-07-30 mischa if ($_ =~ /([\w\-.%]+\@[\w.-]+)/) {
205 0fd75c45 2022-07-30 mischa push (@search_array, $1);
206 0fd75c45 2022-07-30 mischa do_debug ("[STRIP RECIPIENTS]: ", $messageid, $1, "-", "-", "-");
207 0fd75c45 2022-07-30 mischa }
208 0fd75c45 2022-07-30 mischa }
209 0fd75c45 2022-07-30 mischa
210 0fd75c45 2022-07-30 mischa # Search for email address which has vacation
211 0fd75c45 2022-07-30 mischa for (@search_array) {
212 0fd75c45 2022-07-30 mischa my ($rv, $email) = find_real_address ($_);
213 0fd75c45 2022-07-30 mischa if ($rv == 1) {
214 0fd75c45 2022-07-30 mischa do_debug ("[FOUND VACATION]: ", $messageid, $from, $to, $email, $subject);
215 0fd75c45 2022-07-30 mischa send_vacation_email( $email, $subject, $from, $to, $messageid);
216 0fd75c45 2022-07-30 mischa }
217 0fd75c45 2022-07-30 mischa }
218 0fd75c45 2022-07-30 mischa
219 0fd75c45 2022-07-30 mischa 0;