1<?php 2 3namespace Sabre\VObject\Property; 4 5use 6 Sabre\VObject\Property, 7 Sabre\VObject\Component, 8 Sabre\VObject\Parser\MimeDir, 9 Sabre\VObject\Document; 10 11/** 12 * Text property 13 * 14 * This object represents TEXT values. 15 * 16 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) 17 * @author Evert Pot (http://evertpot.com/) 18 * @license http://sabre.io/license/ Modified BSD License 19 */ 20class Text extends Property { 21 22 /** 23 * In case this is a multi-value property. This string will be used as a 24 * delimiter. 25 * 26 * @var string 27 */ 28 public $delimiter = ','; 29 30 /** 31 * List of properties that are considered 'structured'. 32 * 33 * @var array 34 */ 35 protected $structuredValues = array( 36 // vCard 37 'N', 38 'ADR', 39 'ORG', 40 'GENDER', 41 42 // iCalendar 43 'REQUEST-STATUS', 44 ); 45 46 /** 47 * Some text components have a minimum number of components. 48 * 49 * N must for instance be represented as 5 components, separated by ;, even 50 * if the last few components are unused. 51 * 52 * @var array 53 */ 54 protected $minimumPropertyValues = array( 55 'N' => 5, 56 'ADR' => 7, 57 ); 58 59 /** 60 * Creates the property. 61 * 62 * You can specify the parameters either in key=>value syntax, in which case 63 * parameters will automatically be created, or you can just pass a list of 64 * Parameter objects. 65 * 66 * @param Component $root The root document 67 * @param string $name 68 * @param string|array|null $value 69 * @param array $parameters List of parameters 70 * @param string $group The vcard property group 71 * @return void 72 */ 73 public function __construct(Component $root, $name, $value = null, array $parameters = array(), $group = null) { 74 75 // There's two types of multi-valued text properties: 76 // 1. multivalue properties. 77 // 2. structured value properties 78 // 79 // The former is always separated by a comma, the latter by semi-colon. 80 if (in_array($name, $this->structuredValues)) { 81 $this->delimiter = ';'; 82 } 83 84 parent::__construct($root, $name, $value, $parameters, $group); 85 86 } 87 88 89 /** 90 * Sets a raw value coming from a mimedir (iCalendar/vCard) file. 91 * 92 * This has been 'unfolded', so only 1 line will be passed. Unescaping is 93 * not yet done, but parameters are not included. 94 * 95 * @param string $val 96 * @return void 97 */ 98 public function setRawMimeDirValue($val) { 99 100 $this->setValue(MimeDir::unescapeValue($val, $this->delimiter)); 101 102 } 103 104 /** 105 * Sets the value as a quoted-printable encoded string. 106 * 107 * @param string $val 108 * @return void 109 */ 110 public function setQuotedPrintableValue($val) { 111 112 $val = quoted_printable_decode($val); 113 114 // Quoted printable only appears in vCard 2.1, and the only character 115 // that may be escaped there is ;. So we are simply splitting on just 116 // that. 117 // 118 // We also don't have to unescape \\, so all we need to look for is a ; 119 // that's not preceeded with a \. 120 $regex = '# (?<!\\\\) ; #x'; 121 $matches = preg_split($regex, $val); 122 $this->setValue($matches); 123 124 } 125 126 /** 127 * Returns a raw mime-dir representation of the value. 128 * 129 * @return string 130 */ 131 public function getRawMimeDirValue() { 132 133 $val = $this->getParts(); 134 135 if (isset($this->minimumPropertyValues[$this->name])) { 136 $val = array_pad($val, $this->minimumPropertyValues[$this->name], ''); 137 } 138 139 foreach($val as &$item) { 140 141 if (!is_array($item)) { 142 $item = array($item); 143 } 144 145 foreach($item as &$subItem) { 146 $subItem = strtr( 147 $subItem, 148 array( 149 '\\' => '\\\\', 150 ';' => '\;', 151 ',' => '\,', 152 "\n" => '\n', 153 "\r" => "", 154 ) 155 ); 156 } 157 $item = implode(',', $item); 158 159 } 160 161 return implode($this->delimiter, $val); 162 163 } 164 165 /** 166 * Returns the value, in the format it should be encoded for json. 167 * 168 * This method must always return an array. 169 * 170 * @return array 171 */ 172 public function getJsonValue() { 173 174 // Structured text values should always be returned as a single 175 // array-item. Multi-value text should be returned as multiple items in 176 // the top-array. 177 if (in_array($this->name, $this->structuredValues)) { 178 return array($this->getParts()); 179 } else { 180 return $this->getParts(); 181 } 182 183 } 184 185 /** 186 * Returns the type of value. 187 * 188 * This corresponds to the VALUE= parameter. Every property also has a 189 * 'default' valueType. 190 * 191 * @return string 192 */ 193 public function getValueType() { 194 195 return "TEXT"; 196 197 } 198 199 /** 200 * Turns the object back into a serialized blob. 201 * 202 * @return string 203 */ 204 public function serialize() { 205 206 // We need to kick in a special type of encoding, if it's a 2.1 vcard. 207 if ($this->root->getDocumentType() !== Document::VCARD21) { 208 return parent::serialize(); 209 } 210 211 $val = $this->getParts(); 212 213 if (isset($this->minimumPropertyValues[$this->name])) { 214 $val = array_pad($val, $this->minimumPropertyValues[$this->name], ''); 215 } 216 217 // Imploding multiple parts into a single value, and splitting the 218 // values with ;. 219 if (count($val)>1) { 220 foreach($val as $k=>$v) { 221 $val[$k] = str_replace(';','\;', $v); 222 } 223 $val = implode(';', $val); 224 } else { 225 $val = $val[0]; 226 } 227 228 $str = $this->name; 229 if ($this->group) $str = $this->group . '.' . $this->name; 230 foreach($this->parameters as $param) { 231 232 if ($param->getValue() === 'QUOTED-PRINTABLE') { 233 continue; 234 } 235 $str.=';' . $param->serialize(); 236 237 } 238 239 240 241 // If the resulting value contains a \n, we must encode it as 242 // quoted-printable. 243 if (strpos($val,"\n") !== false) { 244 245 $str.=';ENCODING=QUOTED-PRINTABLE:'; 246 $lastLine=$str; 247 $out = null; 248 249 // The PHP built-in quoted-printable-encode does not correctly 250 // encode newlines for us. Specifically, the \r\n sequence must in 251 // vcards be encoded as =0D=OA and we must insert soft-newlines 252 // every 75 bytes. 253 for($ii=0;$ii<strlen($val);$ii++) { 254 $ord = ord($val[$ii]); 255 // These characters are encoded as themselves. 256 if ($ord >= 32 && $ord <=126) { 257 $lastLine.=$val[$ii]; 258 } else { 259 $lastLine.='=' . strtoupper(bin2hex($val[$ii])); 260 } 261 if (strlen($lastLine)>=75) { 262 // Soft line break 263 $out.=$lastLine. "=\r\n "; 264 $lastLine = null; 265 } 266 267 } 268 if (!is_null($lastLine)) $out.= $lastLine . "\r\n"; 269 return $out; 270 271 } else { 272 $str.=':' . $val; 273 $out = ''; 274 while(strlen($str)>0) { 275 if (strlen($str)>75) { 276 $out.= mb_strcut($str,0,75,'utf-8') . "\r\n"; 277 $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8'); 278 } else { 279 $out.=$str . "\r\n"; 280 $str=''; 281 break; 282 } 283 } 284 285 return $out; 286 287 288 } 289 290 } 291 292 /** 293 * Validates the node for correctness. 294 * 295 * The following options are supported: 296 * - Node::REPAIR - If something is broken, and automatic repair may 297 * be attempted. 298 * 299 * An array is returned with warnings. 300 * 301 * Every item in the array has the following properties: 302 * * level - (number between 1 and 3 with severity information) 303 * * message - (human readable message) 304 * * node - (reference to the offending node) 305 * 306 * @param int $options 307 * @return array 308 */ 309 public function validate($options = 0) { 310 311 $warnings = parent::validate($options); 312 313 if (isset($this->minimumPropertyValues[$this->name])) { 314 315 $minimum = $this->minimumPropertyValues[$this->name]; 316 $parts = $this->getParts(); 317 if (count($parts) < $minimum) { 318 $warnings[] = array( 319 'level' => 1, 320 'message' => 'This property must have at least ' . $minimum . ' components. It only has ' . count($parts), 321 'node' => $this, 322 ); 323 if ($options & self::REPAIR) { 324 $parts = array_pad($parts, $minimum, ''); 325 $this->setParts($parts); 326 } 327 } 328 329 } 330 return $warnings; 331 332 } 333} 334