/** * 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) * @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); switch ($params['action']) { case Ingo_Storage::ACTION_KEEP: // Note: you may have to set the DEFAULT variable in your // backend configuration. if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) { $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT'; } elseif (isset($this->_params['delivery_agent'])) { $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' $DEFAULT'; } else { $this->_action[] = '$DEFAULT'; } break; case Ingo_Storage::ACTION_MOVE: if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) { $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . $this->procmailPath($params['action-value']); } elseif (isset($this->_params['delivery_agent'])) { $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->procmailPath($params['action-value']); } else { $this->_action[] = $this->procmailPath($params['action-value']); } break; case Ingo_Storage::ACTION_DISCARD: $this->_action[] = '/dev/null'; break; case Ingo_Storage::ACTION_REDIRECT: $this->_action[] = '! ' . $params['action-value']; break; case Ingo_Storage::ACTION_REDIRECTKEEP: $this->_action[] = '{'; $this->_action[] = ' :0 c'; $this->_action[] = ' ! ' . $params['action-value']; $this->_action[] = ''; $this->_action[] = ' :0' . (isset($this->_params['delivery_agent']) ? ' w' : ''); if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) { $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT'; } elseif (isset($this->_params['delivery_agent'])) { $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT'; } else { $this->_action[] = ' $DEFAULT'; } $this->_action[] = '}'; break; case Ingo_Storage::ACTION_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::quotedPrintableEncode($reason, "\n"); } 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_Storage::ACTION_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)) { $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_Script_Util::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::quotedPrintableEncode($reason, "\n"); } 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_Storage::ACTION_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' : ''); if (isset($this->_params['delivery_agent'])) { $this->_action[] = isset($this->_params['delivery_mailbox_prefix']) ? ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' : ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT'; } else { $this->_action[] = ' $DEFAULT'; } $this->_action[] = ' :0 '; $this->_action[] = ' /dev/null'; $this->_action[] = '}'; break; default: $this->_valid = false; break; } }
/** * Serialize data. * * @param mixed $data The data to be serialized. * @param mixed $mode The mode of serialization. Can be * either a single mode or array of modes. * If array, will be serialized in the * order provided. * @param mixed $params Any additional parameters the serialization method * requires. * * @return string A serialized string. * @throws Horde_Serialize_Exception */ protected static function _serialize($data, $mode, $params = null) { switch ($mode) { case self::NONE: break; // $params['level'] = Level of compression (default: 3) // $params['workfactor'] = How does compression phase behave when given // worst case, highly repetitive, input data // (default: 30) // $params['level'] = Level of compression (default: 3) // $params['workfactor'] = How does compression phase behave when given // worst case, highly repetitive, input data // (default: 30) case self::BZIP: $data = bzcompress($data, isset($params['level']) ? $params['level'] : 3, isset($params['workfactor']) ? $params['workfactor'] : 30); if (is_integer($data)) { $data = false; } break; case self::WDDX: $data = wddx_serialize_value($data); break; case self::IMAP8: $data = Horde_Mime::quotedPrintableEncode($data); break; case self::IMAPUTF7: $data = Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap(Horde_String::convertCharset($data, 'ISO-8859-1', 'UTF-8')); break; case self::IMAPUTF8: $data = Horde_Mime::decode($data); break; // $params['level'] = Level of compression (default: 3) // $params['level'] = Level of compression (default: 3) case self::GZ_DEFLATE: $data = gzdeflate($data, isset($params['level']) ? $params['level'] : 3); break; case self::BASIC: $data = serialize($data); break; // $params['level'] = Level of compression (default: 3) // $params['level'] = Level of compression (default: 3) case self::GZ_COMPRESS: $data = gzcompress($data, isset($params['level']) ? $params['level'] : 3); break; case self::BASE64: $data = base64_encode($data); break; // $params['level'] = Level of compression (default: 3) // $params['level'] = Level of compression (default: 3) case self::GZ_ENCODE: $data = gzencode($data, isset($params['level']) ? $params['level'] : 3); break; case self::RAW: $data = rawurlencode($data); break; case self::URL: $data = urlencode($data); break; // $params = Source character set // $params = Source character set case self::UTF7: $data = Horde_String::convertCharset($data, $params, 'UTF-7'); break; // $params = Source character set // $params = Source character set case self::UTF7_BASIC: $data = self::serialize($data, array(self::UTF7, self::BASIC), $params); break; case self::JSON: $tmp = json_encode($data); /* Basic error handling attempts. * TODO: JSON_ERROR_UTF8 = 5; available as of PHP 5.3.3 */ if (json_last_error() === 5) { $data = json_encode(Horde_String::convertCharset($data, $params, 'UTF-8', true)); } else { $data = $tmp; } break; case self::LZF: $data = lzf_compress($data); break; } if ($data === false) { throw new Horde_Serialize_Exception('Serialization failed.'); } return $data; }
/** * Export this component in vCal format. * * @param string $base The type of the base object. * * @return string vCal format data. */ protected function _exportvData($base = 'VCALENDAR') { $result = 'BEGIN:' . Horde_String::upper($base) . $this->_newline; // VERSION is not allowed for entries enclosed in VCALENDAR/ICALENDAR, // as it is part of the enclosing VCALENDAR/ICALENDAR. See rfc2445 if ($base !== 'VEVENT' && $base !== 'VTODO' && $base !== 'VALARM' && $base !== 'VJOURNAL' && $base !== 'VFREEBUSY' && $base != 'VTIMEZONE' && $base != 'STANDARD' && $base != 'DAYLIGHT') { // Ensure that version is the first attribute. $result .= 'VERSION:' . $this->_version . $this->_newline; } foreach ($this->_attributes as $attribute) { $name = $attribute['name']; if ($name == 'VERSION') { // Already done. continue; } $params_str = ''; $params = $attribute['params']; if ($params) { foreach ($params as $param_name => $param_value) { /* Skip CHARSET for iCalendar 2.0 data, not allowed. */ if ($param_name == 'CHARSET' && !$this->_oldFormat) { continue; } /* Skip VALUE=DATE for vCalendar 1.0 data, not allowed. */ if ($this->_oldFormat && $param_name == 'VALUE' && $param_value == 'DATE') { continue; } if ($param_value === null) { $params_str .= ";{$param_name}"; } else { if (!is_array($param_value)) { $param_value = array($param_value); } foreach ($param_value as &$one_param_value) { $len = strlen($one_param_value); $safe_value = ''; $quote = false; for ($i = 0; $i < $len; ++$i) { $ord = ord($one_param_value[$i]); // Accept only valid characters. if ($ord == 9 || $ord == 32 || $ord == 33 || $ord >= 35 && $ord <= 126 || $ord >= 128) { $safe_value .= $one_param_value[$i]; // Characters above 128 do not need to be // quoted as per RFC2445 but Outlook requires // this. if ($ord == 44 || $ord == 58 || $ord == 59 || $ord >= 128) { $quote = true; } } } if ($quote) { $safe_value = '"' . $safe_value . '"'; } $one_param_value = $safe_value; } $params_str .= ";{$param_name}=" . implode(',', $param_value); } } } $value = $attribute['value']; switch ($name) { // Date fields. case 'COMPLETED': case 'CREATED': case 'DCREATED': case 'LAST-MODIFIED': case 'X-MOZ-LASTACK': case 'X-MOZ-SNOOZE-TIME': $value = $this->_exportDateTime($value); break; case 'DTEND': case 'DTSTART': case 'DTSTAMP': case 'DUE': case 'AALARM': case 'RECURRENCE-ID': $floating = $base == 'STANDARD' || $base == 'DAYLIGHT' || isset($params['TZID']); if (isset($params['VALUE'])) { if ($params['VALUE'] == 'DATE') { // VCALENDAR 1.0 uses T000000 - T235959 for all day events: if ($this->_oldFormat && $name == 'DTEND') { $d = new Horde_Date($value); $value = new Horde_Date(array('year' => $d->year, 'month' => $d->month, 'mday' => $d->mday - 1)); $value = $this->_exportDate($value, '235959'); } else { $value = $this->_exportDate($value, '000000'); } } else { $value = $this->_exportDateTime($value, $floating); } } else { $value = $this->_exportDateTime($value, $floating); } break; // Comma seperated dates. // Comma seperated dates. case 'EXDATE': case 'RDATE': $floating = $base == 'STANDARD' || $base == 'DAYLIGHT'; $dates = array(); foreach ($value as $date) { if (isset($params['VALUE'])) { if ($params['VALUE'] == 'DATE') { $dates[] = $this->_exportDate($date, '000000'); } elseif ($params['VALUE'] == 'PERIOD') { $dates[] = $this->_exportPeriod($date); } else { $dates[] = $this->_exportDateTime($date, $floating); } } else { $dates[] = $this->_exportDateTime($date, $floating); } } $value = implode($this->_oldFormat ? ';' : ',', $dates); break; case 'TRIGGER': if (isset($params['VALUE'])) { if ($params['VALUE'] == 'DATE-TIME') { $value = $this->_exportDateTime($value); } elseif ($params['VALUE'] == 'DURATION') { $value = $this->_exportDuration($value); } } else { $value = $this->_exportDuration($value); } break; // Duration fields. // Duration fields. case 'DURATION': $value = $this->_exportDuration($value); break; // Period of time fields. // Period of time fields. case 'FREEBUSY': $value_str = ''; foreach ($value as $period) { $value_str .= empty($value_str) ? '' : ','; $value_str .= $this->_exportPeriod($period); } $value = $value_str; break; // UTC offset fields. // UTC offset fields. case 'TZOFFSETFROM': case 'TZOFFSETTO': $value = $this->_exportUtcOffset($value); break; // Integer fields. // Integer fields. case 'PERCENT-COMPLETE': case 'PRIORITY': case 'REPEAT': case 'SEQUENCE': $value = "{$value}"; break; // Geo fields. // Geo fields. case 'GEO': if ($this->_oldFormat) { $value = $value['longitude'] . ',' . $value['latitude']; } else { $value = $value['latitude'] . ';' . $value['longitude']; } break; // Recurrence fields. // Recurrence fields. case 'EXRULE': case 'RRULE': break; default: if ($this->_oldFormat) { if (is_array($attribute['values']) && count($attribute['values']) > 1) { $values = $attribute['values']; if ($name == 'N' || $name == 'ADR' || $name == 'ORG') { $glue = ';'; } else { $glue = ','; } $values = str_replace(';', '\\;', $values); $value = implode($glue, $values); } else { /* vcard 2.1 and vcalendar 1.0 escape only * semicolons */ $value = str_replace(';', '\\;', $value); } // Text containing newlines or ASCII >= 127 must be BASE64 // or QUOTED-PRINTABLE encoded. Currently we use // QUOTED-PRINTABLE as default. if (preg_match("/[^ -]/", $value) && empty($params['ENCODING'])) { $params['ENCODING'] = 'QUOTED-PRINTABLE'; $params_str .= ';ENCODING=QUOTED-PRINTABLE'; // Add CHARSET as well. At least the synthesis client // gets confused otherwise if (empty($params['CHARSET'])) { $params['CHARSET'] = 'UTF-8'; $params_str .= ';CHARSET=' . $params['CHARSET']; } } } else { if (is_array($attribute['values']) && count($attribute['values'])) { $values = $attribute['values']; if ($name == 'N' || $name == 'ADR' || $name == 'ORG') { $glue = ';'; } else { $glue = ','; } // As of rfc 2426 2.5 semicolon and comma must be // escaped. $values = str_replace(array('\\', ';', ','), array('\\\\', '\\;', '\\,'), $values); $value = implode($glue, $values); } else { // As of rfc 2426 2.5 semicolon and comma must be // escaped. $value = str_replace(array('\\', ';', ','), array('\\\\', '\\;', '\\,'), $value); } $value = preg_replace('/\\r?\\n/', '\\n', $value); } break; } $value = str_replace("\r", '', $value); if (!empty($params['ENCODING']) && $params['ENCODING'] == 'QUOTED-PRINTABLE' && strlen(trim($value))) { $result .= $name . $params_str . ':' . preg_replace(array('/(?<!\\r)\\n/', '/(?<!=)\\r\\n/'), array("\r\n", "=0D=0A=\r\n "), Horde_Mime::quotedPrintableEncode($value)) . $this->_newline; } else { $attr_string = $name . $params_str . ':' . $value; if (!$this->_oldFormat) { if (isset($params['ENCODING']) && $params['ENCODING'] == 'b') { $attr_string = trim(chunk_split($attr_string, 75, $this->_newline . ' ')); } else { $attr_string = Horde_String::wordwrap($attr_string, 75, $this->_newline . ' ', true, true); } } $result .= $attr_string . $this->_newline; } } $tzs = array(); foreach ($this->_components as $component) { if (!$component instanceof Horde_Icalendar_Vtimezone || !isset($tzs[$component->getAttribute('TZID')])) { $result .= $component->exportvCalendar(); if ($component instanceof Horde_Icalendar_Vtimezone) { $tzs[$component->getAttribute('TZID')] = true; } } } return $result . 'END:' . $base . $this->_newline; }