/** * Render the part based on the view mode. * * @param boolean $inline True if viewing inline. * * @return array See parent::render(). */ protected function _IMPrender($inline) { /* RFC 1740 [4]: There are two parts to an appledouble message: * (1) application/applefile * (2) Data embedded in the Mac file * Since the resource fork is not very useful to us, only provide a * means to download. */ /* Display the resource fork download link. */ $mime_id = $this->_mimepart->getMimeId(); $parts_list = array_keys($this->_mimepart->contentTypeMap()); reset($parts_list); $applefile_id = next($parts_list); $data_id = Horde_Mime::mimeIdArithmetic($applefile_id, 'next'); $applefile_part = $this->_mimepart->getPart($applefile_id); $data_part = $this->_mimepart->getPart($data_id); $data_name = $this->getConfigParam('imp_contents')->getPartName($data_part); $status = new IMP_Mime_Status(array(sprintf(_("This message contains a Macintosh file (named \"%s\")."), $data_name), sprintf(_("The Macintosh resource fork can be downloaded %s."), $this->getConfigParam('imp_contents')->linkViewJS($applefile_part, 'download_attach', _("HERE"), array('jstext' => _("The Macintosh resource fork")))))); $status->icon('mime/apple.png', _("Macintosh File")); /* For inline viewing, attempt to display the data inline. */ $ret = array(); if ($inline && ($disp = $this->getConfigParam('imp_contents')->canDisplay($data_part, IMP_Contents::RENDER_INLINE | IMP_Contents::RENDER_INFO))) { $ret = $this->getConfigParam('imp_contents')->renderMIMEPart($data_id, $disp); } foreach ($parts_list as $val) { if (!isset($ret[$val]) && strcmp($val, $data_id) !== 0) { $ret[$val] = strcmp($val, $mime_id) === 0 ? array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap') : null; } } return $ret; }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $mdn_id = $this->_mimepart->getMimeId(); $parts = array_keys($this->_mimepart->contentTypeMap()); $status = new IMP_Mime_Status(_("A message you have sent has resulted in a return notification from the recipient.")); $status->icon('info_icon.png', _("Info")); /* RFC 3798 [3]: There are three parts to a delivery status * multipart/report message: * (1) Human readable message * (2) Machine parsable body part (message/disposition-notification) * (3) Original message (optional) */ /* Get the human readable message. */ reset($parts); $part1_id = next($parts); /* Display a link to more detailed message. */ $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $part = $this->getConfigParam('imp_contents')->getMIMEPart($part2_id); if ($part) { $status->addText(sprintf(_("Technical details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/plain', 'mode' => IMP_Contents::RENDER_FULL))))); } $ret[$part2_id] = null; /* Display a link to the sent message. */ $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); $part = $this->getConfigParam('imp_contents')->getMIMEPart($part3_id); if ($part) { $status->addText(sprintf(_("The text of the sent message can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("The text of the sent message"), 'params' => array('ctype' => 'message/rfc822', 'mode' => IMP_Contents::RENDER_FULL))))); foreach (array_keys($part->contentTypeMap()) as $key) { $ret[$key] = null; } } $ret[$mdn_id] = array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'); return $ret; }
/** * Constructor. * * @param string $str The subject string. * @param array $opts Additional options: * - keepblob: (boolean) Don't remove any "blob" information (i.e. text * leading text between square brackets) from string. * * @return string The cleaned up subject string. */ public function __construct($str, array $opts = array()) { // Rule 1a: MIME decode. $str = Horde_Mime::decode($str); // Rule 1b: Remove superfluous whitespace. $str = preg_replace("/[\t\r\n ]+/", ' ', $str); do { /* (2) Remove all trailing text of the subject that matches the * the subj-trailer ABNF, repeat until no more matches are * possible. */ $str = preg_replace("/(?:\\s*\\(fwd\\)\\s*)+\$/i", '', $str); do { /* (3) Remove all prefix text of the subject that matches the * subj-leader ABNF. */ $found = $this->_removeSubjLeader($str, !empty($opts['keepblob'])); /* (4) If there is prefix text of the subject that matches * the subj-blob ABNF, and removing that prefix leaves a * non-empty subj-base, then remove the prefix text. */ $found = empty($opts['keepblob']) && $this->_removeBlobWhenNonempty($str) || $found; /* (5) Repeat (3) and (4) until no matches remain. */ } while ($found); /* (6) If the resulting text begins with the subj-fwd-hdr ABNF and * ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and * subj-fwd-trl and repeat from step (2). */ } while ($this->_removeSubjFwdHdr($str)); $this->_subject = strval($str); }
/** */ protected function _setValue($value) { if ($value instanceof Horde_Mime_Headers_Element) { $value = $value->value; } elseif (is_array($value)) { $value = reset($value); } $this->_values = array($this->_sanityCheck(Horde_Mime::decode($value))); }
/** */ protected function _setValue($value) { if ($value instanceof Horde_Mime_Headers_Element) { $value = $value->value; } foreach (is_array($value) ? $value : array($value) as $val) { $this->_values[] = $this->_sanityCheck(Horde_Mime::decode($val)); } }
/** * @param mixed $value Either a single language or an array of languages. */ protected function _setValue($value) { if ($value instanceof Horde_Mime_Headers_Element) { $value = $value->value; } if (!is_array($value)) { $value = array_map('trim', explode(',', $value)); } $this->_values = array(); foreach ($value as $val) { $this->_values[] = Horde_String::lower($this->_sanityCheck(Horde_Mime::decode($val))); } }
public function testGenerate() { $h = new Horde_Mime_Headers(); $ob = new Horde_Mime_Mdn($h); try { $ob->generate(true, true, 'deleted', 'foo', null); $this->fail('Expected Exception'); } catch (RuntimeException $e) { } $date = 'Tue, 18 Nov 2014 20:14:17 -0700'; $mdn_addr = 'Aäb <*****@*****.**>'; $h->addHeader('Date', $date); $h->addHeader('Subject', 'Test'); $h->addHeader('To', '"BAR" <*****@*****.**>'); $ob->addMdnRequestHeaders($mdn_addr); $mailer = new Horde_Mail_Transport_Mock(); $ob->generate(true, true, 'displayed', 'test.example.com', $mailer, array('from_addr' => '*****@*****.**'), array('error'), array('error' => 'Foo')); $sent = str_replace("\r\n", "\n", $mailer->sentMessages[0]); $this->assertEquals('auto-replied', $sent['headers']['Auto-Submitted']); $this->assertEquals('*****@*****.**', $sent['headers']['From']); $this->assertEquals($mdn_addr, Horde_Mime::decode($sent['headers']['To'])); $this->assertEquals('Disposition Notification', $sent['headers']['Subject']); $this->assertStringMatchesFormat('This message is in MIME format. --=%s Content-Type: text/plain; format=flowed; DelSp=Yes The message sent on Tue, 18 Nov 2014 20:14:17 -0700 to BAR <*****@*****.**> with subject "Test" has been displayed. This is no guarantee that the message has been read or understood. --=%s Content-Type: message/disposition-notification Reporting-UA: test.example.com; Horde Application Framework 5 Final-Recipient: rfc822;bar@example.com Disposition: manual-action/MDN-sent-manually; displayed/error Error: Foo --=%s Content-Type: message/rfc822 Date: Tue, 18 Nov 2014 20:14:17 -0700 Subject: Test To: BAR <*****@*****.**> Disposition-Notification-To: =?utf-8?b?QcOkYg==?= <*****@*****.**> --=%s ', $sent['body']); }
/** * Helper function to parse a parameters-like tokenized array. * * @param array $data The tokenized data. * @param string $type The header name. * * @return array The parameter array. */ protected function _parseStructureParams($data, $type) { $params = array(); if (is_array($data)) { for ($i = 0, $cnt = count($data); $i < $cnt; ++$i) { $params[Horde_String::lower($data[$i])] = $data[++$i]; } } $ret = Horde_Mime::decodeParam($type, $params); return $ret['params']; }
/** * 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; } }
/** * Scan text for UUencode data an, if it exists, convert the part to the * embedded MIME representation. * * @return mixed See self::_getEmbeddedMimeParts(). */ protected function _parseUUencode() { $text = Horde_String::convertCharset($this->_mimepart->getContents(), $this->_mimepart->getCharset(), 'UTF-8'); $files = Horde_Mime::uudecode($text); if (empty($files)) { return null; } $new_part = new Horde_Mime_Part(); $new_part->setType('multipart/mixed'); $text_part = new Horde_Mime_Part(); $text_part->setType('text/plain'); $text_part->setCharset($this->getConfigParam('charset')); $text_part->setContents(preg_replace("/begin [0-7]{3} .+\r?\n.+\r?\nend/Us", "\n", $text)); $new_part->addPart($text_part); reset($files); while (list(, $file) = each($files)) { $uupart = new Horde_Mime_Part(); $uupart->setType('application/octet-stream'); $uupart->setContents($file['data']); $uupart->setName(strip_tags($file['name'])); $new_part->addPart($uupart); } return $new_part; }
/** * Parse the output from imap_fetchstructure() into a MIME Part object. * * @param object $data Data from imap_fetchstructure(). * * @return Horde_Mime_Part A MIME Part object. */ protected function _parseStructure($data) { $ob = new Horde_Mime_Part(); $ob->setType(Horde_String::lower($data->type) . '/' . Horde_String::lower($data->subType)); // Optional for multipart-parts, required for all others if (isset($data->parameters)) { $params = array(); foreach ($data->parameters as $key => $value) { $params[Horde_String::lower($key)] = $value; } $params = Horde_Mime::decodeParam('content-type', $params); foreach ($params['params'] as $key => $value) { $ob->setContentTypeParameter($key, $value); } } // Optional entries. 'location' and 'language' not supported if (isset($data->disposition)) { $ob->setDisposition($data->disposition); if (isset($data->dparameters)) { $dparams = array(); foreach ($data->dparameters as $key => $value) { $dparams[Horde_String::lower($key)] = $value; } $dparams = Horde_Mime::decodeParam('content-disposition', $dparams); foreach ($dparams['params'] as $key => $value) { $ob->setDispositionParameter($key, $value); } } } if ($ob->getPrimaryType() == 'multipart') { // multipart/* specific entries foreach ($data->subParts as $val) { $ob->addPart($this->_parseStructure($val)); } } else { // Required options if (isset($data->partID)) { $ob->setContentId($data->partID); } $ob->setTransferEncoding(Horde_String::lower($data->encoding)); $ob->setBytes($data->bytes); if ($ob->getType() == 'message/rfc822') { $ob->addPart($this->_parseStructure(reset($data->subParts))); } } return $ob; }
/** */ protected function _writeAddress($opts) { $addr = $this->addresses->writeAddress($opts); $groupname = $this->groupname; if (!empty($opts['encode'])) { $groupname = Horde_Mime::encode($groupname, $opts['encode']); } $rfc822 = new Horde_Mail_Rfc822(); return $rfc822->encode($groupname, 'personal') . ':' . (strlen($addr) ? ' ' . $addr : '') . ';'; }
/** */ protected function _writeAddress($opts) { $rfc822 = new Horde_Mail_Rfc822(); $address = $rfc822->encode($this->mailbox, 'address'); $host = empty($opts['idn']) ? $this->host : $this->host_idn; if (strlen($host)) { $address .= '@' . $host; } $personal = $this->personal; if (strlen($personal)) { if (!empty($opts['encode'])) { $personal = Horde_Mime::encode($this->personal, $opts['encode']); } if (empty($opts['noquote'])) { $personal = $rfc822->encode($personal, 'personal'); } } if (!empty($opts['comment']) && !empty($this->comment)) { foreach ($this->comment as $val) { $personal .= ' (' . $rfc822->encode($val, 'comment') . ')'; } } return strlen($personal) && $personal != $address ? ltrim($personal) . ' <' . $address . '>' : $address; }
/** * Helper function to parse a parameters-like tokenized array. * * @param mixed $data Message data. Either a Horde_Imap_Client_Tokenize * object or null. * @param string $type The header name. * * @return array The parameter array. */ protected function _parseStructureParams($data, $type) { $params = array(); if (is_null($data)) { return $params; } while (($name = $data->next()) !== false) { $params[strtolower($name)] = $data->next(); } $ret = Horde_Mime::decodeParam($type, $params); return $ret['params']; }
/** * Performs the filtering specified in the rules. * * @param integer $change The timestamp of the latest rule change during * the current session. */ protected function _perform($change) { $api = $this->_params['api']; $notification = $this->_params['notification']; /* Indices that will be ignored by subsequent rules. */ $ignore_ids = array(); /* Only do filtering if: 1. We have not done filtering before -or- 2. The mailbox has changed -or- 3. The rules have changed. */ $cache = $api->getCache(); if ($cache !== false && $cache == $change) { return; } $filters = Ingo_Storage_FilterIterator_Skip::create($this->_params['storage'], $this->_params['skip']); /* Parse through the rules, one-by-one. */ foreach ($filters as $rule) { /* Check to make sure this is a valid rule and that the rule is not disabled. */ if (!$this->_validRule($rule) || $rule->disable) { continue; } switch ($class = get_class($rule)) { case 'Ingo_Rule_System_Blacklist': case 'Ingo_Rule_System_Whitelist': $addr = $rule->addresses; $bl_folder = $class === 'Ingo_Rule_System_Blacklist' ? $rule->mailbox : null; /* If list is empty, move on. */ if (empty($addr)) { continue; } $addr = new Horde_Mail_Rfc822_List($addr); $query = $this->_getQuery(); $or_ob = new Horde_Imap_Client_Search_Query(); foreach ($addr->bare_addresses as $val) { $ob = new Horde_Imap_Client_Search_Query(); $ob->charset('UTF-8', false); $ob->headerText('from', $val); $or_ob->orSearch(array($ob)); } $query->andSearch(array($or_ob)); $indices = $api->search($query); if (!($msgs = $api->fetchEnvelope($indices))) { continue; } /* Remove any indices that got in there by way of partial * address match. */ $remove = array(); foreach ($msgs as $v) { foreach ($v->getEnvelope()->from as $v2) { if (!$addr->contains($v2)) { $remove[] = $v->getUid(); break; } } } if ($remove) { $indices = array_diff($indices, $remove); } if ($class === 'Ingo_Rule_System_Blacklist') { $indices = array_diff($indices, $ignore_ids); if (!empty($indices)) { if (!empty($bl_folder)) { $api->moveMessages($indices, $bl_folder); } else { $api->deleteMessages($indices); } $notification->push(sprintf(_("Filter activity: %s message(s) that matched the blacklist were deleted."), count($indices)), 'horde.message'); } } else { $ignore_ids = $indices; } break; case 'Ingo_Rule_User_Discard': case 'Ingo_Rule_User_Keep': case 'Ingo_Rule_User_Move': case 'Ingo_Rule_User_MoveKeep': $base_query = $this->_getQuery(); $query = new Horde_Imap_Client_Search_Query(); foreach ($rule->conditions as $val) { $ob = new Horde_Imap_Client_Search_Query(); if (!empty($val['type']) && $val['type'] == Ingo_Rule_User::TEST_SIZE) { $ob->size($val['value'], $val['match'] == 'greater than'); } elseif (!empty($val['type']) && $val['type'] == Ingo_Rule_User::TEST_BODY) { $ob->charset('UTF-8', false); $ob->text($val['value'], true, $val['match'] == 'not contain'); } else { if (strpos($val['field'], ',') == false) { $ob->charset('UTF-8', false); $ob->headerText($val['field'], $val['value'], $val['match'] == 'not contain'); } else { foreach (explode(',', $val['field']) as $header) { $hdr_ob = new Horde_Imap_Client_Search_Query(); $hdr_ob->charset('UTF-8', false); $hdr_ob->headerText($header, $val['value'], $val['match'] == 'not contain'); if ($val['match'] == 'contains') { $ob->orSearch(array($hdr_ob)); } elseif ($val['match'] == 'not contain') { $ob->andSearch(array($hdr_ob)); } } } } switch ($rule->combine) { case Ingo_Rule_User::COMBINE_ALL: $query->andSearch(array($ob)); break; case Ingo_Rule_User::COMBINE_ANY: $query->orSearch(array($ob)); break; } } $base_query->andSearch(array($query)); $indices = $api->search($base_query); if ($indices = array_diff($indices, $ignore_ids)) { if ($rule->stop) { /* If the stop action is set, add these * indices to the list of ids that will be * ignored by subsequent rules. */ $ignore_ids = array_unique($indices + $ignore_ids); } /* Set the flags. */ if ($class !== 'Ingo_Rule_User_Discard') { $flags = array(); if ($rule->flags & Ingo_Rule_User::FLAG_ANSWERED) { $flags[] = '\\answered'; } if ($rule->flags & Ingo_Rule_User::FLAG_DELETED) { $flags[] = '\\deleted'; } if ($rule->flags & Ingo_Rule_User::FLAG_FLAGGED) { $flags[] = '\\flagged'; } if ($rule->flags & Ingo_Rule_User::FLAG_SEEN) { $flags[] = '\\seen'; } if (!empty($flags)) { $api->setMessageFlags($indices, $flags); } } switch ($class) { case 'Ingo_Rule_User_Keep': /* Add these indices to the ignore list. */ $ignore_ids = array_unique($indices + $ignore_ids); break; case 'Ingo_Rule_User_Move': /* We need to grab the envelope first. */ if ($this->_params['show_filter_msg'] && !($fetch = $api->fetchEnvelope($indices))) { continue 2; } $mbox = new Horde_Imap_Client_Mailbox($rule->value); /* Move the messages to the requested mailbox. */ $api->moveMessages($indices, strval($mbox)); /* Display notification message(s). */ if ($this->_params['show_filter_msg']) { foreach ($fetch as $msg) { $envelope = $msg->getEnvelope(); $notification->push(sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been moved to the folder \"%s\"."), !empty($envelope->subject) ? Horde_Mime::decode($envelope->subject) : _("[No Subject]"), !empty($envelope->from) ? strval($envelope->from) : _("[No Sender]"), $mbox), 'horde.message'); } } else { $notification->push(sprintf(_("Filter activity: %s message(s) have been moved to the folder \"%s\"."), count($indices), $mbox), 'horde.message'); } break; case 'Ingo_Rule_User_Discard': /* We need to grab the envelope first. */ if ($this->_params['show_filter_msg'] && !($fetch = $api->fetchEnvelope($indices))) { continue; } /* Delete the messages now. */ $api->deleteMessages($indices); /* Display notification message(s). */ if ($this->_params['show_filter_msg']) { foreach ($fetch as $msg) { $envelope = $msg->getEnvelope(); $notification->push(sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been deleted."), !empty($envelope->subject) ? Horde_Mime::decode($envelope->subject) : _("[No Subject]"), !empty($envelope->from) ? strval($envelope->from) : _("[No Sender]")), 'horde.message'); } } else { $notification->push(sprintf(_("Filter activity: %s message(s) have been deleted."), count($indices)), 'horde.message'); } break; case 'Ingo_Rule_User_MoveKeep': $mbox = new Horde_Imap_Client_Mailbox($rule->value); /* Copy the messages to the requested mailbox. */ $api->copyMessages($indices, strval($mbox)); /* Display notification message(s). */ if ($this->_params['show_filter_msg']) { if (!($fetch = $api->fetchEnvelope($indices))) { continue; } foreach ($fetch as $msg) { $envelope = $msg->getEnvelope(); $notification->push(sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been copied to the folder \"%s\"."), !empty($envelope->subject) ? Horde_Mime::decode($envelope->subject) : _("[No Subject]"), !empty($envelope->from) ? strval($envelope->from) : _("[No Sender]"), $mbox), 'horde.message'); } } else { $notification->push(sprintf(_("Filter activity: %s message(s) have been copied to the folder \"%s\"."), count($indices), $mbox), 'horde.message'); } } } break; } } /* Set cache flag. */ $api->storeCache($change); }
/** * AJAX action: Delete an attachment from compose data. * * Variables used: * - atc_indices: (string) [JSON array] Attachment IDs to delete. * - imp_compose: (string) The IMP_Compose cache identifier. * - quiet: (boolean) If true, don't output notifications. * * @return array The list of attchment IDs that were deleted. */ public function deleteAttach() { global $injector, $notification; $result = array(); if (isset($this->vars->atc_indices)) { $imp_compose = $injector->getInstance('IMP_Factory_Compose')->create($this->vars->imp_compose); foreach (json_decode($this->vars->atc_indices) as $val) { if (isset($imp_compose[$val])) { if (empty($this->vars->quiet)) { $notification->push(sprintf(_("Deleted attachment \"%s\"."), Horde_Mime::decode($imp_compose[$val]->getPart()->getName(true))), 'horde.success'); } unset($imp_compose[$val]); $result[] = $val; $this->_base->queue->compose($imp_compose); } } } if (empty($result) && empty($this->vars->quiet)) { $notification->push(_("At least one attachment could not be deleted."), 'horde.error'); } return $result; }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $parts = array_keys($this->_mimepart->contentTypeMap()); /* RFC 3464 [2]: There are three parts to a delivery status * multipart/report message: * (1) Human readable message * (2) Machine parsable body part (message/delivery-status) * (3) Returned message (optional) * * Information on the message status is found in the 'Action' field * located in part #2 (RFC 3464 [2.3.3]). It can be either 'failed', * 'delayed', 'delivered', 'relayed', or 'expanded'. */ if (count($parts) < 2) { return array(); } reset($parts); $part1_id = next($parts); $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); /* Get the action first - it appears in the second part. */ $action = null; $part2 = $this->getConfigParam('imp_contents')->getMIMEPart($part2_id); foreach (explode("\n", $part2->getContents()) as $line) { if (stristr($line, 'Action:') !== false) { $action = strtolower(trim(substr($line, strpos($line, ':') + 1))); if (strpos($action, ' ') !== false) { $action = substr($action, 0, strpos($action, ' ')); } break; } } if (is_null($action)) { return array(); } /* Get the correct text strings for the action type. */ switch ($action) { case 'failed': case 'delayed': $status = new IMP_Mime_Status(array(_("ERROR: Your message could not be delivered."), sprintf(_("Technical error details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/plain', 'mode' => IMP_Contents::RENDER_FULL)))))); $status->action(IMP_Mime_Status::ERROR); $msg_link = _("The text of the returned message can be viewed %s."); $msg_link_status = _("The text of the returned message"); break; case 'delivered': case 'expanded': case 'relayed': $status = new IMP_Mime_Status(array(_("Your message was successfully delivered."), sprintf(_("Technical message details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/x-simple', 'mode' => IMP_Contents::RENDER_FULL)))))); $status->action(IMP_Mime_Status::SUCCESS); $msg_link = _("The text of the message can be viewed %s."); $msg_link_status = _("The text of the message"); break; default: return array(); } /* Display a link to the returned message, if it exists. */ $part3 = $this->getConfigParam('imp_contents')->getMIMEPart($part3_id); if ($part3) { $status->addText(sprintf($msg_link, $this->getConfigParam('imp_contents')->linkViewJS($part3, 'view_attach', _("HERE"), array('jstext' => $msg_link_status, 'params' => array('ctype' => 'message/rfc822'))))); } $ret = array_fill_keys(array_diff($parts, array($part1_id)), null); $ret[$this->_mimepart->getMimeId()] = array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'); return $ret; }
/** * Generates HTML output for 'multipart/signed' MIME parts. * * @return string The HTML output. */ protected function _outputPGPSigned() { global $conf, $injector, $prefs, $registry, $session; $partlist = array_keys($this->_mimepart->contentTypeMap()); $base_id = reset($partlist); $signed_id = next($partlist); $sig_id = Horde_Mime::mimeIdArithmetic($signed_id, 'next'); if (!$prefs->getValue('use_pgp') || empty($conf['gnupg']['path'])) { return array($sig_id => null); } $status = new IMP_Mime_Status(); $status->addText(_("The data in this part has been digitally signed via PGP.")); $status->icon('mime/encryption.png', 'PGP'); $ret = array($base_id => array('data' => '', 'nosummary' => true, 'status' => array($status), 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'), $sig_id => null); if ($prefs->getValue('pgp_verify') || $injector->getInstance('Horde_Variables')->pgp_verify_msg) { $imp_contents = $this->getConfigParam('imp_contents'); $sig_part = $imp_contents->getMIMEPart($sig_id); $status2 = new IMP_Mime_Status(); if (!$sig_part) { $status2->action(IMP_Mime_Status::ERROR); $sig_text = _("This digitally signed message is broken."); $ret[$base_id]['wrap'] = 'mimePartWrapInvalid'; } else { /* Close session, since this may be a long-running * operation. */ $session->close(); try { $imp_pgp = $injector->getInstance('IMP_Crypt_Pgp'); if ($sig_raw = $sig_part->getMetadata(Horde_Crypt_Pgp_Parse::SIG_RAW)) { $sig_result = $imp_pgp->verifySignature($sig_raw, $this->_getSender()->bare_address, null, $sig_part->getMetadata(Horde_Crypt_Pgp_Parse::SIG_CHARSET)); } else { $stream = $imp_contents->isEmbedded($signed_id) ? $this->_mimepart->getMetadata(self::PGP_SIGN_ENC) : $imp_contents->getBodyPart($signed_id, array('mimeheaders' => true, 'stream' => true))->data; rewind($stream); stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol'); stream_filter_append($stream, 'horde_eol', STREAM_FILTER_READ, array('eol' => Horde_Mime_Part::RFC_EOL)); $sig_result = $imp_pgp->verifySignature(stream_get_contents($stream), $this->_getSender()->bare_address, $sig_part->getContents()); } $status2->action(IMP_Mime_Status::SUCCESS); $sig_text = $sig_result->message; $ret[$base_id]['wrap'] = 'mimePartWrapValid'; } catch (Horde_Exception $e) { $status2->action(IMP_Mime_Status::ERROR); $sig_text = $e->getMessage(); $ret[$base_id]['wrap'] = 'mimePartWrapInvalid'; } } $status2->addText($this->_textFilter($sig_text, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::NOHTML))); $ret[$base_id]['status'][] = $status2; } else { switch ($registry->getView()) { case Horde_Registry::VIEW_BASIC: $status->addText(Horde::link(Horde::selfUrlParams()->add(array('pgp_verify_msg' => 1))) . _("Click HERE to verify the message.") . '</a>'); break; case Horde_Registry::VIEW_DYNAMIC: $status->addText(Horde::link('#', '', 'pgpVerifyMsg') . _("Click HERE to verify the message.") . '</a>'); break; } } return $ret; }
/** * Unserialize data. * * @param mixed $data The data to be unserialized. * @param mixed $mode The mode of unserialization. Can be either a * single mode or array of modes. If array, will be * unserialized in the order provided. * @param mixed $params Any additional parameters the unserialization * method requires. * * @return mixed Unserialized data. * @throws Horde_Serialize_Exception */ protected static function _unserialize(&$data, $mode, $params = null) { switch ($mode) { case self::NONE: break; case self::RAW: $data = rawurldecode($data); break; case self::URL: $data = urldecode($data); break; case self::WDDX: $data = wddx_deserialize($data); break; case self::BZIP: // $params['small'] = Use bzip2 'small memory' mode? $data = bzdecompress($data, isset($params['small']) ? $params['small'] : false); break; case self::IMAP8: $data = quoted_printable_decode($data); break; case self::IMAPUTF7: $data = Horde_String::convertCharset(Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($data), 'UTF-8', 'ISO-8859-1'); break; case self::IMAPUTF8: $data = Horde_Mime::encode($data); break; case self::BASIC: $data2 = @unserialize($data); // Unserialize can return false both on error and if $data is the // false value. if ($data2 === false && $data == serialize(false)) { return $data2; } $data = $data2; break; case self::GZ_DEFLATE: $data = gzinflate($data); break; case self::BASE64: $data = base64_decode($data); break; case self::GZ_COMPRESS: $data = gzuncompress($data); break; // $params = Output character set // $params = Output character set case self::UTF7: $data = Horde_String::convertCharset($data, 'utf-7', $params); break; // $params = Output character set // $params = Output character set case self::UTF7_BASIC: $data = self::unserialize($data, array(self::BASIC, self::UTF7), $params); break; case self::JSON: $out = json_decode($data); if (!is_null($out) || strcasecmp($data, 'null') === 0) { return $out; } break; case self::LZF: $data = @lzf_decompress($data); break; } if ($data === false) { throw new Horde_Serialize_Exception('Unserialization failed.'); } return $data; }
/** * Return the entire part in MIME format. * * @param array $options Additional options: * - canonical: (boolean) Returns the encoded part in strict RFC 822 & * 2045 output - namely, all newlines end with the * canonical <CR><LF> sequence. * DEFAULT: false * - defserver: (string) The default server to use when creating the * header string. * DEFAULT: none * - encode: (integer) A mask of allowable encodings. * DEFAULT: self::ENCODE_7BIT * - headers: (mixed) Include the MIME headers? If true, create a new * headers object. If a Horde_Mime_Headers object, add MIME * headers to this object. If a string, use the string * verbatim. * DEFAULT: true * - id: (string) Return only this MIME ID part. * DEFAULT: Returns the base part. * - stream: (boolean) Return a stream resource. * DEFAULT: false * * @return mixed The MIME string (returned as a resource if $stream is * true). */ public function toString($options = array()) { $eol = $this->getEOL(); $isbase = true; $oldbaseptr = null; $parts = $parts_close = array(); if (isset($options['id'])) { $id = $options['id']; if (!($part = $this[$id])) { return $part; } unset($options['id']); $contents = $part->toString($options); $prev_id = Horde_Mime::mimeIdArithmetic($id, 'up', array('norfc822' => true)); $prev_part = $prev_id == $this->getMimeId() ? $this : $this[$prev_id]; if (!$prev_part) { return $contents; } $boundary = trim($this->getContentTypeParameter('boundary'), '"'); $parts = array($eol . '--' . $boundary . $eol, $contents); if (!isset($this[Horde_Mime::mimeIdArithmetic($id, 'next')])) { $parts[] = $eol . '--' . $boundary . '--' . $eol; } } else { if ($isbase = empty($options['_notbase'])) { $headers = !empty($options['headers']) ? $options['headers'] : false; if (empty($options['encode'])) { $options['encode'] = null; } if (empty($options['defserver'])) { $options['defserver'] = null; } $options['headers'] = true; $options['_notbase'] = true; } else { $headers = true; $oldbaseptr =& $options['_baseptr']; } $this->_temp['toString'] = ''; $options['_baseptr'] =& $this->_temp['toString']; /* Any information about a message is embedded in the message * contents themself. Simply output the contents of the part * directly and return. */ $ptype = $this->getPrimaryType(); if ($ptype == 'message') { $parts[] = $this->_contents; } else { if (!empty($this->_contents)) { $encoding = $this->_getTransferEncoding($options['encode']); switch ($encoding) { case '8bit': if (empty($options['_baseptr'])) { $options['_baseptr'] = '8bit'; } break; case 'binary': $options['_baseptr'] = 'binary'; break; } $parts[] = $this->_transferEncode($this->_contents, $encoding); /* If not using $this->_contents, we can close the stream * when finished. */ if ($this->_temp['transferEncodeClose']) { $parts_close[] = end($parts); } } /* Deal with multipart messages. */ if ($ptype == 'multipart') { if (empty($this->_contents)) { $parts[] = 'This message is in MIME format.' . $eol; } $boundary = trim($this->getContentTypeParameter('boundary'), '"'); /* If base part is multipart/digest, children should not * have content-type (automatically treated as * message/rfc822; RFC 2046 [5.1.5]). */ if ($this->getSubType() === 'digest') { $options['is_digest'] = true; } foreach ($this as $part) { $parts[] = $eol . '--' . $boundary . $eol; $tmp = $part->toString($options); if ($part->getEOL() != $eol) { $tmp = $this->replaceEOL($tmp, $eol, !empty($options['stream'])); } if (!empty($options['stream'])) { $parts_close[] = $tmp; } $parts[] = $tmp; } $parts[] = $eol . '--' . $boundary . '--' . $eol; } } if (is_string($headers)) { array_unshift($parts, $headers); } elseif ($headers) { $hdr_ob = $this->addMimeHeaders(array('encode' => $options['encode'], 'headers' => $headers === true ? null : $headers)); if (!$isbase && !empty($options['is_digest'])) { unset($hdr_ob['content-type']); } if (!empty($this->_temp['toString'])) { $hdr_ob->addHeader('Content-Transfer-Encoding', $this->_temp['toString']); } array_unshift($parts, $hdr_ob->toString(array('canonical' => $eol == self::RFC_EOL, 'charset' => $this->getHeaderCharset(), 'defserver' => $options['defserver']))); } } $newfp = $this->_writeStream($parts); array_map('fclose', $parts_close); if (!is_null($oldbaseptr)) { switch ($this->_temp['toString']) { case '8bit': if (empty($oldbaseptr)) { $oldbaseptr = '8bit'; } break; case 'binary': $oldbaseptr = 'binary'; break; } } if ($isbase && !empty($options['canonical'])) { return $this->replaceEOL($newfp, self::RFC_EOL, !empty($options['stream'])); } return empty($options['stream']) ? $this->_readStream($newfp) : $newfp; }
/** * Render out the currently set contents. * * @param boolean $inline Are we viewing inline? * * @return array See parent::render(). */ protected function _IMPrender($inline) { $base_id = $this->_mimepart->getMimeId(); $subparts = $this->_mimepart->contentTypeMap(); $display_ids = $ret = array(); $prefer_plain = $GLOBALS['registry']->getView() == Horde_Registry::VIEW_MINIMAL || $GLOBALS['prefs']->getValue('alternative_display') == 'text'; /* Look for a displayable part. RFC: show the LAST choice that can be * displayed inline. If an alternative is itself a multipart, the user * agent is allowed to show that alternative, an earlier alternative, * or both. If we find a multipart alternative that contains at least * one viewable part, we will display all viewable subparts of that * alternative. */ $imp_contents = $this->getConfigParam('imp_contents'); foreach ($subparts as $mime_id => $type) { $ret[$mime_id] = null; if (strcmp($base_id, $mime_id) !== 0 && $imp_contents->canDisplay($mime_id, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL) && (!$prefer_plain || $type != 'text/html' && strpos($type, 'text/') === 0)) { $display_ids[strval($mime_id)] = true; } } /* If we found no IDs, return now. */ if (empty($display_ids)) { $ret[$base_id] = array('data' => '', 'status' => new IMP_Mime_Status(_("There are no alternative parts that can be displayed inline.")), 'type' => 'text/html; charset=' . $this->getConfigParam('charset')); return $ret; } /* If the last viewable message exists in a subpart, back up to the * base multipart and display all viewable parts in that multipart. * Else, display the single part. */ end($display_ids); $curr_id = key($display_ids); while (!is_null($curr_id) && strcmp($base_id, $curr_id) !== 0) { if (isset($subparts[$curr_id])) { $disp_id = $curr_id; } $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'up'); } /* At this point, $ret contains stubs for all parts living in the base * alternative part. * Go through all subparts of displayable part and make sure all parts * are rendered. Parts not rendered will be marked as not being * handled by this viewer (Bug #9365). */ $render_part = $this->_mimepart->getPart($disp_id); $need_render = $subparts = $render_part->contentTypeMap(); foreach (array_keys($subparts) as $val) { if (isset($display_ids[$val]) && isset($need_render[$val])) { $render = $this->getConfigParam('imp_contents')->renderMIMEPart($val, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL); foreach (array_keys($render) as $id) { unset($need_render[$id]); if (!$inline) { if (!is_null($render[$id])) { return array($base_id => $render[$id]); } } else { $ret[$id] = $render[$id]; } } } } unset($need_render[$disp_id]); foreach (array_keys($need_render) as $val) { unset($ret[$val]); } return $inline ? $ret : null; }
/** */ protected function _sendEncode($opts) { return array(Horde_Mime::encode($this->value, $opts['charset'])); }
public function readForm() { global $prefs, $session; // Event owner. $targetcalendar = Horde_Util::getFormData('targetcalendar'); if (strpos($targetcalendar, '\\')) { list(, $this->creator) = explode('\\', $targetcalendar, 2); } elseif (!isset($this->_id)) { $this->creator = $GLOBALS['registry']->getAuth(); } // Basic fields. $this->title = Horde_Util::getFormData('title', $this->title); $this->description = Horde_Util::getFormData('description', $this->description); $this->location = Horde_Util::getFormData('location', $this->location); $this->timezone = Horde_Util::getFormData('timezone', $this->timezone); $this->private = (bool) Horde_Util::getFormData('private'); // URL. $url = Horde_Util::getFormData('eventurl', $this->url); if (strlen($url)) { // Analyze and re-construct. $url = @parse_url($url); if ($url) { if (function_exists('http_build_url')) { if (empty($url['path'])) { $url['path'] = '/'; } $url = http_build_url($url); } else { $new_url = ''; if (isset($url['scheme'])) { $new_url .= $url['scheme'] . '://'; } if (isset($url['user'])) { $new_url .= $url['user']; if (isset($url['pass'])) { $new_url .= ':' . $url['pass']; } $new_url .= '@'; } if (isset($url['host'])) { // Convert IDN hosts to ASCII. if (function_exists('idn_to_ascii')) { $url['host'] = @idn_to_ascii($url['host']); } elseif (Horde_Mime::is8bit($url['host'])) { //throw new Kronolith_Exception(_("Invalid character in URL.")); $url['host'] = ''; } $new_url .= $url['host']; } if (isset($url['path'])) { $new_url .= $url['path']; } if (isset($url['query'])) { $new_url .= '?' . $url['query']; } if (isset($url['fragment'])) { $new_url .= '#' . $url['fragment']; } $url = $new_url; } } } $this->url = $url; // Status. $this->status = Horde_Util::getFormData('status', $this->status); // Attendees. $attendees = $session->get('kronolith', 'attendees', Horde_Session::TYPE_ARRAY); if (!is_null($newattendees = Horde_Util::getFormData('attendees'))) { $newattendees = Kronolith::parseAttendees(trim($newattendees)); foreach ($newattendees as $email => $attendee) { if (!isset($attendees[$email])) { $attendees[$email] = $attendee; } } foreach (array_keys($attendees) as $email) { if (!isset($newattendees[$email])) { unset($attendees[$email]); } } } $this->attendees = $attendees; // Event start. $allDay = Horde_Util::getFormData('whole_day'); if ($start_date = Horde_Util::getFormData('start_date')) { // From ajax interface. $this->start = Kronolith::parseDate($start_date . ' ' . Horde_Util::getFormData('start_time'), true, $this->timezone); if ($allDay) { $this->start->hour = $this->start->min = $this->start->sec = 0; } } else { // From traditional interface. $start = Horde_Util::getFormData('start'); $start_year = $start['year']; $start_month = $start['month']; $start_day = $start['day']; $start_hour = Horde_Util::getFormData('start_hour'); $start_min = Horde_Util::getFormData('start_min'); $am_pm = Horde_Util::getFormData('am_pm'); if (!$prefs->getValue('twentyFour')) { if ($am_pm == 'PM') { if ($start_hour != 12) { $start_hour += 12; } } elseif ($start_hour == 12) { $start_hour = 0; } } if (Horde_Util::getFormData('end_or_dur') == 1) { if ($allDay) { $start_hour = 0; $start_min = 0; $dur_day = 0; $dur_hour = 24; $dur_min = 0; } else { $dur_day = (int) Horde_Util::getFormData('dur_day'); $dur_hour = (int) Horde_Util::getFormData('dur_hour'); $dur_min = (int) Horde_Util::getFormData('dur_min'); } } $this->start = new Horde_Date(array('hour' => $start_hour, 'min' => $start_min, 'month' => $start_month, 'mday' => $start_day, 'year' => $start_year), $this->timezone); } // Event end. if ($end_date = Horde_Util::getFormData('end_date')) { // From ajax interface. $this->end = Kronolith::parseDate($end_date . ' ' . Horde_Util::getFormData('end_time'), true, $this->timezone); if ($allDay) { $this->end->hour = $this->end->min = $this->end->sec = 0; $this->end->mday++; } } elseif (Horde_Util::getFormData('end_or_dur') == 1) { // Event duration from traditional interface. $this->end = new Horde_Date(array('hour' => $start_hour + $dur_hour, 'min' => $start_min + $dur_min, 'month' => $start_month, 'mday' => $start_day + $dur_day, 'year' => $start_year)); } else { // From traditional interface. $end = Horde_Util::getFormData('end'); $end_year = $end['year']; $end_month = $end['month']; $end_day = $end['day']; $end_hour = Horde_Util::getFormData('end_hour'); $end_min = Horde_Util::getFormData('end_min'); $end_am_pm = Horde_Util::getFormData('end_am_pm'); if (!$prefs->getValue('twentyFour')) { if ($end_am_pm == 'PM') { if ($end_hour != 12) { $end_hour += 12; } } elseif ($end_hour == 12) { $end_hour = 0; } } $this->end = new Horde_Date(array('hour' => $end_hour, 'min' => $end_min, 'month' => $end_month, 'mday' => $end_day, 'year' => $end_year), $this->timezone); if ($this->end->compareDateTime($this->start) < 0) { $this->end = new Horde_Date($this->start); } } $this->allday = false; // Alarm. if (!is_null($alarm = Horde_Util::getFormData('alarm'))) { if ($alarm) { $value = Horde_Util::getFormData('alarm_value'); $unit = Horde_Util::getFormData('alarm_unit'); if ($value == 0) { $value = $unit = 1; } $this->alarm = $value * $unit; // Notification. if (Horde_Util::getFormData('alarm_change_method')) { $types = Horde_Util::getFormData('event_alarms'); $methods = array(); if (!empty($types)) { foreach ($types as $type) { $methods[$type] = array(); switch ($type) { case 'notify': $methods[$type]['sound'] = Horde_Util::getFormData('event_alarms_sound'); break; case 'mail': $methods[$type]['email'] = Horde_Util::getFormData('event_alarms_email'); break; case 'popup': break; } } } $this->methods = $methods; } else { $this->methods = array(); } } else { $this->alarm = 0; $this->methods = array(); } } // Recurrence. $this->recurrence = $this->readRecurrenceForm($this->start, $this->timezone, $this->recurrence); // Convert to local timezone. $this->setTimezone(false); // Resources $existingResources = $this->_resources; if (Horde_Util::getFormData('isajax', false)) { $resources = array(); } else { $resources = $session->get('kronolith', 'resources', Horde_Session::TYPE_ARRAY); } $newresources = Horde_Util::getFormData('resources'); if (!empty($newresources)) { foreach (explode(',', $newresources) as $id) { try { $resource = Kronolith::getDriver('Resource')->getResource($id); } catch (Kronolith_Exception $e) { $GLOBALS['notification']->push($e->getMessage(), 'horde.error'); continue; } if (!$resource instanceof Kronolith_Resource_Group || $resource->isFree($this)) { $resources[$resource->getId()] = array('attendance' => Kronolith::PART_REQUIRED, 'response' => Kronolith::RESPONSE_NONE, 'name' => $resource->get('name')); } else { $GLOBALS['notification']->push(_("No resources from this group were available"), 'horde.error'); } } } $this->_resources = $resources; // Check if we need to remove any resources $merged = $existingResources + $this->_resources; $delete = array_diff(array_keys($existingResources), array_keys($this->_resources)); foreach ($delete as $key) { // Resource might be declined, in which case it won't have the event // on it's calendar. if ($merged[$key]['response'] != Kronolith::RESPONSE_DECLINED) { try { Kronolith::getDriver('Resource')->getResource($key)->removeEvent($this); } catch (Kronolith_Exception $e) { $GLOBALS['notification']->push('foo', 'horde.error'); } } } // Tags. $this->tags = Horde_Util::getFormData('tags', $this->tags); // Geolocation if (Horde_Util::getFormData('lat') && Horde_Util::getFormData('lon')) { $this->geoLocation = array('lat' => Horde_Util::getFormData('lat'), 'lon' => Horde_Util::getFormData('lon'), 'zoom' => Horde_Util::getFormData('zoom')); } $this->initialized = true; }
/** * Exports a given Turba_Object as an iCalendar vCard. * * @param Turba_Object $object Turba_Object. * @param string $version The vcard version to produce. * @param array $fields Hash of field names and * Horde_SyncMl_Property properties with the * requested fields. * @param boolean $skipEmpty Whether to skip empty fields. * * @return Horde_Icalendar_Vcard A vcard object. */ public function tovCard(Turba_Object $object, $version = '2.1', array $fields = null, $skipEmpty = false) { global $injector; $hash = $object->getAttributes(); $attributes = array_keys($this->map); $vcard = new Horde_Icalendar_Vcard($version); $formattedname = false; $charset = $version == '2.1' ? array('CHARSET' => 'UTF-8') : array(); $hooks = $injector->getInstance('Horde_Core_Hooks'); $decode_hook = $hooks->hookExists('decode_attribute', 'turba'); // Tags are stored externally to Turba, so they don't appear in the // source map. $attributes[] = '__tags'; foreach ($attributes as $key) { $val = $object->getValue($key); if ($skipEmpty && !is_array($val) && !strlen($val)) { continue; } if ($decode_hook) { try { $val = $hooks->callHook('decode_attribute', 'turba', array($key, $val, $object)); } catch (Turba_Exception $e) { } } switch ($key) { case '__uid': $vcard->setAttribute('UID', $val); break; case 'name': if ($fields && !isset($fields['FN'])) { break; } $vcard->setAttribute('FN', $val, Horde_Mime::is8bit($val) ? $charset : array()); $formattedname = true; break; case 'nickname': case 'alias': $params = Horde_Mime::is8bit($val) ? $charset : array(); if (!$fields || isset($fields['NICKNAME'])) { $vcard->setAttribute('NICKNAME', $val, $params); } if (!$fields || isset($fields['X-EPOCSECONDNAME'])) { $vcard->setAttribute('X-EPOCSECONDNAME', $val, $params); } break; case 'homeAddress': if ($fields && (!isset($fields['LABEL']) || isset($fields['LABEL']->Params['TYPE']) && !$this->_hasValEnum($fields['LABEL']->Params['TYPE']->ValEnum, 'HOME'))) { break; } if ($version == '2.1') { $vcard->setAttribute('LABEL', $val, array('HOME' => null)); } else { $vcard->setAttribute('LABEL', $val, array('TYPE' => 'HOME')); } break; case 'workAddress': if ($fields && (!isset($fields['LABEL']) || isset($fields['LABEL']->Params['TYPE']) && !$this->_hasValEnum($fields['LABEL']->Params['TYPE']->ValEnum, 'WORK'))) { break; } if ($version == '2.1') { $vcard->setAttribute('LABEL', $val, array('WORK' => null)); } else { $vcard->setAttribute('LABEL', $val, array('TYPE' => 'WORK')); } break; case 'otherAddress': if ($fields && !isset($fields['LABEL'])) { break; } $vcard->setAttribute('LABEL', $val); break; case 'phone': if ($fields && !isset($fields['TEL'])) { break; } $vcard->setAttribute('TEL', $val); break; case 'homePhone': if ($fields && (!isset($fields['TEL']) || isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME'))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('HOME' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('HOME', 'VOICE'))); } break; case 'workPhone': if ($fields && (!isset($fields['TEL']) || isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK'))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('WORK' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('WORK', 'VOICE'))); } break; case 'cellPhone': if ($fields && (!isset($fields['TEL']) || isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL'))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('CELL' => null, 'VOICE' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('CELL', 'VOICE'))); } break; case 'homeCellPhone': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')) { if ($version == '2.1') { $parameters['CELL'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('CELL', 'VOICE'); } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('HOME', 'VOICE'); } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('CELL' => null, 'HOME' => null, 'VOICE' => null); } else { $parameters = array('TYPE' => array('CELL', 'HOME', 'VOICE')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workCellPhone': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'CELL')) { if ($version == '2.1') { $parameters['CELL'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('CELL', 'VOICE'); } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; $parameters['VOICE'] = null; } else { $parameters['TYPE'] = array('WORK', 'VOICE'); } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('CELL' => null, 'WORK' => null, 'VOICE' => null); } else { $parameters = array('TYPE' => array('CELL', 'WORK', 'VOICE')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'videoCall': if ($fields && (!isset($fields['TEL']) || isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO'))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('VIDEO' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'VIDEO')); } break; case 'homeVideoCall': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')) { if ($version == '2.1') { $parameters['VIDEO'] = null; } else { $parameters['TYPE'] = 'VIDEO'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; } else { $parameters['TYPE'] = 'HOME'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('VIDEO' => null, 'HOME' => null); } else { $parameters = array('TYPE' => array('VIDEO', 'HOME')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workVideoCall': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'VIDEO')) { if ($version == '2.1') { $parameters['VIDEO'] = null; } else { $parameters['TYPE'] = 'VIDEO'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; } else { $parameters['TYPE'] = 'WORK'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('VIDEO' => null, 'WORK' => null); } else { $parameters = array('TYPE' => array('VIDEO', 'WORK')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'sip': if ($fields && !isset($fields['X-SIP'])) { break; } $vcard->setAttribute('X-SIP', $val); break; case 'ptt': if ($fields && (!isset($fields['X-SIP']) || isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'POC'))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('POC' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'POC')); } break; case 'voip': if ($fields && (!isset($fields['X-SIP']) || isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'VOIP'))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('VOIP' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'VOIP')); } break; case 'shareView': if ($fields && (!isset($fields['X-SIP']) || isset($fields['X-SIP']->Params['TYPE']) && !$this->_hasValEnum($fields['X-SIP']->Params['TYPE']->ValEnum, 'SWIS'))) { break; } if ($version == '2.1') { $vcard->setAttribute('X-SIP', $val, array('SWIS' => null)); } else { $vcard->setAttribute('X-SIP', $val, array('TYPE' => 'SWIS')); } break; case 'imaddress': if ($fields && !isset($fields['X-WV-ID'])) { break; } $vcard->setAttribute('X-WV-ID', $val); break; case 'fax': if ($fields && (!isset($fields['TEL']) || isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX'))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('FAX' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'FAX')); } break; case 'homeFax': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')) { if ($version == '2.1') { $parameters['FAX'] = null; } else { $parameters['TYPE'] = 'FAX'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'HOME')) { if ($version == '2.1') { $parameters['HOME'] = null; } else { $parameters['TYPE'] = 'HOME'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('FAX' => null, 'HOME' => null); } else { $parameters = array('TYPE' => array('FAX', 'HOME')); } } $vcard->setAttribute('TEL', $val, $parameters); break; case 'workFax': $parameters = array(); if ($fields) { if (!isset($fields['TEL'])) { break; } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'FAX')) { if ($version == '2.1') { $parameters['FAX'] = null; } else { $parameters['TYPE'] = 'FAX'; } } if (!isset($fields['TEL']->Params['TYPE']) || $this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'WORK')) { if ($version == '2.1') { $parameters['WORK'] = null; } else { $parameters['TYPE'] = 'WORK'; } } if (empty($parameters)) { break; } } else { if ($version == '2.1') { $parameters = array('FAX' => null, 'WORK' => null); } else { $parameters = array('TYPE' => array('FAX', 'WORK')); } } $vcard->setAttribute('TEL', $val, $parameters); if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('FAX' => null, 'WORK' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => array('FAX', 'WORK'))); } break; case 'pager': if ($fields && (!isset($fields['TEL']) || isset($fields['TEL']->Params['TYPE']) && !$this->_hasValEnum($fields['TEL']->Params['TYPE']->ValEnum, 'PAGER'))) { break; } if ($version == '2.1') { $vcard->setAttribute('TEL', $val, array('PAGER' => null)); } else { $vcard->setAttribute('TEL', $val, array('TYPE' => 'PAGER')); } break; case 'email': if ($fields && !isset($fields['EMAIL'])) { break; } if ($version == '2.1') { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('INTERNET' => null)); } else { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'INTERNET')); } break; case 'homeEmail': if ($fields && (!isset($fields['EMAIL']) || isset($fields['EMAIL']->Params['TYPE']) && !$this->_hasValEnum($fields['EMAIL']->Params['TYPE']->ValEnum, 'HOME'))) { break; } if ($version == '2.1') { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('HOME' => null)); } else { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'HOME')); } break; case 'workEmail': if ($fields && (!isset($fields['EMAIL']) || isset($fields['EMAIL']->Params['TYPE']) && !$this->_hasValEnum($fields['EMAIL']->Params['TYPE']->ValEnum, 'WORK'))) { break; } if ($version == '2.1') { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('WORK' => null)); } else { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($val), array('TYPE' => 'WORK')); } break; case 'emails': if ($fields && !isset($fields['EMAIL'])) { break; } $emails = explode(',', $val); foreach ($emails as $email) { $vcard->setAttribute('EMAIL', Horde_Icalendar_Vcard::getBareEmail($email)); } break; case 'title': if ($fields && !isset($fields['TITLE'])) { break; } $vcard->setAttribute('TITLE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case 'role': if ($fields && !isset($fields['ROLE'])) { break; } $vcard->setAttribute('ROLE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case 'notes': if ($fields && !isset($fields['NOTE'])) { break; } $vcard->setAttribute('NOTE', $val, Horde_Mime::is8bit($val) ? $charset : array()); break; case '__tags': $val = $injector->getInstance('Turba_Tagger')->split($val); case 'businessCategory': // No CATEGORIES in vCard 2.1 if ($version == '2.1' || $fields && !isset($fields['CATEGORIES'])) { break; } $vcard->setAttribute('CATEGORIES', null, array(), true, $val); break; case 'anniversary': if (!$fields || isset($fields['X-ANNIVERSARY'])) { $vcard->setAttribute('X-ANNIVERSARY', $val); } break; case 'spouse': if (!$fields || isset($fields['X-SPOUSE'])) { $vcard->setAttribute('X-SPOUSE', $val); } break; case 'children': if (!$fields || isset($fields['X-CHILDREN'])) { $vcard->setAttribute('X-CHILDREN', $val); } break; case 'website': if ($fields && !isset($fields['URL'])) { break; } $vcard->setAttribute('URL', $val); break; case 'homeWebsite': if ($fields && (!isset($fields['URL']) || isset($fields['URL']->Params['TYPE']) && !$this->_hasValEnum($fields['URL']->Params['TYPE']->ValEnum, 'HOME'))) { break; } if ($version == '2.1') { $vcard->setAttribute('URL', $val, array('HOME' => null)); } else { $vcard->setAttribute('URL', $val, array('TYPE' => 'HOME')); } break; case 'workWebsite': if ($fields && (!isset($fields['URL']) || isset($fields['URL']->Params['TYPE']) && !$this->_hasValEnum($fields['URL']->Params['TYPE']->ValEnum, 'WORK'))) { break; } if ($version == '2.1') { $vcard->setAttribute('URL', $val, array('WORK' => null)); } else { $vcard->setAttribute('URL', $val, array('TYPE' => 'WORK')); } break; case 'freebusyUrl': if ($version == '2.1' || $fields && !isset($fields['FBURL'])) { break; } $vcard->setAttribute('FBURL', $val); break; case 'birthday': if ($fields && !isset($fields['BDAY'])) { break; } $vcard->setAttribute('BDAY', $val); break; case 'timezone': if ($fields && !isset($fields['TZ'])) { break; } $vcard->setAttribute('TZ', $val, array('VALUE' => 'text')); break; case 'latitude': if ($fields && !isset($fields['GEO'])) { break; } if (isset($hash['longitude'])) { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['longitude'])); } break; case 'homeLatitude': if ($fields && (!isset($fields['GEO']) || isset($fields['GEO']->Params['TYPE']) && !$this->_hasValEnum($fields['GEO']->Params['TYPE']->ValEnum, 'HOME'))) { break; } if (isset($hash['homeLongitude'])) { if ($version == '2.1') { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['homeLongitude']), array('HOME' => null)); } else { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['homeLongitude']), array('TYPE' => 'HOME')); } } break; case 'workLatitude': if ($fields && (!isset($fields['GEO']) || isset($fields['GEO']->Params['TYPE']) && !$this->_hasValEnum($fields['GEO']->Params['TYPE']->ValEnum, 'HOME'))) { break; } if (isset($hash['workLongitude'])) { if ($version == '2.1') { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['workLongitude']), array('WORK' => null)); } else { $vcard->setAttribute('GEO', array('latitude' => $val, 'longitude' => $hash['workLongitude']), array('TYPE' => 'WORK')); } } break; case 'photo': case 'logo': $name = Horde_String::upper($key); $params = array(); if (strlen($hash[$key])) { $params['ENCODING'] = 'b'; } if (isset($hash[$key . 'type'])) { $params['TYPE'] = $hash[$key . 'type']; } if ($fields && (!isset($fields[$name]) || isset($params['TYPE']) && isset($fields[$name]->Params['TYPE']) && !$this->_hasValEnum($fields[$name]->Params['TYPE']->ValEnum, $params['TYPE']))) { break; } $vcard->setAttribute($name, base64_encode($hash[$key]), $params); break; } } // No explicit firstname/lastname in data source: we have to guess. if (!isset($hash['lastname']) && isset($hash['name'])) { $this->_guessName($hash); } $a = array(Horde_Icalendar_Vcard::N_FAMILY => isset($hash['lastname']) ? $hash['lastname'] : '', Horde_Icalendar_Vcard::N_GIVEN => isset($hash['firstname']) ? $hash['firstname'] : '', Horde_Icalendar_Vcard::N_ADDL => isset($hash['middlenames']) ? $hash['middlenames'] : '', Horde_Icalendar_Vcard::N_PREFIX => isset($hash['namePrefix']) ? $hash['namePrefix'] : '', Horde_Icalendar_Vcard::N_SUFFIX => isset($hash['nameSuffix']) ? $hash['nameSuffix'] : ''); $val = implode(';', $a); if (!$fields || isset($fields['N'])) { $vcard->setAttribute('N', $val, Horde_Mime::is8bit($val) ? $charset : array(), false, $a); } if (!$formattedname && (!$fields || isset($fields['FN']))) { if ($object->getValue('name')) { $val = $object->getValue('name'); } elseif (!empty($this->alternativeName) && isset($hash[$this->alternativeName])) { $val = $hash[$this->alternativeName]; } else { $val = ''; } $vcard->setAttribute('FN', $val, Horde_Mime::is8bit($val) ? $charset : array()); } $org = array(); if (!empty($hash['company']) || !$skipEmpty && array_key_exists('company', $hash)) { $org[] = $hash['company']; } if (!empty($hash['department']) || !$skipEmpty && array_key_exists('department', $hash)) { $org[] = $hash['department']; } if (count($org) && (!$fields || isset($fields['ORG']))) { $val = implode(';', $org); $vcard->setAttribute('ORG', $val, Horde_Mime::is8bit($val) ? $charset : array(), false, $org); } if ((!$fields || isset($fields['ADR'])) && (!empty($hash['commonAddress']) || !empty($hash['commonStreet']) || !empty($hash['commonPOBox']) || !empty($hash['commonExtended']) || !empty($hash['commonCity']) || !empty($hash['commonProvince']) || !empty($hash['commonPostalCode']) || !empty($hash['commonCountry']) || !$skipEmpty && (array_key_exists('commonAddress', $hash) || array_key_exists('commonStreet', $hash) || array_key_exists('commonPOBox', $hash) || array_key_exists('commonExtended', $hash) || array_key_exists('commonCity', $hash) || array_key_exists('commonProvince', $hash) || array_key_exists('commonPostalCode', $hash) || array_key_exists('commonCountry', $hash)))) { /* We can't know if this particular Turba source uses a single * address field or multiple for * street/city/province/postcode/country. Try to deal with * both. */ if (isset($hash['commonAddress']) && !isset($hash['commonStreet'])) { $hash['commonStreet'] = $hash['commonAddress']; } $a = array(Horde_Icalendar_Vcard::ADR_POB => isset($hash['commonPOBox']) ? $hash['commonPOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['commonExtended']) ? $hash['commonExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['commonStreet']) ? $hash['commonStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['commonCity']) ? $hash['commonCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['commonProvince']) ? $hash['commonProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['commonPostalCode']) ? $hash['commonPostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['commonCountry']) ? Horde_Nls::getCountryISO($hash['commonCountry']) : ''); $val = implode(';', $a); if ($version == '2.1') { $params = array(); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => ''); } $vcard->setAttribute('ADR', $val, $params, true, $a); } if ((!$fields || isset($fields['ADR']) && (!isset($fields['ADR']->Params['TYPE']) || $this->_hasValEnum($fields['ADR']->Params['TYPE']->ValEnum, 'HOME'))) && (!empty($hash['homeAddress']) || !empty($hash['homeStreet']) || !empty($hash['homePOBox']) || !empty($hash['homeExtended']) || !empty($hash['homeCity']) || !empty($hash['homeProvince']) || !empty($hash['homePostalCode']) || !empty($hash['homeCountry']) || !$skipEmpty && (array_key_exists('homeAddress', $hash) || array_key_exists('homeStreet', $hash) || array_key_exists('homePOBox', $hash) || array_key_exists('homeExtended', $hash) || array_key_exists('homeCity', $hash) || array_key_exists('homeProvince', $hash) || array_key_exists('homePostalCode', $hash) || array_key_exists('homeCountry', $hash)))) { if (isset($hash['homeAddress']) && !isset($hash['homeStreet'])) { $hash['homeStreet'] = $hash['homeAddress']; } $a = array(Horde_Icalendar_Vcard::ADR_POB => isset($hash['homePOBox']) ? $hash['homePOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['homeExtended']) ? $hash['homeExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['homeStreet']) ? $hash['homeStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['homeCity']) ? $hash['homeCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['homeProvince']) ? $hash['homeProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['homePostalCode']) ? $hash['homePostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['homeCountry']) ? Horde_Nls::getCountryISO($hash['homeCountry']) : ''); $val = implode(';', $a); if ($version == '2.1') { $params = array('HOME' => null); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => 'HOME'); } $vcard->setAttribute('ADR', $val, $params, true, $a); } if ((!$fields || isset($fields['ADR']) && (!isset($fields['ADR']->Params['TYPE']) || $this->_hasValEnum($fields['ADR']->Params['TYPE']->ValEnum, 'WORK'))) && (!empty($hash['workAddress']) || !empty($hash['workStreet']) || !empty($hash['workPOBox']) || !empty($hash['workExtended']) || !empty($hash['workCity']) || !empty($hash['workProvince']) || !empty($hash['workPostalCode']) || !empty($hash['workCountry']) || !$skipEmpty && (array_key_exists('workAddress', $hash) || array_key_exists('workStreet', $hash) || array_key_exists('workPOBox', $hash) || array_key_exists('workExtended', $hash) || array_key_exists('workCity', $hash) || array_key_exists('workProvince', $hash) || array_key_exists('workPostalCode', $hash) || array_key_exists('workCountry', $hash)))) { if (isset($hash['workAddress']) && !isset($hash['workStreet'])) { $hash['workStreet'] = $hash['workAddress']; } $a = array(Horde_Icalendar_Vcard::ADR_POB => isset($hash['workPOBox']) ? $hash['workPOBox'] : '', Horde_Icalendar_Vcard::ADR_EXTEND => isset($hash['workExtended']) ? $hash['workExtended'] : '', Horde_Icalendar_Vcard::ADR_STREET => isset($hash['workStreet']) ? $hash['workStreet'] : '', Horde_Icalendar_Vcard::ADR_LOCALITY => isset($hash['workCity']) ? $hash['workCity'] : '', Horde_Icalendar_Vcard::ADR_REGION => isset($hash['workProvince']) ? $hash['workProvince'] : '', Horde_Icalendar_Vcard::ADR_POSTCODE => isset($hash['workPostalCode']) ? $hash['workPostalCode'] : '', Horde_Icalendar_Vcard::ADR_COUNTRY => isset($hash['workCountry']) ? Horde_Nls::getCountryISO($hash['workCountry']) : ''); $val = implode(';', $a); if ($version == '2.1') { $params = array('WORK' => null); if (Horde_Mime::is8bit($val)) { $params['CHARSET'] = 'UTF-8'; } } else { $params = array('TYPE' => 'WORK'); } $vcard->setAttribute('ADR', $val, $params, true, $a); } return $vcard; }
/** * Builds a Horde_Mime_Headers object from header text. * This function can be called statically: * $headers = Horde_Mime_Headers::parseHeaders(). * * @param string $text A text string containing the headers. * * @return Horde_Mime_Headers A new Horde_Mime_Headers object. */ public static function parseHeaders($text) { $currheader = $currtext = null; $mime = self::mimeParamFields(); $to_process = array(); foreach (explode("\n", $text) as $val) { $val = rtrim($val); if (empty($val)) { break; } if ($val[0] == ' ' || $val[0] == "\t") { $currtext .= ' ' . ltrim($val); } else { if (!is_null($currheader)) { $to_process[] = array($currheader, $currtext); } $pos = strpos($val, ':'); $currheader = substr($val, 0, $pos); $currtext = ltrim(substr($val, $pos + 1)); } } if (!is_null($currheader)) { $to_process[] = array($currheader, $currtext); } $headers = new Horde_Mime_Headers(); reset($to_process); while (list(, $val) = each($to_process)) { /* Ignore empty headers. */ if (!strlen($val[1])) { continue; } if (in_array(Horde_String::lower($val[0]), $mime)) { $res = Horde_Mime::decodeParam($val[0], $val[1]); $headers->addHeader($val[0], $res['val'], array('params' => $res['params'], 'sanity_check' => true)); } else { $headers->addHeader($val[0], $val[1], array('sanity_check' => true)); } } return $headers; }
/** * Decodes a MIME content parameter string pursuant to RFC 2183 & 2231 * (Content-Type and Content-Disposition headers). * * Stores value/parameter data in the current object. * * @param mixed $data Parameter data. Either an array or a string. */ public function decode($data) { $add = $convert = array(); if (is_array($data)) { $params = $data; } else { $parts = explode(';', $data, 2); if (isset($parts[0]) && strpos($parts[0], '=') === false) { $this->setContentParamValue($parts[0]); $param = isset($parts[1]) ? $parts[1] : null; } else { $param = $data; } if (empty($param)) { $params = array(); } else { $decode = new Horde_Mime_ContentParam_Decode(); $params = $decode->decode($param); } } $to_add = array(); foreach ($params as $name => $val) { /* Asterisk at end indicates encoded value. */ if (substr($name, -1) == '*') { $name = substr($name, 0, -1); $encoded = true; } else { $encoded = false; } /* This asterisk indicates continuation parameter. */ if (($pos = strrpos($name, '*')) !== false && is_numeric($order = substr($name, $pos + 1))) { $name = substr($name, 0, $pos); $to_add[Horde_String::lower($name)][$order] = $val; } else { $to_add[$name] = array($val); } if ($encoded) { $convert[$name] = true; } } foreach ($to_add as $key => $val) { ksort($val); $add[$key] = implode('', $val); } foreach (array_keys($convert) as $name) { $val = $add[$name]; $quote = strpos($val, "'"); if ($quote === false) { $add[$name] = urldecode($val); } else { $orig_charset = substr($val, 0, $quote); if (Horde_String::lower($orig_charset) == 'iso-8859-1') { $orig_charset = 'windows-1252'; } /* Ignore language. */ $quote = strpos($val, "'", $quote + 1); substr($val, $quote + 1); $add[$name] = Horde_String::convertCharset(urldecode(substr($val, $quote + 1)), $orig_charset, 'UTF-8'); } } /* MIME parameters are supposed to be encoded via RFC 2231, but many * mailers do RFC 2045 encoding instead. However, if we see at least * one RFC 2231 encoding, then assume the sending mailer knew what * it was doing and didn't send any parameters RFC 2045 encoded. */ if (empty($convert)) { foreach ($add as $key => $val) { $add[$key] = Horde_Mime::decode($val); } } if (count($add)) { foreach ($add as $key => $val) { /* When parsing a content-param string, lowercase all * parameter names to normalize. Only maintain case of * parameters explicitly added by calling code. */ $this[Horde_String::lower($key)] = $val; } } elseif (is_string($data)) { $this->setContentParamValue($parts[0]); } }
/** * Builds a proper AS mail message object. * * @param Horde_Imap_Client_Mailbox $mbox The IMAP mailbox. * @param Horde_Imap_Client_Data_Fetch $data The fetch results. * @param array $options Additional Options: * - truncation: (integer) Truncate the message body to this length. * DEFAULT: No truncation. * - bodyprefs: (array) Bodyprefs, if sent from device. * DEFAULT: none (No body prefs sent or enforced). * - bodypartprefs: (array) Bodypartprefs, if sent from device. * DEFAULT: none (No body part prefs sent or enforced). * - mimesupport: (integer) Indicates if MIME is supported or not. * Possible values: 0 - Not supported 1 - Only S/MIME or * 2 - All MIME. * DEFAULT: 0 (No MIME support) * - protocolversion: (float) The EAS protocol version to support. * DEFAULT: 2.5 * * @return Horde_ActiveSync_Message_Mail The message object suitable for * streaming to the device. */ protected function _buildMailMessage(Horde_Imap_Client_Mailbox $mbox, Horde_Imap_Client_Data_Fetch $data, $options = array()) { $version = empty($options['protocolversion']) ? Horde_ActiveSync::VERSION_TWOFIVE : $options['protocolversion']; $imap_message = new Horde_ActiveSync_Imap_Message($this->_getImapOb(), $mbox, $data); $eas_message = Horde_ActiveSync::messageFactory('Mail'); // Build To: data (POOMMAIL_TO has a max length of 32768). $to = $imap_message->getToAddresses(); $eas_message->to = array_pop($to['to']); foreach ($to['to'] as $to_atom) { if (strlen($eas_message->to) + strlen($to_atom) > 32768) { break; } $eas_message->to .= ',' . $to_atom; } $eas_message->displayto = implode(';', $to['displayto']); if (empty($eas_message->displayto)) { $eas_message->displayto = $eas_message->to; } // Ensure we don't send broken UTF8 data to the client. It makes clients // angry. And we don't like angry clients. $hdr_charset = $imap_message->getStructure()->getHeaderCharset(); // Fill in other header data try { $eas_message->from = $imap_message->getFromAddress(); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } try { $eas_message->cc = $imap_message->getCc(); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } try { $eas_message->reply_to = $imap_message->getReplyTo(); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } $eas_message->subject = Horde_ActiveSync_Utils::ensureUtf8($imap_message->getSubject(), $hdr_charset); $eas_message->threadtopic = $eas_message->subject; $eas_message->datereceived = $imap_message->getDate(); $eas_message->read = $imap_message->getFlag(Horde_Imap_Client::FLAG_SEEN); // Default to IPM.Note - may change below depending on message content. $eas_message->messageclass = 'IPM.Note'; // Codepage id. MS recommends to always set to UTF-8 when possible. // See http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx $eas_message->cpid = Horde_ActiveSync_Message_Mail::INTERNET_CPID_UTF8; // Message importance. First try X-Priority, then Importance since // Outlook sends the later. if ($priority = $imap_message->getHeaders()->getValue('X-priority')) { $priority = preg_replace('/\\D+/', '', $priority); } else { $priority = $imap_message->getHeaders()->getValue('Importance'); } $eas_message->importance = $this->_getEASImportance($priority); // Get the body data. $mbd = $imap_message->getMessageBodyDataObject($options); if ($version == Horde_ActiveSync::VERSION_TWOFIVE) { $eas_message->body = $mbd->plain['body']->stream; $eas_message->bodysize = $mbd->plain['body']->length(true); $eas_message->bodytruncated = $mbd->plain['truncated']; $eas_message->attachments = $imap_message->getAttachments($version); } else { // Get the message body and determine original type. if ($mbd->html) { $eas_message->airsyncbasenativebodytype = Horde_ActiveSync::BODYPREF_TYPE_HTML; } else { $eas_message->airsyncbasenativebodytype = Horde_ActiveSync::BODYPREF_TYPE_PLAIN; } $airsync_body = Horde_ActiveSync::messageFactory('AirSyncBaseBody'); $body_type_pref = $mbd->getBodyTypePreference(); if ($body_type_pref == Horde_ActiveSync::BODYPREF_TYPE_MIME) { $this->_logger->info(sprintf('[%s] Sending MIME Message.', $this->_procid)); // ActiveSync *REQUIRES* all data sent to be in UTF-8, so we // must convert the body parts to UTF-8. Unfortunately if the // email is signed (or encrypted for that matter) we can't // alter the data in anyway or the signature will not be // verified, so we fetch the entire message and hope for the best. if (!$imap_message->isSigned() && !$imap_message->isEncrypted()) { $mime = new Horde_Mime_Part(); if ($mbd->plain) { $plain_mime = new Horde_Mime_Part(); $plain_mime->setType('text/plain'); $plain_mime->setContents($mbd->plain['body']->stream, array('usestream' => true)); $plain_mime->setCharset('UTF-8'); } if ($mbd->html) { $html_mime = new Horde_Mime_Part(); $html_mime->setType('text/html'); $html_mime->setContents($mbd->html['body']->stream, array('usestream' => true)); $html_mime->setCharset('UTF-8'); } // Sanity check the mime type if (!$mbd->html && !empty($plain_mime)) { $mime = $plain_mime; } elseif (!$mbd->plain && !empty($html_mime)) { $mime = $html_mime; } elseif (!empty($plain_mime) && !empty($html_mime)) { $mime->setType('multipart/alternative'); $mime->addPart($plain_mime); $mime->addPart($html_mime); } $html_mime = null; $plain_mime = null; // If we have attachments, create a multipart/mixed wrapper. if ($imap_message->hasAttachments()) { $base = new Horde_Mime_Part(); $base->setType('multipart/mixed'); $base->addPart($mime); $atc = $imap_message->getAttachmentsMimeParts(); foreach ($atc as $atc_part) { $base->addPart($atc_part); } $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } else { $base = $mime; } $mime = null; // Populate the EAS body structure with the MIME data, but // remove the Content-Type and Content-Transfer-Encoding // headers since we are building this ourselves. $headers = $imap_message->getHeaders(); $headers->removeHeader('Content-Type'); $headers->removeHeader('Content-Transfer-Encoding'); $airsync_body->data = $base->toString(array('headers' => $headers, 'stream' => true)); $airsync_body->estimateddatasize = $base->getBytes(); } else { // Signed/Encrypted message - can't mess with it at all. $raw = new Horde_ActiveSync_Rfc822($imap_message->getFullMsg(true), false); $airsync_body->estimateddatasize = $raw->getBytes(); $airsync_body->data = $raw->getString(); $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } $airsync_body->type = Horde_ActiveSync::BODYPREF_TYPE_MIME; // MIME Truncation // @todo Remove this sanity-check hack in 3.0. This is needed // since truncationsize incorrectly defaulted to a // MIME_TRUNCATION constant and could be cached in the sync-cache. $ts = !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME]['truncationsize']) ? $options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME]['truncationsize'] : false; $mime_truncation = !empty($ts) && $ts > 9 ? $ts : (!empty($options['truncation']) && $options['truncation'] > 9 ? $options['truncation'] : false); $this->_logger->info(sprintf('[%s] Checking MIMETRUNCATION: %s, ServerData: %s', $this->_procid, $mime_truncation, $airsync_body->estimateddatasize)); if (!empty($mime_truncation) && $airsync_body->estimateddatasize > $mime_truncation) { ftruncate($airsync_body->data, $mime_truncation); $airsync_body->truncated = '1'; } else { $airsync_body->truncated = '0'; } $eas_message->airsyncbasebody = $airsync_body; } elseif ($body_type_pref == Horde_ActiveSync::BODYPREF_TYPE_HTML) { // Sending non MIME encoded HTML message text. $eas_message->airsyncbasebody = $this->_buildHtmlPart($mbd, $airsync_body); $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } elseif ($body_type_pref == Horde_ActiveSync::BODYPREF_TYPE_PLAIN) { // Non MIME encoded plaintext $this->_logger->info(sprintf('[%s] Sending PLAINTEXT Message.', $this->_procid)); if (!empty($mbd->plain['size'])) { $airsync_body->estimateddatasize = $mbd->plain['size']; $airsync_body->truncated = $mbd->plain['truncated']; $airsync_body->data = $mbd->plain['body']->stream; $airsync_body->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN; $eas_message->airsyncbasebody = $airsync_body; } $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } // It's legal to have both a BODY and a BODYPART, so we must also // check for that. if ($version > Horde_ActiveSync::VERSION_FOURTEEN && !empty($options['bodypartprefs'])) { $body_part = Horde_ActiveSync::messageFactory('AirSyncBaseBodypart'); $eas_message->airsyncbasebodypart = $this->_buildBodyPart($mbd, $options, $body_part); } if ($version > Horde_ActiveSync::VERSION_TWELVEONE) { $flags = array(); $msgFlags = $this->_getMsgFlags(); foreach ($imap_message->getFlags() as $flag) { if (!empty($msgFlags[Horde_String::lower($flag)])) { $flags[] = $msgFlags[Horde_String::lower($flag)]; } } $eas_message->categories = $flags; } } // Body Preview? Note that this is different from BodyPart's preview if ($version >= Horde_ActiveSync::VERSION_FOURTEEN && !empty($options['bodyprefs']['preview'])) { $mbd->plain['body']->rewind(); $eas_message->airsyncbasebody->preview = $mbd->plain['body']->substring(0, $options['bodyprefs']['preview']); } $mbd = null; // Check for special message types. if ($imap_message->isEncrypted()) { $eas_message->messageclass = 'IPM.Note.SMIME'; } elseif ($imap_message->isSigned()) { $eas_message->messageclass = 'IPM.Note.SMIME.MultipartSigned'; } $part = $imap_message->getStructure(); if ($part->getType() == 'multipart/report') { $ids = array_keys($imap_message->contentTypeMap()); reset($ids); $part1_id = next($ids); $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $lines = explode(chr(13), $imap_message->getBodyPart($part2_id, array('decode' => true))); switch ($part->getContentTypeParameter('report-type')) { case 'delivery-status': foreach ($lines as $line) { if (strpos(trim($line), 'Action:') === 0) { switch (trim(substr(trim($line), 7))) { case 'failed': $eas_message->messageclass = 'REPORT.IPM.NOTE.NDR'; break 2; case 'delayed': $eas_message->messageclass = 'REPORT.IPM.NOTE.DELAYED'; break 2; case 'delivered': $eas_message->messageclass = 'REPORT.IPM.NOTE.DR'; break 2; } } } break; case 'disposition-notification': foreach ($lines as $line) { if (strpos(trim($line), 'Disposition:') === 0) { if (strpos($line, 'displayed') !== false) { $eas_message->messageclass = 'REPORT.IPM.NOTE.IPNRN'; } elseif (strpos($line, 'deleted') !== false) { $eas_message->messageclass = 'REPORT.IPM.NOTE.IPNNRN'; } break; } } } } $part = null; // Check for meeting requests and POOMMAIL_FLAG data if ($version >= Horde_ActiveSync::VERSION_TWELVE) { $eas_message->contentclass = 'urn:content-classes:message'; if ($mime_part = $imap_message->hasiCalendar()) { $data = Horde_ActiveSync_Utils::ensureUtf8($mime_part->getContents(), $mime_part->getCharset()); $vCal = new Horde_Icalendar(); if ($vCal->parsevCalendar($data, 'VCALENDAR', $mime_part->getCharset())) { $classes = $vCal->getComponentClasses(); } else { $classes = array(); } if (!empty($classes['horde_icalendar_vevent'])) { try { $method = $vCal->getAttribute('METHOD'); $eas_message->contentclass = 'urn:content-classes:calendarmessage'; } catch (Horde_Icalendar_Exception $e) { } switch ($method) { case 'REQUEST': case 'PUBLISH': $eas_message->messageclass = 'IPM.Schedule.Meeting.Request'; $mtg = Horde_ActiveSync::messageFactory('MeetingRequest'); $mtg->fromvEvent($vCal); $eas_message->meetingrequest = $mtg; break; case 'REPLY': try { $reply_status = $this->_getiTipStatus($vCal); switch ($reply_status) { case 'ACCEPTED': $eas_message->messageclass = 'IPM.Schedule.Meeting.Resp.Pos'; break; case 'DECLINED': $eas_message->messageclass = 'IPM.Schedule.Meeting.Resp.Neg'; break; case 'TENTATIVE': $eas_message->messageclass = 'IPM.Schedule.Meeting.Resp.Tent'; } $mtg = Horde_ActiveSync::messageFactory('MeetingRequest'); $mtg->fromvEvent($vCal); $eas_message->meetingrequest = $mtg; } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } } } } if ($imap_message->getFlag(Horde_Imap_Client::FLAG_FLAGGED)) { $poommail_flag = Horde_ActiveSync::messageFactory('Flag'); $poommail_flag->subject = $imap_message->getSubject(); $poommail_flag->flagstatus = Horde_ActiveSync_Message_Flag::FLAG_STATUS_ACTIVE; $poommail_flag->flagtype = Horde_Imap_Client::FLAG_FLAGGED; $eas_message->flag = $poommail_flag; } } if ($version >= Horde_ActiveSync::VERSION_FOURTEEN) { $eas_message->messageid = $imap_message->getHeaders()->getValue('Message-ID'); $eas_message->forwarded = $imap_message->getFlag(Horde_Imap_Client::FLAG_FORWARDED); $eas_message->answered = $imap_message->getFlag(Horde_Imap_Client::FLAG_ANSWERED); } $imap_message = null; return $eas_message; }
/** * As of IMP 6, special mailboxes are stored in UTF-8, not UTF7-IMAP. */ protected function _upgradeMailboxPrefs() { global $injector, $prefs; $special_mboxes = array('drafts_folder', 'spam_folder', 'trash_folder'); foreach ($special_mboxes as $val) { if (!$prefs->isDefault($val)) { $old_pref = strval(IMP_Mailbox::getPref($val)); if (!Horde_Mime::is8bit($old_pref, 'UTF-8')) { $mbox = IMP_Mailbox::get(Horde_String::convertCharset($old_pref, 'UTF7-IMAP', 'UTF-8')); $prefs->setValue($val, $old_pref->{$mbox}->pref_to); } } } $imp_identity = $injector->getInstance('IMP_Identity'); foreach ($imp_identity->getAll('sent_mail_folder') as $key => $val) { if (!is_null($val) && !Horde_Mime::is8bit($val, 'UTF-8')) { $mbox = IMP_Mailbox::get(Horde_String::convertCharset(strval($val), 'UTF7-IMAP', 'UTF-8')); $imp_identity->setValue(IMP_Mailbox::MBOX_SENT, $mbox, $key); } } }
/** * Authenticate to the POP3 server. * * @param string $method POP3 login method. * * @throws Horde_Imap_Client_Exception */ protected function _tryLogin($method) { $username = $this->getParam('username'); $password = $this->getParam('password'); switch ($method) { case 'CRAM-MD5': case 'CRAM-SHA1': case 'CRAM-SHA256': // RFC 5034: CRAM-MD5 // CRAM-SHA1 & CRAM-SHA256 supported by Courier SASL library $challenge = $this->_sendLine('AUTH ' . $method); $response = base64_encode($username . ' ' . hash_hmac(Horde_String::lower(substr($method, 5)), base64_decode(substr($challenge['resp'], 2)), $password, true)); $this->_sendLine($response, array('debug' => sprintf('[AUTH Response (username: %s)]', $username))); break; case 'DIGEST-MD5': // RFC 2831; Obsoleted by RFC 6331 $challenge = $this->_sendLine('AUTH DIGEST-MD5'); $response = base64_encode(new Horde_Imap_Client_Auth_DigestMD5($username, $password, base64_decode(substr($challenge['resp'], 2)), $this->getParam('hostspec'), 'pop3')); $sresponse = $this->_sendLine($response, array('debug' => sprintf('[AUTH Response (username: %s)]', $username))); if (stripos(base64_decode(substr($sresponse['resp'], 2)), 'rspauth=') === false) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Unexpected response from server when authenticating."), Horde_Imap_Client_Exception::SERVER_CONNECT); } /* POP3 doesn't use protocol's third step. */ $this->_sendLine(''); break; case 'LOGIN': // RFC 4616 (AUTH=PLAIN) & 5034 (POP3 SASL) $this->_sendLine('AUTH LOGIN'); $this->_sendLine(base64_encode($username)); $this->_sendLine(base64_encode($password), array('debug' => sprintf('[AUTH Password (username: %s)]', $username))); break; case 'PLAIN': // RFC 5034 $this->_sendLine('AUTH PLAIN ' . base64_encode(implode("", array($username, $username, $password))), array('debug' => sprintf('AUTH PLAIN [Auth Response (username: %s)]', $username))); break; case 'APOP': /* If UTF8 (+ USER) is active, and non-ASCII exists, need to apply * SASLprep to username/password. RFC 6856[2.2]. Reject if * UTF8 (+ USER) is not supported and 8-bit characters exist. */ if (Horde_Mime::is8bit($username) || Horde_Mime::is8bit($password)) { if (empty($this->_temp['utf8']) || !$this->_capability('UTF8', 'USER') || !class_exists('Horde_Stringprep')) { $error = true; } else { Horde_Stringprep::autoload(); $saslprep = new Znerol\Component\Stringprep\Profile\SASLprep(); try { $username = $saslprep->apply($username, 'UTF-8', Znerol\Compnonent\Stringprep\Profile::MODE_QUERY); $password = $saslprep->apply($password, 'UTF-8', Znerol\Compnonent\Stringprep\Profile::MODE_STORE); $error = false; } catch (Znerol\Component\Stringprep\ProfileException $e) { $error = true; } } if ($error) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); } } // RFC 1939 [7] $this->_sendLine('APOP ' . $username . ' ' . hash('md5', $this->_temp['pop3timestamp'] . $password)); break; case 'USER': /* POP3 servers without UTF8 (+ USER) does not accept non-ASCII * in USER/PASS. RFC 6856[2.2] */ if ((empty($this->_temp['utf8']) || !$this->_capability('UTF8', 'USER')) && (Horde_Mime::is8bit($username) || Horde_Mime::is8bit($password))) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); } // RFC 1939 [7] $this->_sendLine('USER ' . $username); $this->_sendLine('PASS ' . $password, array('debug' => 'PASS [Password]')); break; case 'SCRAM-SHA-1': $scram = new Horde_Imap_Client_Auth_Scram($username, $password, 'SHA1'); $c1 = $this->_sendLine('AUTH ' . $method . ' ' . base64_encode($scram->getClientFirstMessage())); $sr1 = base64_decode(substr($c1['resp'], 2)); if (!$scram->parseServerFirstMessage($sr1)) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); } $c2 = $this->_sendLine(base64_encode($scram->getClientFinalMessage())); $sr2 = base64_decode(substr($c2['resp'], 2)); if (!$scram->parseServerFirstMessage($sr)) { throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Authentication failed."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED); /* This means authentication passed, according to the server, * but the server signature is incorrect. This indicates that * server verification has failed. Immediately disconnect from * the server, since this is a possible security issue. */ $this->logout(); throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Server failed verification check."), Horde_Imap_Client_Exception::LOGIN_SERVER_VERIFICATION_FAILED); } $this->_sendLine(''); break; default: $e = new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Unknown authentication method: %s"), Horde_Imap_Client_Exception::SERVER_CONNECT); $e->messagePrintf(array($method)); throw $e; } }
/** */ protected function _vacationCode() { $code = 'vacation :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_Script_Util::vacationReason($this->_vars['reason'], $this->_vars['start'], $this->_vars['end'])) . '";'; }