/** * Constructs a new procmail recipe. * * @param array $params Array of parameters. * REQUIRED FIELDS: * 'action' * OPTIONAL FIELDS: * 'action-value' (only used if the * 'action' requires it) * 'disable' * @param array $scriptparams Array of parameters passed to * Ingo_Script_Procmail. */ public function __construct($params = array(), $scriptparams = array()) { $this->_disable = !empty($params['disable']); $this->_params = array_merge($this->_params, $scriptparams); $delivery = '| '; if (isset($this->_params['delivery_agent'])) { $delivery .= $this->_params['delivery_agent'] . ' '; } if (isset($this->_params['delivery_mailbox_prefix'])) { $delivery .= ' ' . $this->_params['delivery_mailbox_prefix']; } switch ($params['action']) { case 'Ingo_Rule_User_Keep': // Note: you may have to set the DEFAULT variable in your // backend configuration. $this->_action[] = $delivery .= '$DEFAULT'; break; case 'Ingo_Rule_User_Move': $this->_action[] = $delivery .= $this->procmailPath($params['action-value']); break; case 'Ingo_Rule_User_Discard': $this->_action[] = '/dev/null'; break; case 'Ingo_Rule_User_Redirect': $this->_action[] = '! ' . $params['action-value']; break; case 'Ingo_Rule_User_RedirectKeep': $this->_action[] = '{'; $this->_action[] = ' :0 c'; $this->_action[] = ' ! ' . $params['action-value']; if (strpos($this->_flags, 'c') === false) { $this->_action[] = ''; $this->_action[] = ' :0' . (isset($this->_params['delivery_agent']) ? ' w' : ''); $this->_action[] = ' ' . $delivery . '$DEFAULT'; } $this->_action[] = '}'; break; case 'Ingo_Rule_User_Reject': $this->_action[] = '{'; $this->_action[] = ' :0 h'; $this->_action[] = ' SUBJECT=| formail -xSubject:'; $this->_action[] = ''; $this->_action[] = ' :0 h'; $this->_action[] = ' SENDER=| formail -zxFrom:'; $this->_action[] = ''; $this->_action[] = ' :0 Wh'; $this->_action[] = ' * !^FROM_DAEMON'; $this->_action[] = ' * !^X-Loop: $SENDER'; $this->_action[] = ' | (formail -rA"X-Loop: $SENDER" \\'; $reason = $params['action-value']; if (Horde_Mime::is8bit($reason)) { $this->_action[] = ' -i"Subject: Re: $SUBJECT" \\'; $this->_action[] = ' -i"Content-Transfer-Encoding: quoted-printable" \\'; $this->_action[] = ' -i"Content-Type: text/plain; charset=UTF-8" ; \\'; $reason = Horde_Mime_QuotedPrintable::encode($reason); } else { $this->_action[] = ' -i"Subject: Re: $SUBJECT" ; \\'; } $reason = addcslashes($reason, "\\\n\r\t\"`"); $this->_action[] = ' ' . $this->_params['echo'] . ' -e "' . $reason . '" \\'; $this->_action[] = ' ) | $SENDMAIL -oi -t'; $this->_action[] = '}'; break; case 'Ingo_Rule_System_Vacation': $days = $params['action-value']['days']; $timed = !empty($params['action-value']['start']) && !empty($params['action-value']['end']); $this->_action[] = '{'; foreach ($params['action-value']['addresses'] as $address) { if (empty($address)) { continue; } $this->_action[] = ' :0'; $this->_action[] = ' * ^TO_' . $address; $this->_action[] = ' {'; $this->_action[] = ' FILEDATE=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && ' . $this->_params['ls'] . ' -lcn --time-style=+%s ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' | ' . 'awk \'{ print $6 + (' . $days * 86400 . ') }\'`'; $this->_action[] = ' DATE=`' . $this->_params['date'] . ' +%s`'; $this->_action[] = ' DUMMY=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && ' . 'test $FILEDATE -le $DATE && ' . 'rm ${VACATION_DIR:-.}/\'.vacation.' . $address . '\'`'; if ($timed) { $this->_action[] = ' START=' . $params['action-value']['start']; $this->_action[] = ' END=' . $params['action-value']['end']; } $this->_action[] = ''; $this->_action[] = ' :0 h'; $this->_action[] = ' SUBJECT=| formail -xSubject:'; $this->_action[] = ''; $this->_action[] = ' :0 Whc: ${VACATION_DIR:-.}/vacation.lock'; if ($timed) { $this->_action[] = ' * ? test $DATE -gt $START && test $END -gt $DATE'; } $this->_action[] = ' {'; $this->_action[] = ' :0 Wh'; $this->_action[] = ' * ^TO_' . $address; $this->_action[] = ' * !^X-Loop: ' . $address; $this->_action[] = ' * !^X-Spam-Flag: YES'; if (count($params['action-value']['excludes']) > 0) { foreach ($params['action-value']['excludes'] as $exclude) { if (!empty($exclude)) { $this->_action[] = ' * !^From.*' . $exclude; } } } if ($params['action-value']['ignorelist']) { $this->_action[] = ' * !^FROM_DAEMON'; } $this->_action[] = ' | formail -rD 8192 ${VACATION_DIR:-.}/.vacation.' . $address; $this->_action[] = ' :0 eh'; $this->_action[] = ' | (formail -rI"Precedence: junk" \\'; $this->_action[] = ' -a"From: <' . $address . '>" \\'; $this->_action[] = ' -A"X-Loop: ' . $address . '" \\'; $reason = Ingo_Rule_System_Vacation::vacationReason($params['action-value']['reason'], $params['action-value']['start'], $params['action-value']['end']); if (Horde_Mime::is8bit($reason)) { $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)') . '" \\'; $this->_action[] = ' -i"Content-Transfer-Encoding: quoted-printable" \\'; $this->_action[] = ' -i"Content-Type: text/plain; charset=UTF-8" ; \\'; $reason = Horde_Mime_QuotedPrintable::encode($reason); } else { $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)') . '" ; \\'; } $reason = addcslashes($reason, "\\\n\r\t\"`"); $this->_action[] = ' ' . $this->_params['echo'] . ' -e "' . $reason . '" \\'; $this->_action[] = ' ) | $SENDMAIL -f' . $address . ' -oi -t'; $this->_action[] = ' }'; $this->_action[] = ' }'; } $this->_action[] = '}'; break; case 'Ingo_Rule_System_Forward': /* Make sure that we prevent mail loops using 3 methods. * * First, we call sendmail -f to set the envelope sender to be the * same as the original sender, so bounces will go to the original * sender rather than to us. This unfortunately triggers lots of * Authentication-Warning: messages in sendmail's logs. * * Second, add an X-Loop header, to handle the case where the * address we forward to forwards back to us. * * Third, don't forward mailer daemon messages (i.e., bounces). * Method 1 above should make this redundant, unless we're sending * mail from this account and have a bad forward-to account. * * Get the from address, saving a call to formail if possible. * The procmail code for doing this is borrowed from the * Procmail Library Project, http://pm-lib.sourceforge.net/. * The Ingo project has the permission to use Procmail Library code * under Apache licence v 1.x or any later version. * Permission obtained 2006-04-04 from Author Jari Aalto. */ $this->_action[] = '{'; $this->_action[] = ' :0 '; $this->_action[] = ' *$ ! ^From *\\/[^ ]+'; $this->_action[] = ' *$ ! ^Sender: *\\/[^ ]+'; $this->_action[] = ' *$ ! ^From: *\\/[^ ]+'; $this->_action[] = ' *$ ! ^Reply-to: *\\/[^ ]+'; $this->_action[] = ' {'; $this->_action[] = ' OUTPUT = `formail -zxFrom:`'; $this->_action[] = ' }'; $this->_action[] = ' :0 E'; $this->_action[] = ' {'; $this->_action[] = ' OUTPUT = $MATCH'; $this->_action[] = ' }'; $this->_action[] = ''; /* Forward to each address on our list. */ foreach ($params['action-value'] as $address) { if (!empty($address)) { $this->_action[] = ' :0 c'; $this->_action[] = ' * !^FROM_MAILER'; $this->_action[] = ' * !^X-Loop: to-' . $address; $this->_action[] = ' | formail -A"X-Loop: to-' . $address . '" | $SENDMAIL -oi -f $OUTPUT ' . $address; } } /* In case of mail loop or bounce, store a copy locally. Note * that if we forward to more than one address, only a mail loop * on the last address will cause a local copy to be saved. TODO: * The next two lines are redundant (and create an extra copy of * the message) if "Keep a copy of messages in this account" is * checked. */ $this->_action[] = ' :0 E' . (isset($this->_params['delivery_agent']) ? 'w' : ''); $this->_action[] = ' ' . $delivery . '$DEFAULT'; $this->_action[] = ' :0 '; $this->_action[] = ' /dev/null'; $this->_action[] = '}'; break; default: $this->_valid = false; break; } }
public function testVacationEnabled() { $vacation = new Ingo_Rule_System_Vacation(); $vacation->addAddresses('*****@*****.**'); $vacation->disable = false; $vacation->subject = 'Subject'; $vacation->reason = "Because I don't like working!"; $this->storage->updateRule($vacation); $this->_assertScript(':0 { :0 * ^TO_from@example.com { FILEDATE=`test -f ${VACATION_DIR:-.}/\'.vacation.from@example.com\' && ls -lcn --time-style=+%s ${VACATION_DIR:-.}/\'.vacation.from@example.com\' | awk \'{ print $6 + (604800) }\'` DATE=`date +%s` DUMMY=`test -f ${VACATION_DIR:-.}/\'.vacation.from@example.com\' && test $FILEDATE -le $DATE && rm ${VACATION_DIR:-.}/\'.vacation.from@example.com\'` :0 h SUBJECT=| formail -xSubject: :0 Whc: ${VACATION_DIR:-.}/vacation.lock { :0 Wh * ^TO_from@example.com * !^X-Loop: from@example.com * !^X-Spam-Flag: YES * !^FROM_DAEMON | formail -rD 8192 ${VACATION_DIR:-.}/.vacation.from@example.com :0 eh | (formail -rI"Precedence: junk" \\ -a"From: <*****@*****.**>" \\ -A"X-Loop: from@example.com" \\ -i"Subject: Subject (Re: $SUBJECT)" ; \\ echo -e "Because I don\'t like working!" \\ ) | $SENDMAIL -ffrom@example.com -oi -t } } }'); }
/** * Generates the maildrop script to handle vacation messages. * * @param Ingo_Rule $rule Rule object. */ protected function _generateVacation(Ingo_Rule $rule) { if (!count($rule)) { return; } $this->_addItem(Ingo::RULE_VACATION, new Ingo_Script_Maildrop_Comment(_("Vacation"), $disable, true)); $recipe = new Ingo_Script_Maildrop_Recipe(array('action' => 'Ingo_Rule_System_Vacation', 'action-value' => array('addresses' => $rule->addresses, 'subject' => $rule->subject, 'days' => $rule->days, 'ignorelist' => $rule->ignore_list, 'excludes' => $rule->exclude, 'start' => $rule->start, 'end' => $rule->end), 'disable' => $disable), $this->_params); $this->_addItem(Ingo::RULE_VACATION, $recipe); $this->_addItem(Ingo::RULE_VACATION, new Ingo_Script_String(Ingo_Rule_System_Vacation::vacationReason($rule->reason, $rule->start, $rule->end)), 'vacation.msg'); }
/** * Set vacation * * @param array $info Vacation details. * - addresses: (mixed) Address list to enable vacation for. * - days: (integer) Number of days between vacation replies. * - excludes: (mixed) Address list to exclude from vacation replies. * - ignorelist: (boolean) If set, ignore mailing lists. * - reason: (string) Vacation message. * - subject: (string) Vacation email subject. * - start: (integer) Timestamp of vacation starttime. * - end: (integer) Timestamp of vacation endtime. * @param boolean $enable Enable the filter? * * @throws Ingo_Exception */ public function setVacation($info, $enable = true) { global $injector, $registry; if (empty($info)) { return true; } /* Get vacation filter. */ $ingo_storage = $injector->getInstance('Ingo_Factory_Storage')->create(); $vacation = new Ingo_Rule_System_Vacation(); /* Make sure we have at least one address. */ if (empty($info['addresses'])) { $identity = $injector->getInstance('Horde_Core_Factory_Identity')->create(); /* Remove empty lines. */ $info['addresses'] = preg_replace('/\\n{2,}/', "\n", implode("\n", $identity->getAll('from_addr'))); if (empty($info['addresses'])) { $info['addresses'] = $registry->getAuth(); } } $vacation->addresses($info['addresses']); if (isset($info['days'])) { $vacation->days = $info['days']; } if (isset($info['excludes'])) { $vacation->exclude = $info['excludes']; } if (isset($info['ignorelist'])) { $vacation->ignore_list = $info['ignorelist'] == 'on'; } if (isset($info['reason'])) { $vacation->reason = $info['reason']; } if (isset($info['subject'])) { $vacation->subject = $info['subject']; } if (isset($info['start'])) { $vacation->start = $info['start']; } if (isset($info['end'])) { $vacation->end = $info['end']; } $vacation->enable = $enable; $ingo_storage->updateRule($vacation); $injector->getInstance('Ingo_Factory_Script')->activateAll(); }
public function testVacationEnabled() { $vacation = new Ingo_Rule_System_Vacation(); $vacation->addAddresses('*****@*****.**'); $vacation->disable = false; $vacation->subject = 'Subject'; $vacation->reason = "Because I don't like working!"; $this->storage->updateRule($vacation); $this->_assertScript('require ["vacation", "regex"]; if allof ( not exists "list-help", not exists "list-unsubscribe", not exists "list-subscribe", not exists "list-owner", not exists "list-post", not exists "list-archive", not exists "list-id", not exists "Mailing-List", not header :comparator "i;ascii-casemap" :is "Precedence" ["list", "bulk", "junk"], not header :comparator "i;ascii-casemap" :matches "To" "Multiple recipients of*" ) { vacation :days 7 :addresses "*****@*****.**" :subject "Subject" "Because I don\'t like working!"; }'); }
/** */ protected function _vacationCode() { $code = 'vacation '; if (!empty($this->_vars['days'])) { $code .= ':days ' . $this->_vars['days'] . ' '; } $addresses = $this->_vars['addresses']; $stringlist = ''; if (count($addresses) > 1) { foreach ($addresses as $address) { $address = trim($address); if (!empty($address)) { $stringlist .= empty($stringlist) ? '"' : ', "'; $stringlist .= Ingo_Script_Sieve::escapeString($address) . '"'; } } $stringlist = "[" . $stringlist . "] "; } elseif (count($addresses) == 1) { $stringlist = '"' . Ingo_Script_Sieve::escapeString($addresses[0]) . '" '; } if (!empty($stringlist)) { $code .= ':addresses ' . $stringlist; } if (!empty($this->_vars['subject'])) { $code .= ':subject "' . Horde_Mime::encode(Ingo_Script_Sieve::escapeString($this->_vars['subject'])) . '" '; } return $code . '"' . Ingo_Script_Sieve::escapeString(Ingo_Rule_System_Vacation::vacationReason($this->_vars['reason'], $this->_vars['start'], $this->_vars['end'])) . '";'; }