1 <?php 2 /** 3 * Copyright 2008-2017 Horde LLC (http://www.horde.org/) 4 * 5 * getBaseSubject() code adapted from imap-base-subject.c (Dovecot 1.2) 6 * Original code released under the LGPL-2.1 7 * Copyright (c) 2002-2008 Timo Sirainen <tss@iki.fi> 8 * 9 * See the enclosed file LICENSE for license information (LGPL). If you 10 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 11 * 12 * @category Horde 13 * @copyright 2002-2008 Timo Sirainen 14 * @copyright 2008-2017 Horde LLC 15 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 16 * @package Imap_Client 17 */ 18 19 /** 20 * Determines the "base subject" of a string (RFC 5256 [2.1]). 21 * 22 * @author Timo Sirainen <tss@iki.fi> 23 * @author Michael Slusarz <slusarz@horde.org> 24 * @category Horde 25 * @copyright 2002-2008 Timo Sirainen 26 * @copyright 2011-2017 Horde LLC 27 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 28 * @package Imap_Client 29 */ 30 class Horde_Imap_Client_Data_BaseSubject 31 { 32 /** 33 * The base subject. 34 * 35 * @var string 36 */ 37 protected $_subject; 38 39 /** 40 * Constructor. 41 * 42 * @param string $str The subject string. 43 * @param array $opts Additional options: 44 * - keepblob: (boolean) Don't remove any "blob" information (i.e. text 45 * leading text between square brackets) from string. 46 * 47 * @return string The cleaned up subject string. 48 */ 49 public function __construct($str, array $opts = array()) 50 { 51 // Rule 1a: MIME decode. 52 $str = Horde_Mime::decode($str); 53 54 // Rule 1b: Remove superfluous whitespace. 55 $str = preg_replace("/[\t\r\n ]+/", ' ', $str); 56 57 do { 58 /* (2) Remove all trailing text of the subject that matches the 59 * the subj-trailer ABNF, repeat until no more matches are 60 * possible. */ 61 $str = preg_replace("/(?:\s*\(fwd\)\s*)+$/i", '', $str); 62 63 do { 64 /* (3) Remove all prefix text of the subject that matches the 65 * subj-leader ABNF. */ 66 $found = $this->_removeSubjLeader($str, !empty($opts['keepblob'])); 67 68 /* (4) If there is prefix text of the subject that matches 69 * the subj-blob ABNF, and removing that prefix leaves a 70 * non-empty subj-base, then remove the prefix text. */ 71 $found = (empty($opts['keepblob']) && $this->_removeBlobWhenNonempty($str)) || $found; 72 73 /* (5) Repeat (3) and (4) until no matches remain. */ 74 } while ($found); 75 76 /* (6) If the resulting text begins with the subj-fwd-hdr ABNF and 77 * ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and 78 * subj-fwd-trl and repeat from step (2). */ 79 } while ($this->_removeSubjFwdHdr($str)); 80 81 $this->_subject = strval($str); 82 } 83 84 /** 85 * Return the "base subject" defined in RFC 5256 [2.1]. 86 * 87 * @return string The base subject. 88 */ 89 public function __toString() 90 { 91 return $this->_subject; 92 } 93 94 /** 95 * Remove all prefix text of the subject that matches the subj-leader 96 * ABNF. 97 * 98 * @param string &$str The subject string. 99 * @param boolean $keepblob Remove blob information? 100 * 101 * @return boolean True if string was altered. 102 */ 103 protected function _removeSubjLeader(&$str, $keepblob = false) 104 { 105 $ret = false; 106 107 if (!strlen($str)) { 108 return $ret; 109 } 110 111 if ($len = strspn($str, " \t")) { 112 $str = substr($str, $len); 113 $ret = true; 114 } 115 116 $i = 0; 117 118 if (!$keepblob) { 119 while (isset($str[$i]) && ($str[$i] === '[')) { 120 if (($i = $this->_removeBlob($str, $i)) === false) { 121 return $ret; 122 } 123 } 124 } 125 126 if (stripos($str, 're', $i) === 0) { 127 $i += 2; 128 } elseif (stripos($str, 'fw', $i) === 0) { 129 $i += (stripos($str, 'fwd', $i) === 0) ? 3 : 2; 130 } else { 131 return $ret; 132 } 133 134 $i += strspn($str, " \t", $i); 135 136 if (!$keepblob) { 137 while (isset($str[$i]) && ($str[$i] === '[')) { 138 if (($i = $this->_removeBlob($str, $i)) === false) { 139 return $ret; 140 } 141 } 142 } 143 144 if (!isset($str[$i]) || ($str[$i] !== ':')) { 145 return $ret; 146 } 147 148 $str = substr($str, ++$i); 149 150 return true; 151 } 152 153 /** 154 * Remove "[...]" text. 155 * 156 * @param string $str The subject string. 157 * @param integer $i Current position. 158 * 159 * @return boolean|integer False if blob was not found, otherwise the 160 * string position of the first non-blob char. 161 */ 162 protected function _removeBlob($str, $i) 163 { 164 if ($str[$i] !== '[') { 165 return false; 166 } 167 168 ++$i; 169 170 for ($cnt = strlen($str); $i < $cnt; ++$i) { 171 if ($str[$i] === ']') { 172 break; 173 } 174 175 if ($str[$i] === '[') { 176 return false; 177 } 178 } 179 180 if ($i === ($cnt - 1)) { 181 return false; 182 } 183 184 ++$i; 185 186 if ($str[$i] === ' ') { 187 ++$i; 188 } 189 190 return $i; 191 } 192 193 /** 194 * Remove "[...]" text if it doesn't result in the subject becoming 195 * empty. 196 * 197 * @param string &$str The subject string. 198 * 199 * @return boolean True if string was altered. 200 */ 201 protected function _removeBlobWhenNonempty(&$str) 202 { 203 if ($str && 204 ($str[0] === '[') && 205 (($i = $this->_removeBlob($str, 0)) !== false) && 206 ($i !== strlen($str))) { 207 $str = substr($str, $i); 208 return true; 209 } 210 211 return false; 212 } 213 214 /** 215 * Remove a "[fwd: ... ]" string. 216 * 217 * @param string &$str The subject string. 218 * 219 * @return boolean True if string was altered. 220 */ 221 protected function _removeSubjFwdHdr(&$str) 222 { 223 if ((stripos($str, '[fwd:') !== 0) || (substr($str, -1) !== ']')) { 224 return false; 225 } 226 227 $str = substr($str, 5, -1); 228 return true; 229 } 230 231 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body