/lib/horde/framework/Horde/Mail/Rfc822/List.php
PHP | 518 lines | 321 code | 71 blank | 126 comment | 48 complexity | bb2af5e2abb4f4e4d51268f42a88d104 MD5 | raw file
- <?php
- /**
- * Copyright 2012-2017 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (BSD). If you
- * did not receive this file, see http://www.horde.org/licenses/bsd.
- *
- * @category Horde
- * @copyright 2012-2017 Horde LLC
- * @license http://www.horde.org/licenses/bsd New BSD License
- * @package Mail
- */
- /**
- * Container object for a collection of RFC 822 elements.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @copyright 2012-2017 Horde LLC
- * @license http://www.horde.org/licenses/bsd New BSD License
- * @package Mail
- *
- * @property-read array $addresses The list of all addresses (address
- * w/personal parts).
- * @property-read array $bare_addresses The list of all addresses (mail@host).
- * @property-read array $bare_addresses_idn The list of all addresses
- * (mail@host; IDN encoded).
- * (@since 2.1.0)
- * @property-read array $base_addresses The list of ONLY base addresses
- * (Address objects).
- * @property-read array $raw_addresses The list of all addresses (Address
- * objects).
- */
- class Horde_Mail_Rfc822_List
- extends Horde_Mail_Rfc822_Object
- implements ArrayAccess, Countable, SeekableIterator, Serializable
- {
- /** Filter masks. */
- const HIDE_GROUPS = 1;
- const BASE_ELEMENTS = 2;
- /**
- * List data.
- *
- * @var array
- */
- protected $_data = array();
- /**
- * Current Iterator filter.
- *
- * @var array
- */
- protected $_filter = array();
- /**
- * Current Iterator pointer.
- *
- * @var array
- */
- protected $_ptr;
- /**
- * Constructor.
- *
- * @param mixed $obs Address data to store in this object.
- */
- public function __construct($obs = null)
- {
- if (!is_null($obs)) {
- $this->add($obs);
- }
- }
- /**
- */
- public function __get($name)
- {
- switch ($name) {
- case 'addresses':
- case 'bare_addresses':
- case 'bare_addresses_idn':
- case 'base_addresses':
- case 'raw_addresses':
- $old = $this->_filter;
- $mask = ($name == 'base_addresses')
- ? self::BASE_ELEMENTS
- : self::HIDE_GROUPS;
- $this->setIteratorFilter($mask, empty($old['filter']) ? null : $old['filter']);
- $out = array();
- foreach ($this as $val) {
- switch ($name) {
- case 'addresses':
- $out[] = strval($val);
- break;
- case 'bare_addresses':
- $out[] = $val->bare_address;
- break;
- case 'bare_addresses_idn':
- $out[] = $val->bare_address_idn;
- break;
- case 'base_addresses':
- case 'raw_addresses':
- $out[] = clone $val;
- break;
- }
- }
- $this->_filter = $old;
- return $out;
- }
- }
- /**
- * Add objects to the container.
- *
- * @param mixed $obs Address data to store in this object.
- */
- public function add($obs)
- {
- foreach ($this->_normalize($obs) as $val) {
- $this->_data[] = $val;
- }
- }
- /**
- * Remove addresses from the container. This method ignores Group objects.
- *
- * @param mixed $obs Addresses to remove.
- */
- public function remove($obs)
- {
- $old = $this->_filter;
- $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);
- foreach ($this->_normalize($obs) as $val) {
- $remove = array();
- foreach ($this as $key => $val2) {
- if ($val2->match($val)) {
- $remove[] = $key;
- }
- }
- foreach (array_reverse($remove) as $key) {
- unset($this[$key]);
- }
- }
- $this->_filter = $old;
- }
- /**
- * Removes duplicate addresses from list. This method ignores Group
- * objects.
- */
- public function unique()
- {
- $exist = $remove = array();
- $old = $this->_filter;
- $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);
- // For duplicates, we use the first address that contains personal
- // information.
- foreach ($this as $key => $val) {
- $bare = $val->bare_address;
- if (isset($exist[$bare])) {
- if (($exist[$bare] == -1) || is_null($val->personal)) {
- $remove[] = $key;
- } else {
- $remove[] = $exist[$bare];
- $exist[$bare] = -1;
- }
- } else {
- $exist[$bare] = is_null($val->personal)
- ? $key
- : -1;
- }
- }
- foreach (array_reverse($remove) as $key) {
- unset($this[$key]);
- }
- $this->_filter = $old;
- }
- /**
- * Group count.
- *
- * @return integer The number of groups in the list.
- */
- public function groupCount()
- {
- $ret = 0;
- foreach ($this->_data as $val) {
- if ($val instanceof Horde_Mail_Rfc822_Group) {
- ++$ret;
- }
- }
- return $ret;
- }
- /**
- * Set the Iterator filter.
- *
- * @param integer $mask Filter masks.
- * @param mixed $filter An e-mail, or as list of e-mails, to filter by.
- */
- public function setIteratorFilter($mask = 0, $filter = null)
- {
- $this->_filter = array();
- if ($mask) {
- $this->_filter['mask'] = $mask;
- }
- if (!is_null($filter)) {
- $rfc822 = new Horde_Mail_Rfc822();
- $this->_filter['filter'] = $rfc822->parseAddressList($filter);
- }
- }
- /**
- */
- protected function _writeAddress($opts)
- {
- $out = array();
- foreach ($this->_data as $val) {
- $out[] = $val->writeAddress($opts);
- }
- return implode(', ', $out);
- }
- /**
- */
- public function match($ob)
- {
- if (!($ob instanceof Horde_Mail_Rfc822_List)) {
- $ob = new Horde_Mail_Rfc822_List($ob);
- }
- $a = $this->bare_addresses;
- sort($a);
- $b = $ob->bare_addresses;
- sort($b);
- return ($a == $b);
- }
- /**
- * Does this list contain the given e-mail address?
- *
- * @param mixed $address An e-mail address.
- *
- * @return boolean True if the e-mail address is contained in the list.
- */
- public function contains($address)
- {
- $ob = new Horde_Mail_Rfc822_Address($address);
- foreach ($this->raw_addresses as $val) {
- if ($val->match($ob)) {
- return true;
- }
- }
- return false;
- }
- /**
- * Convenience method to return the first element in a list.
- *
- * Useful since it allows chaining; older PHP versions did not allow array
- * access dereferencing from the results of a function call.
- *
- * @since 2.5.0
- *
- * @return Horde_Mail_Rfc822_Object Rfc822 object, or null if no object.
- */
- public function first()
- {
- return $this[0];
- }
- /**
- * Normalize objects to add to list.
- *
- * @param mixed $obs Address data to store in this object.
- *
- * @return array Entries to add.
- */
- protected function _normalize($obs)
- {
- $add = array();
- if (!($obs instanceof Horde_Mail_Rfc822_List) &&
- !is_array($obs)) {
- $obs = array($obs);
- }
- foreach ($obs as $val) {
- if (is_string($val)) {
- $rfc822 = new Horde_Mail_Rfc822();
- $val = $rfc822->parseAddressList($val);
- }
- if ($val instanceof Horde_Mail_Rfc822_List) {
- $val->setIteratorFilter(self::BASE_ELEMENTS);
- foreach ($val as $val2) {
- $add[] = $val2;
- }
- } elseif ($val instanceof Horde_Mail_Rfc822_Object) {
- $add[] = $val;
- }
- }
- return $add;
- }
- /* ArrayAccess methods. */
- /**
- */
- public function offsetExists($offset)
- {
- return !is_null($this[$offset]);
- }
- /**
- */
- public function offsetGet($offset)
- {
- try {
- $this->seek($offset);
- return $this->current();
- } catch (OutOfBoundsException $e) {
- return null;
- }
- }
- /**
- */
- public function offsetSet($offset, $value)
- {
- if ($ob = $this[$offset]) {
- if (is_null($this->_ptr['subidx'])) {
- $tmp = $this->_normalize($value);
- if (isset($tmp[0])) {
- $this->_data[$this->_ptr['idx']] = $tmp[0];
- }
- } else {
- $ob[$offset] = $value;
- }
- $this->_ptr = null;
- }
- }
- /**
- */
- public function offsetUnset($offset)
- {
- if ($ob = $this[$offset]) {
- if (is_null($this->_ptr['subidx'])) {
- unset($this->_data[$this->_ptr['idx']]);
- $this->_data = array_values($this->_data);
- } else {
- unset($ob->addresses[$this->_ptr['subidx']]);
- }
- $this->_ptr = null;
- }
- }
- /* Countable methods. */
- /**
- * Address count.
- *
- * @return integer The number of addresses.
- */
- public function count()
- {
- return count($this->addresses);
- }
- /* Iterator methods. */
- public function current()
- {
- if (!$this->valid()) {
- return null;
- }
- $ob = $this->_data[$this->_ptr['idx']];
- return is_null($this->_ptr['subidx'])
- ? $ob
- : $ob->addresses[$this->_ptr['subidx']];
- }
- public function key()
- {
- return $this->_ptr['key'];
- }
- public function next()
- {
- if (is_null($this->_ptr['subidx'])) {
- $curr = $this->current();
- if (($curr instanceof Horde_Mail_Rfc822_Group) && count($curr)) {
- $this->_ptr['subidx'] = 0;
- } else {
- ++$this->_ptr['idx'];
- }
- $curr = $this->current();
- } elseif (!($curr = $this->_data[$this->_ptr['idx']]->addresses[++$this->_ptr['subidx']])) {
- $this->_ptr['subidx'] = null;
- ++$this->_ptr['idx'];
- $curr = $this->current();
- }
- if (!is_null($curr)) {
- if (!empty($this->_filter) && $this->_iteratorFilter($curr)) {
- $this->next();
- } else {
- ++$this->_ptr['key'];
- }
- }
- }
- public function rewind()
- {
- $this->_ptr = array(
- 'idx' => 0,
- 'key' => 0,
- 'subidx' => null
- );
- if ($this->valid() &&
- !empty($this->_filter) &&
- $this->_iteratorFilter($this->current())) {
- $this->next();
- $this->_ptr['key'] = 0;
- }
- }
- public function valid()
- {
- return (!empty($this->_ptr) && isset($this->_data[$this->_ptr['idx']]));
- }
- public function seek($position)
- {
- if (!$this->valid() ||
- ($position < $this->_ptr['key'])) {
- $this->rewind();
- }
- for ($i = $this->_ptr['key']; ; ++$i) {
- if ($i == $position) {
- return;
- }
- $this->next();
- if (!$this->valid()) {
- throw new OutOfBoundsException('Position not found.');
- }
- }
- }
- protected function _iteratorFilter($ob)
- {
- if (!empty($this->_filter['mask'])) {
- if (($this->_filter['mask'] & self::HIDE_GROUPS) &&
- ($ob instanceof Horde_Mail_Rfc822_Group)) {
- return true;
- }
- if (($this->_filter['mask'] & self::BASE_ELEMENTS) &&
- !is_null($this->_ptr['subidx'])) {
- return true;
- }
- }
- if (!empty($this->_filter['filter']) &&
- ($ob instanceof Horde_Mail_Rfc822_Address)) {
- foreach ($this->_filter['filter'] as $val) {
- if ($ob->match($val)) {
- return true;
- }
- }
- }
- return false;
- }
- /* Serializable methods. */
- public function serialize()
- {
- return serialize($this->_data);
- }
- public function unserialize($data)
- {
- $this->_data = unserialize($data);
- }
- }