[ Index ] |
|
Code source de eGroupWare 1.2.106-2 |
1 <?php 2 /** 3 * Class representing iCalendar files. 4 * 5 * $Horde: framework/iCalendar/iCalendar.php,v 1.53 2004/09/24 03:34:43 chuck Exp $ 6 * 7 * Copyright 2003-2004 Mike Cochrane <mike@graftonhall.co.nz> 8 * 9 * See the enclosed file COPYING for license information (LGPL). If you 10 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. 11 * 12 * @author Mike Cochrane <mike@graftonhall.co.nz> 13 * @version $Revision: 22838 $ 14 * @since Horde 3.0 15 * @package Horde_iCalendar 16 */ 17 class Horde_iCalendar { 18 19 /** 20 * The parent (containing) iCalendar object. 21 * 22 * @var object Horde_iCalendar $_container 23 */ 24 var $_container = false; 25 26 var $_attributes = array(); 27 28 var $_components = array(); 29 30 /** 31 * According to RFC 2425, we should always use CRLF-terminated 32 * lines. 33 * 34 * @var string $_newline 35 */ 36 var $_newline = "\r\n"; 37 38 /** 39 * Return a reference to a new component. 40 * 41 * @param string $type The type of component to return 42 * @param object $container A container that this component 43 * will be associated with. 44 * 45 * @return object Reference to a Horde_iCalendar_* object as specified. 46 */ 47 function &newComponent($type, &$container) 48 { 49 # require_once 'Horde/String.php'; 50 $type = strtolower($type); 51 $class = 'Horde_iCalendar_' . strtolower($type); 52 if (!class_exists($class)) { 53 include_once dirname(__FILE__) . '/iCalendar/' . $type . '.php'; 54 } 55 if (class_exists($class)) { 56 $component = &new $class(); 57 if ($container !== false) { 58 $component->_container = &$container; 59 } 60 return $component; 61 } else { 62 // Should return an dummy x-unknown type class here. 63 return false; 64 } 65 } 66 67 /** 68 * Set the value of an attribute. 69 * 70 * @param string $name The name of the attribute. 71 * @param string $value The value of the attribute. 72 * @param array $params (optional) Array containing any addition 73 * parameters for this attribute. 74 * @param boolean $append (optional) True to append the attribute, False 75 * to replace the first matching attribute found. 76 * @param array $values (optional) array representation of $value. 77 * For comma/semicolon seperated lists of values. 78 * If not set use $value as single array element. 79 */ 80 function setAttribute($name, $value, $params = array(), $append = true, $values = false) 81 { 82 $found = $append; 83 if (!$values) { 84 $values = array($value); 85 } 86 $keys = array_keys($this->_attributes); 87 foreach ($keys as $key) { 88 if ($found) break; 89 if ($this->_attributes[$key]['name'] == $name) { 90 $this->_attributes[$key]['params'] = $params; 91 $this->_attributes[$key]['value'] = $value; 92 $this->_attributes[$key]['values'] = $values; 93 $found = true; 94 } 95 } 96 97 if ($append || !$found) { 98 $this->_attributes[] = array( 99 'name' => $name, 100 'params' => $params, 101 'value' => $value, 102 'values' => $values 103 ); 104 } 105 } 106 107 /** 108 * Sets parameter(s) for an (already existing) attribute. The 109 * parameter set is merged into the existing set. 110 * 111 * @param string $name The name of the attribute. 112 * @param array $params Array containing any additional 113 * parameters for this attribute. 114 * @return boolean True on success, false if no attribute $name exists. 115 */ 116 function setParameter($name, $params) 117 { 118 $keys = array_keys($this->_attributes); 119 foreach ($keys as $key) { 120 if ($this->_attributes[$key]['name'] == $name) { 121 $this->_attributes[$key]['params'] = 122 array_merge((array)$this->_attributes[$key]['params'] , $params); 123 return true; 124 } 125 } 126 127 return false; 128 } 129 130 /** 131 * Get the value of an attribute. 132 * 133 * @param string $name The name of the attribute. 134 * @param boolean $params Return the parameters for this attribute 135 * instead of its value. 136 * 137 * @return mixed (object) PEAR_Error if the attribute does not exist. 138 * (string) The value of the attribute. 139 * (array) The parameters for the attribute or 140 * multiple values for an attribute. 141 */ 142 function getAttribute($name, $params = false) 143 { 144 $result = array(); 145 foreach ($this->_attributes as $attribute) { 146 if ($attribute['name'] == $name) { 147 if ($params) { 148 $result[] = $attribute['params']; 149 } else { 150 $result[] = $attribute['value']; 151 } 152 } 153 } 154 if (count($result) == 0) { 155 require_once 'PEAR.php'; 156 return PEAR::raiseError('Attribute "' . $name . '" Not Found'); 157 } if (count($result) == 1 && !$params) { 158 return $result[0]; 159 } else { 160 return $result; 161 } 162 } 163 164 /** 165 * Gets the values of an attribute as an array. Multiple values 166 * are possible due to: 167 * 168 * a) multiplce occurences of 'name' 169 * b) (unsecapd) comma seperated lists. 170 * 171 * So for a vcard like "KEY:a,b\nKEY:c" getAttributesValues('KEY') 172 * will return array('a','b','c'). 173 * 174 * @param string $name The name of the attribute. 175 * @return mixed (object) PEAR_Error if the attribute does not exist. 176 * (array) Multiple values for an attribute. 177 */ 178 function getAttributeValues($name) 179 { 180 $result = array(); 181 foreach ($this->_attributes as $attribute) { 182 if ($attribute['name'] == $name) { 183 $result = array_merge($attribute['values'], $result); 184 } 185 } 186 if (!count($result)) { 187 return PEAR::raiseError('Attribute "' . $name . '" Not Found'); 188 } 189 return $result; 190 } 191 192 /** 193 * Returns the value of an attribute, or a specified default value 194 * if the attribute does not exist. 195 * 196 * @param string $name The name of the attribute. 197 * @param mixed $default (optional) What to return if the attribute 198 * specified by $name does not exist. 199 * 200 * @return mixed (string) The value of $name. 201 * (mixed) $default if $name does not exist. 202 */ 203 function getAttributeDefault($name, $default = '') 204 { 205 $value = $this->getAttribute($name); 206 return is_a($value, 'PEAR_Error') ? $default : $value; 207 } 208 209 /** 210 * Remove all occurences of an attribute. 211 * 212 * @param string $name The name of the attribute. 213 */ 214 function removeAttribute($name) 215 { 216 $keys = array_keys($this->_attributes); 217 foreach ($keys as $key) { 218 if ($this->_attributes[$key]['name'] == $name) { 219 unset($this->_attributes[$key]); 220 } 221 } 222 } 223 224 /** 225 * Get attributes for all tags or for a given tag. 226 * 227 * @param string $tag (optional) return attributes for this tag. 228 * or all attributes if not given 229 * @return array Array containing all the attributes and their types. 230 */ 231 function getAllAttributes($tag = false) 232 { 233 if ($tag === false) { 234 return $this->_attributes; 235 } 236 $result = array(); 237 foreach ($this->_attributes as $attribute) { 238 if ($attribute['name'] == $tag) { 239 $result[] = $attribute; 240 } 241 } 242 return $result; 243 } 244 245 /** 246 * Add a vCalendar component (eg vEvent, vTimezone, etc.). 247 * 248 * @param object Horde_iCalendar $component Component (subclass) to add. 249 */ 250 function addComponent($component) 251 { 252 if (is_a($component, 'Horde_iCalendar')) { 253 $component->_container = &$this; 254 $this->_components[] = &$component; 255 } 256 } 257 258 /** 259 * Retrieve all the components. 260 * 261 * @return array Array of Horde_iCalendar objects. 262 */ 263 function getComponents() 264 { 265 return $this->_components; 266 } 267 268 /** 269 * Return the classes (entry types) we have. 270 * 271 * @return array Hash with class names Horde_iCalendar_xxx as keys 272 * and number of components of this class as value. 273 */ 274 function getComponentClasses() 275 { 276 $r = array(); 277 foreach ($this->_components as $c) { 278 $cn = strtolower(get_class($c)); 279 if (empty($r[$cn])) { 280 $r[$cn] = 1; 281 } else { 282 $r[$cn]++; 283 } 284 } 285 286 return $r; 287 } 288 289 /** 290 * Number of components in this container. 291 * 292 * @return integer Number of components in this container. 293 */ 294 function getComponentCount() 295 { 296 return count($this->_components); 297 } 298 299 /** 300 * Retrieve a specific component. 301 * 302 * @param integer $idx The index of the object to retrieve. 303 * 304 * @return mixed (boolean) False if the index does not exist. 305 * (Horde_iCalendar_*) The requested component. 306 */ 307 function getComponent($idx) 308 { 309 if (isset($this->_components[$idx])) { 310 return $this->_components[$idx]; 311 } else { 312 return false; 313 } 314 } 315 316 /** 317 * Locates the first child component of the specified class, and 318 * returns a reference to this component. 319 * 320 * @param string $type The type of component to find. 321 * 322 * @return mixed (boolean) False if no subcomponent of the specified 323 * class exists. 324 * (Horde_iCalendar_*) A reference to the requested component. 325 */ 326 function &findComponent($childclass) 327 { 328 # require_once 'Horde/String.php'; 329 # $childclass = 'Horde_iCalendar_' . String::lower($childclass); 330 $childclass = 'Horde_iCalendar_' . strtolower($childclass); 331 $keys = array_keys($this->_components); 332 foreach ($keys as $key) { 333 if (is_a($this->_components[$key], $childclass)) { 334 return $this->_components[$key]; 335 } 336 } 337 338 return false; 339 } 340 341 /** 342 * Clears the iCalendar object (resets the components and 343 * attributes arrays). 344 */ 345 function clear() 346 { 347 $this->_components = array(); 348 $this->_attributes = array(); 349 } 350 351 /** 352 * Export as vCalendar format. 353 */ 354 function exportvCalendar() 355 { 356 // Default values. 357 $requiredAttributes['VERSION'] = '2.0'; 358 $requiredAttributes['PRODID'] = '-//The Horde Project//Horde_iCalendar Library, Horde 3.0-cvs //EN'; 359 $requiredAttributes['METHOD'] = 'PUBLISH'; 360 361 foreach ($requiredAttributes as $name => $default_value) { 362 if (is_a($this->getattribute($name), 'PEAR_Error')) { 363 $this->setAttribute($name, $default_value); 364 } 365 } 366 367 return $this->_exportvData('VCALENDAR') . $this->_newline; 368 } 369 370 /** 371 * Export this entry as a hash array with tag names as keys. 372 * 373 * @param boolean (optional) $paramsInKeys 374 * If false, the operation can be quite lossy as the 375 * parameters are ignored when building the array keys. 376 * So if you export a vcard with 377 * LABEL;TYPE=WORK:foo 378 * LABEL;TYPE=HOME:bar 379 * the resulting hash contains only one label field! 380 * If set to true, array keys look like 'LABEL;TYPE=WORK' 381 * @return array A hash array with tag names as keys. 382 */ 383 function toHash($paramsInKeys = false) 384 { 385 $hash = array(); 386 foreach ($this->_attributes as $a) { 387 $k = $a['name']; 388 if ($paramsInKeys && is_array($a['params'])) { 389 foreach ($a['params'] as $p => $v) { 390 $k .= ";$p=$v"; 391 } 392 } 393 $hash[$k] = $a['value']; 394 } 395 396 return $hash; 397 } 398 399 /** 400 * Parse a string containing vCalendar data. 401 * 402 * @param string $text The data to parse. 403 * @param string $base The type of the base object. 404 * @param string $charset (optional) The encoding charset for $text. Defaults to utf-8 405 * @param boolean $clear (optional) True to clear() the iCal object before parsing. 406 * 407 * @return boolean True on successful import, false otherwise. 408 */ 409 function parsevCalendar($text, $base = 'VCALENDAR', $charset = 'utf-8', $clear = true) 410 { 411 if ($clear) { 412 $this->clear(); 413 } 414 if (preg_match('/(BEGIN:' . $base . '\r?\n)([\W\w]*)(END:' . $base . '\r?\n?)/i', $text, $matches)) { 415 $vCal = $matches[2]; 416 } else { 417 // Text isn't enclosed in BEGIN:VCALENDAR 418 // .. END:VCALENDAR. We'll try to parse it anyway. 419 $vCal = $text; 420 } 421 422 // All subcomponents. 423 $matches = null; 424 if (preg_match_all('/BEGIN:([\S]*)(\r\n|\r|\n)([\W\w]*)END:\1(\r\n|\r|\n)/U', $vCal, $matches)) { 425 foreach ($matches[0] as $key => $data) { 426 $type = $matches[1][$key]; 427 $component = &Horde_iCalendar::newComponent(trim($type), $this); 428 if ($component === false) { 429 return PEAR::raiseError("Unable to create object for type $type"); 430 } 431 $component->parsevCalendar($data); 432 433 $this->addComponent($component); 434 435 // Remove from the vCalendar data. 436 $vCal = str_replace($data, '', $vCal); 437 } 438 } 439 440 // Unfold any folded lines. 441 #$vCal = preg_replace ('/(\r|\n)+ /', ' ', $vCal); 442 443 // Unfold "quoted printable" folded lines like: 444 // BODY;ENCODING=QUOTED-PRINTABLE:= 445 // another=20line= 446 // last=20line 447 # Horde::logMessage("SymcML: match 1", __FILE__, __LINE__, PEAR_LOG_DEBUG); 448 # if (preg_match_all('/^([^:]+;\s*ENCODING=QUOTED-PRINTABLE(.*=[\r\n|\r|\n])+(.*[^=])[\r\n|\r|\n])/mU', $vCal, $matches)) { 449 ## if (preg_match_all('/^(BODY;ENCODING=QUOTED-PRINTABLE(.*=\r\n)+(.*)?\r?\n)/mU', $vCal, $matches)) { 450 # Horde::logMessage("SymcML: match 2", __FILE__, __LINE__, PEAR_LOG_DEBUG); 451 # foreach ($matches[1] as $s) { 452 # Horde::logMessage("SymcML: match 3 $s", __FILE__, __LINE__, PEAR_LOG_DEBUG); 453 # $r = preg_replace('/=[\r\n|\r|\n]/', '', $s); 454 # Horde::logMessage("SymcML: match 4 $r", __FILE__, __LINE__, PEAR_LOG_DEBUG); 455 # $vCal = str_replace($s, $r, $vCal); 456 # } 457 # } 458 459 // Unfold "quoted printable" folded lines like: 460 // BODY;ENCODING=QUOTED-PRINTABLE:= 461 // another=20line= 462 // last=20line 463 #if (preg_match_all('/^([^:]+;\s*ENCODING=QUOTED-PRINTABLE(.*=*\s))/mU', $vCal, $matches)) { 464 # $matches = preg_split('/=+\s/',$vCal); 465 # $vCal = implode('',$matches); 466 #} 467 if (preg_match_all('/^([^:]+;\s*ENCODING=QUOTED-PRINTABLE(.*=*\s))/mU', $vCal, $matches)) { 468 $matches = preg_split('/=(\r\n|\r|\n)/',$vCal); 469 $vCal = implode('',$matches); 470 } 471 472 // Parse the remaining attributes. 473 474 if (preg_match_all('/(.*):([^\r\n]*)[\r\n]+/', $vCal, $matches)) { 475 foreach ($matches[0] as $attribute) { 476 preg_match('/([^;^:]*)((;[^:]*)?):([^\r\n]*)[\r\n]*/', $attribute, $parts); 477 $tag = $parts[1]; 478 $value = $parts[4]; 479 $params = array(); 480 481 // Parse parameters. 482 if (!empty($parts[2])) { 483 preg_match_all('/;(([^;=]*)(=([^;]*))?)/', $parts[2], $param_parts); 484 foreach ($param_parts[2] as $key => $paramName) { 485 $paramValue = $param_parts[4][$key]; 486 $params[$paramName] = $paramValue; 487 } 488 } 489 490 // Charset and encoding handling. 491 if ((isset($params['ENCODING']) 492 && $params['ENCODING'] == 'QUOTED-PRINTABLE') 493 || isset($params['QUOTED-PRINTABLE'])) { 494 495 $value = quoted_printable_decode($value); 496 } 497 498 if (isset($params['CHARSET'])) { 499 $value = $GLOBALS['egw']->translation->convert($value, $params['CHARSET']); 500 } else { 501 // As per RFC 2279, assume UTF8 if we don't have 502 // an explicit charset parameter. 503 $value = $GLOBALS['egw']->translation->convert($value, 'utf-8'); 504 } 505 506 switch ($tag) { 507 // Date fields. 508 case 'DTSTAMP': 509 case 'COMPLETED': 510 case 'CREATED': 511 case 'LAST-MODIFIED': 512 case 'BDAY': 513 $this->setAttribute($tag, $this->_parseDateTime($value), $params); 514 break; 515 516 case 'DTEND': 517 case 'DTSTART': 518 case 'DUE': 519 case 'RECURRENCE-ID': 520 if (isset($params['VALUE']) && $params['VALUE'] == 'DATE') { 521 $this->setAttribute($tag, $this->_parseDate($value), $params); 522 } else { 523 $this->setAttribute($tag, $this->_parseDateTime($value), $params); 524 } 525 break; 526 527 case 'RDATE': 528 if (isset($params['VALUE'])) { 529 if ($params['VALUE'] == 'DATE') { 530 $this->setAttribute($tag, $this->_parseDate($value), $params); 531 } elseif ($params['VALUE'] == 'PERIOD') { 532 $this->setAttribute($tag, $this->_parsePeriod($value), $params); 533 } else { 534 $this->setAttribute($tag, $this->_parseDateTime($value), $params); 535 } 536 } else { 537 $this->setAttribute($tag, $this->_parseDateTime($value), $params); 538 } 539 break; 540 541 case 'TRIGGER': 542 if (isset($params['VALUE'])) { 543 if ($params['VALUE'] == 'DATE-TIME') { 544 $this->setAttribute($tag, $this->_parseDateTime($value), $params); 545 } else { 546 $this->setAttribute($tag, $this->_parseDuration($value), $params); 547 } 548 } else { 549 $this->setAttribute($tag, $this->_parseDuration($value), $params); 550 } 551 break; 552 553 // Comma seperated dates. 554 case 'EXDATE': 555 $values = array(); 556 $dates = array(); 557 preg_match_all('/;([^;]*)/', ';' . $value, $values); 558 559 foreach ($values[1] as $value) { 560 if (isset($params['VALUE'])) { 561 if ($params['VALUE'] == 'DATE-TIME') { 562 $dates[] = $this->_parseDateTime($value); 563 } elseif ($params['VALUE'] == 'DATE') { 564 $dates[] = $this->_parseDate($value); 565 } 566 } else { 567 $dates[] = $this->_parseDateTime($value); 568 } 569 } 570 $this->setAttribute($tag, $dates, $params); 571 break; 572 573 // Duration fields. 574 case 'DURATION': 575 $this->setAttribute($tag, $this->_parseDuration($value), $params); 576 break; 577 578 // Period of time fields. 579 case 'FREEBUSY': 580 $values = array(); 581 $periods = array(); 582 preg_match_all('/,([^,]*)/', ',' . $value, $values); 583 foreach ($values[1] as $value) { 584 $periods[] = $this->_parsePeriod($value); 585 } 586 587 $this->setAttribute($tag, $periods, $params); 588 break; 589 590 // UTC offset fields. 591 case 'TZOFFSETFROM': 592 case 'TZOFFSETTO': 593 $this->setAttribute($tag, $this->_parseUtcOffset($value), $params); 594 break; 595 596 // Integer fields. 597 case 'PERCENT-COMPLETE': 598 case 'PRIORITY': 599 case 'REPEAT': 600 case 'SEQUENCE': 601 $this->setAttribute($tag, intval($value), $params); 602 break; 603 604 // Geo fields. 605 case 'GEO': 606 $floats = split(';', $value); 607 $value['latitude'] = floatval($floats[0]); 608 $value['longitude'] = floatval($floats[1]); 609 $this->setAttribute($tag, $value, $params); 610 break; 611 612 // Recursion fields. 613 case 'EXRULE': 614 case 'RRULE': 615 $this->setAttribute($tag, trim($value), $params); 616 break; 617 618 // ADR an N are lists seperated by unescaped semi-colons. 619 case 'ADR': 620 case 'N': 621 case 'ORG': 622 623 $value = trim($value); 624 // As of rfc 2426 2.4.2 semi-colon, comma, and 625 // colon must be escaped. 626 $value = str_replace('\\n', $this->_newline, $value); 627 $value = str_replace('\\,', ',', $value); 628 $value = str_replace('\\:', ':', $value); 629 630 // Split by unescaped semi-colons: 631 $values = preg_split('/(?<!\\\\);/',$value); 632 $value = str_replace('\\;', ';', $value); 633 $values = str_replace('\\;', ';', $values); 634 $this->setAttribute($tag, trim($value), $params, true, $values); 635 636 break; 637 638 // String fields. 639 default: 640 $value = trim($value); 641 // As of rfc 2426 2.4.2 semi-colon, comma, and 642 // colon must be escaped. 643 $value = str_replace('\\n', $this->_newline, $value); 644 $value = str_replace('\\;', ';', $value); 645 $value = str_replace('\\:', ':', $value); 646 647 // Split by unescaped commas: 648 $values = preg_split('/(?<!\\\\),/',$value); 649 $value = str_replace('\\,', ',', $value); 650 $values = str_replace('\\,', ',', $values); 651 652 $this->setAttribute($tag, trim($value), $params, true, $values); 653 break; 654 } 655 } 656 } 657 658 return true; 659 } 660 661 /** 662 * Export this component in vCal format. 663 * 664 * @param string $base (optional) The type of the base object. 665 * 666 * @return string vCal format data. 667 */ 668 function _exportvData($base = 'VCALENDAR') 669 { 670 $result = 'BEGIN:' . strtoupper($base) . $this->_newline; 671 672 // Ensure that version is the first attribute. 673 $v = $this->getAttributeDefault('VERSION', false); 674 if ($v) { 675 $result .= 'VERSION:' . $v. $this->_newline; 676 } 677 678 foreach ($this->_attributes as $attribute) { 679 $name = $attribute['name']; 680 if ($name == 'VERSION') { 681 // Already done. 682 continue; 683 } 684 685 $params = $attribute['params']; 686 $params_str = ''; 687 688 if (count($params)) { 689 foreach ($params as $param_name => $param_value) { 690 $params_str .= ";$param_name=$param_value"; 691 } 692 } 693 694 $value = $attribute['value']; 695 696 switch ($name) { 697 // Date fields. 698 case 'DTSTAMP': 699 case 'COMPLETED': 700 case 'CREATED': 701 case 'DCREATED': 702 case 'LAST-MODIFIED': 703 $value = $this->_exportDateTime($value); 704 break; 705 706 case 'DTEND': 707 case 'DTSTART': 708 case 'DUE': 709 case 'RECURRENCE-ID': 710 if (isset($params['VALUE'])) { 711 if ($params['VALUE'] == 'DATE') { 712 $value = $this->_exportDate($value); 713 } else { 714 $value = $this->_exportDateTime($value); 715 } 716 } else { 717 $value = $this->_exportDateTime($value); 718 } 719 break; 720 721 case 'RDATE': 722 if (isset($params['VALUE'])) { 723 if ($params['VALUE'] == 'DATE') { 724 $value = $this->_exportDate($value); 725 } elseif ($params['VALUE'] == 'PERIOD') { 726 $value = $this->_exportPeriod($value); 727 } else { 728 $value = $this->_exportDateTime($value); 729 } 730 } else { 731 $value = $this->_exportDateTime($value); 732 } 733 break; 734 735 case 'TRIGGER': 736 if (isset($params['VALUE'])) { 737 if ($params['VALUE'] == 'DATE-TIME') { 738 $value = $this->_exportDateTime($value); 739 } elseif ($params['VALUE'] == 'DURATION') { 740 $value = $this->_exportDuration($value); 741 } 742 } else { 743 $value = $this->_exportDuration($value); 744 } 745 break; 746 747 // Duration fields. 748 case 'DURATION': 749 $value = $this->_exportDuration($value); 750 break; 751 752 // Period of time fields. 753 case 'FREEBUSY': 754 $value_str = ''; 755 foreach ($value as $period) { 756 $value_str .= empty($value_str) ? '' : ','; 757 $value_str .= $this->_exportPeriod($period); 758 } 759 $value = $value_str; 760 break; 761 762 // UTC offset fields. 763 case 'TZOFFSETFROM': 764 case 'TZOFFSETTO': 765 $value = $this->_exportUtcOffset($value); 766 break; 767 768 // Integer fields. 769 case 'PERCENT-COMPLETE': 770 case 'PRIORITY': 771 case 'REPEAT': 772 case 'SEQUENCE': 773 $value = "$value"; 774 break; 775 776 // Geo fields. 777 case 'GEO': 778 $value = $value['latitude'] . ',' . $value['longitude']; 779 break; 780 781 // Recurrence fields. 782 case 'EXRULE': 783 case 'RRULE': 784 785 //Text Fields 786 case 'SUMMARY': 787 case 'DESCRIPTION': 788 case 'COMMENT': 789 $value = str_replace('\\', '\\\\', $value); 790 $value = str_replace($this->_newline, '\n', $value); 791 $value = str_replace(',', '\,', $value); 792 $value = str_replace(';', '\;', $value); 793 $value = str_replace(':', '\:', $value); 794 break; 795 796 default: 797 break; 798 } 799 800 if (!empty($params['ENCODING']) && 801 $params['ENCODING'] == 'QUOTED-PRINTABLE' && strlen(trim($value)) > 0) { 802 $value = str_replace("\r", '', $value); 803 # $result .= "$name$params_str:=" . $this->_newline 804 # . $this->_quotedPrintableEncode($value) 805 # . $this->_newline; 806 $result .= "$name$params_str:" 807 . $this->_quotedPrintableEncode($value) 808 . $this->_newline; 809 } else { 810 # JVL: prevent : for empty values 811 # $attr_string = "$name$params_str:$value"; 812 $attr_string = "$name$params_str"; 813 $attr_string .= (!empty($value)) ? ":$value" : ';'; 814 815 $result .= $this->_foldLine($attr_string) . $this->_newline; 816 } 817 } 818 819 foreach ($this->getComponents() as $component) { 820 $result .= $component->exportvCalendar() . $this->_newline; 821 } 822 823 $result .= 'END:' . $base; 824 825 return $result; 826 } 827 828 /** 829 * Parse a UTC Offset field. 830 */ 831 function _parseUtcOffset($text) 832 { 833 $offset = array(); 834 if (preg_match('/(\+|-)([0-9]{2})([0-9]{2})([0-9]{2})?/', $text, $timeParts)) { 835 $offset['ahead'] = (boolean)($timeParts[1] == '+'); 836 $offset['hour'] = intval($timeParts[2]); 837 $offset['minute'] = intval($timeParts[3]); 838 if (isset($timeParts[4])) { 839 $offset['second'] = intval($timeParts[4]); 840 } 841 return $offset; 842 } else { 843 return false; 844 } 845 } 846 847 /** 848 * Export a UTC Offset field. 849 */ 850 function _exportUtcOffset($value) 851 { 852 $offset = $value['ahead'] ? '+' : '-'; 853 $offset .= sprintf('%02d%02d', 854 $value['hour'], $value['minute']); 855 if (isset($value['second'])) { 856 $offset .= sprintf('%02d', $value['second']); 857 } 858 859 return $offset; 860 } 861 862 /** 863 * Parse a Time Period field. 864 */ 865 function _parsePeriod($text) 866 { 867 $periodParts = split('/', $text); 868 869 $start = $this->_parseDateTime($periodParts[0]); 870 871 if ($duration = $this->_parseDuration($periodParts[1])) { 872 return array('start' => $start, 'duration' => $duration); 873 } elseif ($end = $this->_parseDateTime($periodParts[1])) { 874 return array('start' => $start, 'end' => $end); 875 } 876 } 877 878 /** 879 * Export a Time Period field. 880 */ 881 function _exportPeriod($value) 882 { 883 $period = $this->_exportDateTime($value['start']); 884 $period .= '/'; 885 if (isset($value['duration'])) { 886 $period .= $this->_exportDuration($value['duration']); 887 } else { 888 $period .= $this->_exportDateTime($value['end']); 889 } 890 return $period; 891 } 892 893 /** 894 * Parse a DateTime field into a unix timestamp. 895 */ 896 function _parseDateTime($text) 897 { 898 $dateParts = split('T', $text); 899 if (count($dateParts) != 2 && !empty($text)) { 900 // Not a datetime field but may be just a date field. 901 if (!$date = $this->_parseDate($text)) { 902 return $date; 903 } 904 return @gmmktime(0, 0, 0, $date['month'], $date['mday'], $date['year']); 905 } 906 907 if (!$date = $this->_parseDate($dateParts[0])) { 908 return $date; 909 } 910 if (!$time = $this->_parseTime($dateParts[1])) { 911 return $time; 912 } 913 914 if ($time['zone'] == 'UTC') { 915 return @gmmktime($time['hour'], $time['minute'], $time['second'], 916 $date['month'], $date['mday'], $date['year']); 917 } else { 918 return @mktime($time['hour'], $time['minute'], $time['second'], 919 $date['month'], $date['mday'], $date['year']); 920 } 921 } 922 923 /** 924 * Export a DateTime field. 925 */ 926 function _exportDateTime($value) 927 { 928 $temp = array(); 929 if (!is_object($value) || is_array($value)) { 930 $TZOffset = 3600 * substr(date('O',$value), 0, 3); 931 $TZOffset += 60 * substr(date('O',$value), 3, 2); 932 $value -= $TZOffset; 933 934 $temp['zone'] = 'UTC'; 935 $temp['year'] = date('Y', $value); 936 $temp['month'] = date('n', $value); 937 $temp['mday'] = date('j', $value); 938 $temp['hour'] = date('G', $value); 939 $temp['minute'] = date('i', $value); 940 $temp['second'] = date('s', $value); 941 } else { 942 $dateOb = (object)$value; 943 944 $TZOffset = date('O',mktime($dateOb->hour,$dateOb->min,$dateOb->sec,$dateOb->month,$dateOb->mday,$dateOb->year)); 945 946 // Minutes. 947 $TZOffsetMin = substr($TZOffset, 0, 1) . substr($TZOffset, 3, 2); 948 $thisMin = $dateOb->min - $TZOffsetMin; 949 950 // Hours. 951 $TZOffsetHour = substr($TZOffset, 0, 3); 952 $thisHour = $dateOb->hour - $TZOffsetHour; 953 954 if ($thisMin < 0) { 955 $thisHour -= 1; 956 $thisMin += 60; 957 } 958 959 if ($thisHour < 0) { 960 require_once 'Date/Calc.php'; 961 $prevday = Date_Calc::prevDay($dateOb->mday, $dateOb->month, $dateOb->year); 962 $dateOb->mday = substr($prevday, 6, 2); 963 $dateOb->month = substr($prevday, 4, 2); 964 $dateOb->year = substr($prevday, 0, 4); 965 $thisHour += 24; 966 } 967 968 $temp['zone'] = 'UTC'; 969 $temp['year'] = $dateOb->year; 970 $temp['month'] = $dateOb->month; 971 $temp['mday'] = $dateOb->mday; 972 $temp['hour'] = $thisHour; 973 $temp['minute'] = $dateOb->min; 974 $temp['second'] = $dateOb->sec; 975 } 976 977 return Horde_iCalendar::_exportDate($temp) . 'T' . Horde_iCalendar::_exportTime($temp); 978 } 979 980 /** 981 * Parse a Time field. 982 */ 983 function _parseTime($text) 984 { 985 if (preg_match('/([0-9]{2})([0-9]{2})([0-9]{2})(Z)?/', $text, $timeParts)) { 986 $time['hour'] = intval($timeParts[1]); 987 $time['minute'] = intval($timeParts[2]); 988 $time['second'] = intval($timeParts[3]); 989 if (isset($timeParts[4])) { 990 $time['zone'] = 'UTC'; 991 } else { 992 $time['zone'] = 'Local'; 993 } 994 return $time; 995 } else { 996 return false; 997 } 998 } 999 1000 /** 1001 * Export a Time field. 1002 */ 1003 function _exportTime($value) 1004 { 1005 $time = sprintf('%02d%02d%02d', 1006 $value['hour'], $value['minute'], $value['second']); 1007 if ($value['zone'] == 'UTC') { 1008 $time .= 'Z'; 1009 } 1010 return $time; 1011 } 1012 1013 /** 1014 * Parse a Date field. 1015 */ 1016 function _parseDate($text) 1017 { 1018 if (strlen($text) != 8) { 1019 return false; 1020 } 1021 1022 $date['year'] = intval(substr($text, 0, 4)); 1023 $date['month'] = intval(substr($text, 4, 2)); 1024 $date['mday'] = intval(substr($text, 6, 2)); 1025 1026 return $date; 1027 } 1028 1029 /** 1030 * Export a Date field. 1031 */ 1032 function _exportDate($value) 1033 { 1034 return sprintf('%04d%02d%02d', 1035 $value['year'], $value['month'], $value['mday']); 1036 } 1037 1038 /** 1039 * Parse a Duration Value field. 1040 */ 1041 function _parseDuration($text) 1042 { 1043 if (preg_match('/([+]?|[-])P(([0-9]+W)|([0-9]+D)|)(T(([0-9]+H)|([0-9]+M)|([0-9]+S))+)?/', trim($text), $durvalue)) { 1044 // Weeks. 1045 $duration = 7 * 86400 * intval($durvalue[3]); 1046 1047 if (count($durvalue) > 4) { 1048 // Days. 1049 $duration += 86400 * intval($durvalue[4]); 1050 } 1051 if (count($durvalue) > 5) { 1052 // Hours. 1053 $duration += 3600 * intval($durvalue[7]); 1054 1055 // Mins. 1056 if (isset($durvalue[8])) { 1057 $duration += 60 * intval($durvalue[8]); 1058 } 1059 1060 // Secs. 1061 if (isset($durvalue[9])) { 1062 $duration += intval($durvalue[9]); 1063 } 1064 } 1065 1066 // Sign. 1067 if ($durvalue[1] == "-") { 1068 $duration *= -1; 1069 } 1070 1071 return $duration; 1072 } else { 1073 return false; 1074 } 1075 } 1076 1077 /** 1078 * Export a duration value. 1079 */ 1080 function _exportDuration($value) 1081 { 1082 $duration = ''; 1083 if ($value < 0) { 1084 $value *= -1; 1085 $duration .= '-'; 1086 } 1087 $duration .= 'P'; 1088 1089 $weeks = floor($value / (7 * 86400)); 1090 $value = $value % (7 * 86400); 1091 if ($weeks) { 1092 $duration .= $weeks . 'W'; 1093 } 1094 1095 $days = floor($value / (86400)); 1096 $value = $value % (86400); 1097 if ($days) { 1098 $duration .= $days . 'D'; 1099 } 1100 1101 if ($value) { 1102 $duration .= 'T'; 1103 1104 $hours = floor($value / 3600); 1105 $value = $value % 3600; 1106 if ($hours) { 1107 $duration .= $hours . 'H'; 1108 } 1109 1110 $mins = floor($value / 60); 1111 $value = $value % 60; 1112 if ($mins) { 1113 $duration .= $mins . 'M'; 1114 } 1115 1116 if ($value) { 1117 $duration .= $value . 'S'; 1118 } 1119 } 1120 1121 return $duration; 1122 } 1123 1124 /** 1125 * Return the folded version of a line. 1126 * JVL rewritten to fold on any ; or: or = if present before column 75 1127 * this is still rfc2445 section 4.1 compliant 1128 */ 1129 function _foldLine($line) 1130 { 1131 $line = preg_replace("/\r\n|\n|\r/", '\n', $line); 1132 if (strlen($line) > 75) { 1133 $foldedline = ''; 1134 while (!empty($line)) { 1135 $maxLine = substr($line, 0, 75); 1136 $cutPoint = 1+max(is_numeric($p1 = strrpos($maxLine,';')) ? $p1 : -1, 1137 is_numeric($p1 = strrpos($maxLine,':')) ? $p1 : -1, 1138 is_numeric($p1 = strrpos($maxLine,'=')) ? $p1 : -1); 1139 if ($cutPoint < 1) // nothing found, then fold complete maxLine 1140 $cutPoint = 75; 1141 // now fold [0..(cutPoint-1)] 1142 $foldedline .= (empty($foldedline)) 1143 ? substr($line, 0, $cutPoint) 1144 : $this->_newline . ' ' . substr($line, 0, $cutPoint); 1145 1146 $line = (strlen($line) <= $cutPoint) 1147 ? '' 1148 : substr($line, $cutPoint); 1149 1150 if (strlen($line) < 75) { 1151 $foldedline .= $this->_newline . ' ' . $line; 1152 $line = ''; 1153 } 1154 1155 } 1156 return $foldedline; 1157 } 1158 return $line; 1159 } 1160 1161 /** 1162 * Convert an 8bit string to a quoted-printable string according 1163 * to RFC2045, section 6.7. 1164 * 1165 * Uses imap_8bit if available. 1166 * 1167 * @param string $input The string to be encoded. 1168 * 1169 * @return string The quoted-printable encoded string. 1170 */ 1171 function _quotedPrintableEncode($input = '') 1172 { 1173 return $this->EncodeQP($input); 1174 1175 #$input = preg_replace('!(\r\n|\r|\n)!',"\n",$input); 1176 1177 // If imap_8bit() is available, use it. 1178 if (function_exists('imap_8bit')) { 1179 $retValue = imap_8bit($input); 1180 #$retValue = preg_replace('/=0A/',"=0D=0A=\r\n",$retValue); 1181 return $retValue; 1182 } 1183 1184 // Rather dumb replacment: just encode everything. 1185 $hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 1186 'A', 'B', 'C', 'D', 'E', 'F'); 1187 1188 $output = ''; 1189 $len = strlen($input); 1190 for ($i = 0; $i < $len; ++$i) { 1191 $c = substr($input, $i, 1); 1192 $dec = ord($c); 1193 $output .= '=' . $hex[floor($dec / 16)] . $hex[floor($dec % 16)]; 1194 if (($i + 1) % 25 == 0) { 1195 $output .= "=\r\n"; 1196 } 1197 } 1198 return $output; 1199 } 1200 var $LE = "\r\n"; 1201 1202 /** 1203 * Encode string to quoted-printable. 1204 * @access private 1205 * @return string 1206 */ 1207 function EncodeQP_old ($str) { 1208 $encoded = $this->FixEOL($str); 1209 #$encoded = $str; 1210 #if (substr($encoded, -(strlen($this->LE))) != $this->LE) 1211 # $encoded .= $this->LE; 1212 1213 // Replace every high ascii, control and = characters 1214 #$encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e', 1215 # "'='.sprintf('%02X', ord('\\1'))", $encoded); 1216 $encoded = preg_replace('/([\000-\012\015\016\020-\037\075\177-\377])/e', 1217 "'='.sprintf('%02X', ord('\\1'))", $encoded); 1218 // Replace every spaces and tabs when it's the last character on a line 1219 #$encoded = preg_replace("/([\011\040])".$this->LE."/e", 1220 # "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded); 1221 $encoded = preg_replace("/([\011\040])".$this->LE."/e", 1222 "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded); 1223 1224 // Maximum line length of 76 characters before CRLF (74 + space + '=') 1225 $encoded = $this->WrapText($encoded, 74, true); 1226 1227 return $encoded; 1228 } 1229 1230 /** 1231 * Wraps message for use with mailers that do not 1232 * automatically perform wrapping and for quoted-printable. 1233 * Original written by philippe. 1234 * @access private 1235 * @return string 1236 */ 1237 function WrapText_old($message, $length, $qp_mode = false) { 1238 $soft_break = ($qp_mode) ? "=\r\n" : $this->LE; 1239 1240 #$message = $this->FixEOL($message); 1241 if (substr($message, -1) == $this->LE) 1242 $message = substr($message, 0, -1); 1243 1244 $line = explode("=0D=0A", $message); 1245 $message = ""; 1246 for ($i=0 ;$i < count($line); $i++) 1247 { 1248 $line_part = explode(" ", $line[$i]); 1249 $buf = ""; 1250 for ($e = 0; $e<count($line_part); $e++) 1251 { 1252 $word = $line_part[$e]; 1253 if ($qp_mode and (strlen($word) > $length)) 1254 { 1255 $space_left = $length - strlen($buf) - 1; 1256 if ($e != 0) 1257 { 1258 if ($space_left > 20) 1259 { 1260 $len = $space_left; 1261 if (substr($word, $len - 1, 1) == "=") 1262 $len--; 1263 elseif (substr($word, $len - 2, 1) == "=") 1264 $len -= 2; 1265 $part = substr($word, 0, $len); 1266 $word = substr($word, $len); 1267 $buf .= " " . $part; 1268 $message .= $buf . sprintf("=%s", $this->LE); 1269 } 1270 else 1271 { 1272 $message .= $buf . $soft_break; 1273 } 1274 $buf = ""; 1275 } 1276 while (strlen($word) > 0) 1277 { 1278 $len = $length; 1279 if (substr($word, $len - 1, 1) == "=") 1280 $len--; 1281 elseif (substr($word, $len - 2, 1) == "=") 1282 $len -= 2; 1283 $part = substr($word, 0, $len); 1284 $word = substr($word, $len); 1285 1286 if (strlen($word) > 0) 1287 $message .= $part . sprintf("=%s", $this->LE); 1288 else 1289 $buf = $part; 1290 } 1291 } 1292 else 1293 { 1294 $buf_o = $buf; 1295 $buf .= ($e == 0) ? $word : (" " . $word); 1296 1297 if (strlen($buf) > $length and $buf_o != "") 1298 { 1299 $message .= $buf_o . $soft_break; 1300 $buf = $word; 1301 } 1302 } 1303 } 1304 $message .= $buf; 1305 if((count($line)-1) > $i) 1306 $message .= "=0D=0A=\r\n"; 1307 } 1308 1309 return $message; 1310 } 1311 /** 1312 * Changes every end of line from CR or LF to CRLF. 1313 * @access private 1314 * @return string 1315 */ 1316 function FixEOL($str) { 1317 $str = str_replace("\r\n", "\n", $str); 1318 $str = str_replace("\r", "\n", $str); 1319 $str = str_replace("\n", $this->LE, $str); 1320 return $str; 1321 } 1322 1323 /** 1324 * Encode string to quoted-printable. 1325 * @access private 1326 * @return string 1327 */ 1328 function EncodeQP ($str) { 1329 $encoded = $this->FixEOL($str); 1330 # see bugreport http://sourceforge.net/tracker/index.php?func=detail&aid=1536674&group_id=78745&atid=554338 1331 //if (substr($encoded, -(strlen($this->LE))) != $this->LE) 1332 // $encoded .= $this->LE; 1333 1334 // Replace every high ascii, control and = characters 1335 #$encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e', 1336 # "'='.sprintf('%02X', ord('\\1'))", $encoded); 1337 $encoded = preg_replace('/([\000-\012\015\016\020-\037\075\177-\377])/e', 1338 "'='.sprintf('%02X', ord('\\1'))", $encoded); 1339 // Replace every spaces and tabs when it's the last character on a line 1340 $encoded = preg_replace("/([\011\040])".$this->LE."/e", 1341 "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded); 1342 1343 // Maximum line length of 76 characters before CRLF (74 + space + '=') 1344 #$encoded = $this->WrapText($encoded, 74, true); 1345 1346 return $encoded; 1347 } 1348 1349 /** 1350 * Wraps message for use with mailers that do not 1351 * automatically perform wrapping and for quoted-printable. 1352 * Original written by philippe. 1353 * @access private 1354 * @return string 1355 */ 1356 function WrapText($message, $length, $qp_mode = false) { 1357 $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; 1358 $soft_break = "..="; 1359 1360 $message = $this->FixEOL($message); 1361 if (substr($message, -1) == $this->LE) 1362 $message = substr($message, 0, -1); 1363 1364 $line = explode($this->LE, $message); 1365 $message = ""; 1366 for ($i=0 ;$i < count($line); $i++) 1367 { 1368 $line_part = explode(" ", $line[$i]); 1369 $buf = ""; 1370 for ($e = 0; $e<count($line_part); $e++) 1371 { 1372 $word = $line_part[$e]; 1373 if ($qp_mode and (strlen($word) > $length)) 1374 { 1375 $space_left = $length - strlen($buf) - 1; 1376 if ($e != 0) 1377 { 1378 if ($space_left > 20) 1379 { 1380 $len = $space_left; 1381 if (substr($word, $len - 1, 1) == "=") 1382 $len--; 1383 elseif (substr($word, $len - 2, 1) == "=") 1384 $len -= 2; 1385 $part = substr($word, 0, $len); 1386 $word = substr($word, $len); 1387 $buf .= " " . $part; 1388 $message .= $buf . sprintf("=%s", $this->LE); 1389 } 1390 else 1391 { 1392 $message .= $buf . $soft_break; 1393 } 1394 $buf = ""; 1395 } 1396 while (strlen($word) > 0) 1397 { 1398 $len = $length; 1399 if (substr($word, $len - 1, 1) == "=") 1400 $len--; 1401 elseif (substr($word, $len - 2, 1) == "=") 1402 $len -= 2; 1403 $part = substr($word, 0, $len); 1404 $word = substr($word, $len); 1405 1406 if (strlen($word) > 0) 1407 $message .= $part . sprintf("=%s", $this->LE); 1408 else 1409 $buf = $part; 1410 } 1411 } 1412 else 1413 { 1414 $buf_o = $buf; 1415 $buf .= ($e == 0) ? $word : (" " . $word); 1416 1417 if (strlen($buf) > $length and $buf_o != "") 1418 { 1419 $message .= $buf_o . $soft_break; 1420 $buf = $word; 1421 } 1422 } 1423 } 1424 $message .= $buf . $this->LE; 1425 } 1426 1427 return $message; 1428 } 1429 1430 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 17:20:01 2007 | par Balluche grâce à PHPXref 0.7 |