1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575:
<?php
require_once('XMLElement.php');
class vProperty extends vObject {
protected $name;
protected $parameters;
protected $content;
protected $iterator;
protected $seek;
protected $line;
function __construct( $name = null, &$master = null, &$refData = null, $seek = null ) {
parent::__construct($master);
if(isset($name) && strlen($name) > 0){
$this->name = $name;
} else {
unset($this->name);
}
unset($this->content);
unset($this->parameters);
if ( isset($refData)){
if(gettype($refData) == 'object') {
$this->iterator = &$refData;
$this->seek = &$seek;
unset($this->line);
} else {
$this->line = $refData;
unset($this->iterator);
unset($this->seek);
}
} else {
unset($this->iterator);
unset($this->seek);
}
}
function ParseFromIterator()
{
$unescaped;
if (isset($this->iterator)) {
$this->iterator->seek($this->seek);
$unescaped = $this->iterator->current();
} else if (isset($this->line)) {
$unescaped = $this->line;
} else {
$unescaped = '';
}
$this->ParseFrom($unescaped);
unset($unescaped);
}
function ParseFrom( &$unescaped ) {
$unescaped = preg_replace( array('{\\\\[nN]}', '{\\\\[rR]}'), array("\n", "\r"), $unescaped);
$offset = 0;
do {
$splitpos = strpos($unescaped,':',$offset);
$start = substr($unescaped,0,$splitpos);
if ( substr($start,-1) == '\\' ) {
$offset = $splitpos + 1;
continue;
}
$quotecount = strlen(preg_replace('{[^"]}', '', $start ));
if ( ($quotecount % 2) != 0 ) {
$offset = $splitpos + 1;
continue;
}
break;
}
while( true );
$values = substr($unescaped, $splitpos+1);
$possiblecontent = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $values);
if(!isset($this->content)){
$len = strlen($possiblecontent);
if($len > 0 && "\r" == $possiblecontent[$len-1]){
$possiblecontent = substr($possiblecontent, 0, $len-1);
}
$this->content = $possiblecontent;
}
$parameters = preg_split( '{(?<!\\\\);}', $start);
$possiblename = strtoupper(array_shift( $parameters ));
if(!isset($this->name)){
$this->name = $possiblename;
}
if(!isset($this->parameters)){
$this->parameters = array();
foreach( $parameters AS $k => $v ) {
$pos = strpos($v,'=');
if($pos !== FALSE) {
$name = strtoupper(substr( $v, 0, $pos));
$value = substr( $v, $pos + 1);
}
else {
$name = strtoupper($v);
$value = null;
}
if ( preg_match( '{^"(.*)"$}', $value, $matches) ) {
$value = $matches[1];
}
if ( isset($this->parameters[$name]) && is_array($this->parameters[$name]) ) {
$this->parameters[$name][] = $value;
}
elseif ( isset($this->parameters[$name]) ) {
$this->parameters[$name] = array( $this->parameters[$name], $value);
}
else
$this->parameters[$name] = $value;
}
}
}
function Name( $newname = null ) {
if ( $newname != null ) {
$this->name = strtoupper($newname);
if ( $this->isValid() ) $this->invalidate();
} else if(!isset($this->name)){
$this->ParseFromIterator();
}
return $this->name;
}
function Value( $newvalue = null ) {
if ( $newvalue != null ) {
$this->content = $newvalue;
if ( $this->isValid() ) $this->invalidate();
} else if(!isset($this->content)){
$this->ParseFromIterator();
}
return $this->content;
}
function Parameters( $newparams = null ) {
if ( $newparams != null ) {
$this->parameters = array();
foreach( $newparams AS $k => $v ) {
$this->parameters[strtoupper($k)] = $v;
}
if ( $this->isValid() ) $this->invalidate();
} else if(!isset($this->parameters)){
$this->ParseFromIterator();
}
return $this->parameters;
}
function TextMatch( $search ) {
if ( isset($this->content) ) return strstr( $this->content, $search );
return false;
}
function GetParameterValue( $name ) {
$name = strtoupper($name);
if(!isset($this->parameters)){
$this->ParseFromIterator();
}
if ( isset($this->parameters[$name]) ){
return $this->parameters[$name];
}
return null;
}
function SetParameterValue( $name, $value ) {
if(!isset($this->parameters)){
$this->ParseFromIterator();
}
if ( $this->isValid() ) {
$this->invalidate();
}
$this->parameters[strtoupper($name)] = $value;
}
function ClearParameters( $type = null ) {
if(!isset($this->parameters)){
$this->ParseFromIterator();
}
if ( $this->isValid() ) {
$this->invalidate();
}
if ( $type != null ) {
$testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
foreach( $this->parameters AS $k => $v ) {
if ( isset($testtypes[$k]) && $testtypes[$k] ) {
unset($this->parameters[$k]);
}
}
}
}
private static function escapeParameter($p) {
if ( strpos($p, ';') === false && strpos($p, ':') === false ) return $p;
return '"'.str_replace('"','\\"',$p).'"';
}
function RenderParameters() {
$rendered = "";
if(isset($this->parameters)){
foreach( $this->parameters AS $k => $v ) {
if ( is_array($v) ) {
foreach( $v AS $vv ) {
$rendered .= sprintf( ';%s=%s', $k, vProperty::escapeParameter($vv) );
}
}
else {
if($v !== null) {
$rendered .= sprintf( ';%s=%s', $k, vProperty::escapeParameter($v) );
}
else {
$rendered .= sprintf( ';%s', $k);
}
}
}
}
return $rendered;
}
function Render( $force = false ) {
if(!isset($this->name) || !isset($this->content) || !isset($this->parameters)) {
$this->ParseFromIterator();
}
$property = preg_replace( '/[;].*$/', '', $this->name );
$escaped = $this->content;
$property = preg_replace( '/^.*[.]/', '', $property );
switch( $property ) {
case 'ATTACH': case 'GEO': case 'PERCENT-COMPLETE': case 'PRIORITY':
case 'DURATION': case 'FREEBUSY': case 'TZOFFSETFROM': case 'TZOFFSETTO':
case 'TZURL': case 'ATTENDEE': case 'ORGANIZER': case 'RECURRENCE-ID':
case 'URL': case 'EXRULE': case 'SEQUENCE': case 'CREATED':
case 'RRULE': case 'REPEAT': case 'TRIGGER': case 'RDATE':
case 'COMPLETED': case 'DTEND': case 'DUE': case 'DTSTART':
case 'DTSTAMP': case 'LAST-MODIFIED': case 'CREATED': case 'EXDATE':
case 'CATEGORIES' :
break;
case 'ADR': case 'N': case 'ORG':
$escaped = str_replace( '\\', '\\\\', $escaped);
$escaped = preg_replace( '/\r?\n/', '\\n', $escaped);
$escaped = str_replace( ',', '\\,', $escaped);
break;
default:
$escaped = str_replace( '\\', '\\\\', $escaped);
$escaped = preg_replace( '/\r?\n/', '\\n', $escaped);
$escaped = preg_replace( "/([,;])/", '\\\\$1', $escaped);
}
$rendered = '';
$property = sprintf( "%s%s:", $this->name, $this->RenderParameters() );
if ( (strlen($property) + strlen($escaped)) <= 72 ) {
$rendered = $property . $escaped;
}
else if ( (strlen($property) <= 72) && (strlen($escaped) <= 72) ) {
$rendered = $property . "\r\n " . $escaped;
}
else {
$rendered = preg_replace( '/(.{72})/u', '$1'."\r\n ", $property.$escaped );
}
return $rendered;
}
public function __toString() {
return $this->Render();
}
function TestFilter( $filters ) {
foreach( $filters AS $k => $v ) {
$tag = $v->GetNSTag();
switch( $tag ) {
case 'urn:ietf:params:xml:ns:caldav:is-defined':
case 'urn:ietf:params:xml:ns:carddav:is-defined':
if ( empty($this->content) ) return false;
break;
case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
case 'urn:ietf:params:xml:ns:carddav:is-not-defined':
if ( ! empty($this->content) ) return false;
break;
case 'urn:ietf:params:xml:ns:caldav:time-range':
break;
case 'urn:ietf:params:xml:ns:carddav:text-match':
case 'urn:ietf:params:xml:ns:caldav:text-match':
$search = $v->GetContent();
$haystack = $this->Value();
$match = isset($haystack);
if ( $match ) {
$collation = $v->GetAttribute("collation");
switch( strtolower($collation) ) {
case 'i;octet':
break;
case 'i;ascii-casemap':
case 'i;unicode-casemap':
default:
$search = strtolower( $search );
$haystack = strtolower( $haystack );
break;
}
$matchType = $v->GetAttribute("match-type");
switch( strtolower($matchType) ) {
case 'equals':
$match = ( $haystack === $search );
break;
case 'starts-with':
$length = strlen($search);
if ($length == 0) {
$match = true;
} else {
$match = !strncmp($haystack, $search, $length);
}
break;
case 'ends-with':
$length = strlen($search);
if ($length == 0) {
$match = true;
} else {
$match = ( substr($haystack, -$length) === $search );
}
break;
default:
$match = strstr( $haystack, $search );
break;
}
}
$negate = $v->GetAttribute("negate-condition");
if ( isset($negate) && strtolower($negate) == "yes" ) {
$match = !$match;
}
if ( ! $match ) return false;
break;
case 'urn:ietf:params:xml:ns:carddav:param-filter':
case 'urn:ietf:params:xml:ns:caldav:param-filter':
$subfilter = $v->GetContent();
$parameter = $this->GetParameterValue($v->GetAttribute("name"));
if ( ! $this->TestParamFilter($subfilter,$parameter) ) return false;
break;
default:
dbg_error_log( 'myComponent', ' vProperty::TestFilter: unhandled tag "%s"', $tag );
break;
}
}
return true;
}
function fill($sp, $en, $pe){
}
function TestParamFilter( $filters, $parameter_value ) {
foreach( $filters AS $k => $v ) {
$subtag = $v->GetNSTag();
switch( $subtag ) {
case 'urn:ietf:params:xml:ns:caldav:is-defined':
case 'urn:ietf:params:xml:ns:carddav:is-defined':
if ( empty($parameter_value) ) return false;
break;
case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
case 'urn:ietf:params:xml:ns:carddav:is-not-defined':
if ( ! empty($parameter_value) ) return false;
break;
case 'urn:ietf:params:xml:ns:caldav:time-range':
break;
case 'urn:ietf:params:xml:ns:carddav:text-match':
case 'urn:ietf:params:xml:ns:caldav:text-match':
$search = $v->GetContent();
$match = false;
if ( !empty($parameter_value) ) $match = strstr( $this->content, $search );
$negate = $v->GetAttribute("negate-condition");
if ( isset($negate) && strtolower($negate) == "yes" ) {
$match = !$match;
}
if ( ! $match ) return false;
break;
default:
dbg_error_log( 'myComponent', ' vProperty::TestParamFilter: unhandled tag "%s"', $tag );
break;
}
}
return true;
}
}