Back to index

awl  0.53
vComponent.php
Go to the documentation of this file.
00001 <?php
00016 require_once('XMLElement.php');
00017 
00023 class vProperty {
00033   protected $name;
00034 
00040   protected $parameters;
00041 
00047   protected $content;
00048 
00054   protected $rendered;
00055 
00066   function __construct( $propstring = null ) {
00067     $this->name = "";
00068     $this->content = "";
00069     $this->parameters = array();
00070     unset($this->rendered);
00071     if ( $propstring != null && gettype($propstring) == 'string' ) {
00072       $this->ParseFrom($propstring);
00073     }
00074   }
00075 
00076 
00085   function ParseFrom( $propstring ) {
00086     $this->rendered = (strlen($propstring) < 73 ? $propstring : null);  // Only pre-rendered if we didn't unescape it
00087 
00088     $unescaped = preg_replace( '{\\\\[nN]}', "\n", $propstring);
00089 
00090     // Split into two parts on : which is not preceded by a \, or within quotes like "str:ing".
00091     $offset = 0;
00092     do {
00093       $splitpos = strpos($unescaped,':',$offset);
00094       $start = substr($unescaped,0,$splitpos);
00095       if ( substr($start,-1) == '\\' ) {
00096         $offset = $splitpos + 1;
00097         continue;
00098       }
00099       $quotecount = strlen(preg_replace('{[^"]}', '', $start ));
00100       if ( ($quotecount % 2) != 0 ) {
00101         $offset = $splitpos + 1;
00102         continue;
00103       }
00104       break;
00105     }
00106     while( true );
00107     $values = substr($unescaped,$splitpos+1);
00108     $this->content = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $values);
00109 
00110     // Split on ; which is not preceded by a \
00111     $parameters = preg_split( '{(?<!\\\\);}', $start);
00112 
00113     $this->name = strtoupper(array_shift( $parameters ));
00114     $this->parameters = array();
00115     foreach( $parameters AS $k => $v ) {
00116       $pos = strpos($v,'=');
00117       $name = strtoupper(substr( $v, 0, $pos));
00118       $value = substr( $v, $pos + 1);
00119       if ( preg_match( '{^"(.*)"$}', $value, $matches) ) {
00120         $value = $matches[1];
00121       }
00122       if ( isset($this->parameters[$name]) && is_array($this->parameters[$name]) ) {
00123         $this->parameters[$name][] = $value;
00124       }
00125       elseif ( isset($this->parameters[$name]) ) {
00126         $this->parameters[$name] = array( $this->parameters[$name], $value);
00127       }
00128       else
00129         $this->parameters[$name] = $value;
00130     }
00131 //    dbg_error_log('vComponent', " vProperty::ParseFrom found '%s' = '%s' with %d parameters", $this->name, substr($this->content,0,200), count($this->parameters) );
00132   }
00133 
00134 
00142   function Name( $newname = null ) {
00143     if ( $newname != null ) {
00144       $this->name = strtoupper($newname);
00145       if ( isset($this->rendered) ) unset($this->rendered);
00146 //      dbg_error_log('vComponent', " vProperty::Name(%s)", $this->name );
00147     }
00148     return $this->name;
00149   }
00150 
00151 
00159   function Value( $newvalue = null ) {
00160     if ( $newvalue != null ) {
00161       $this->content = $newvalue;
00162       if ( isset($this->rendered) ) unset($this->rendered);
00163     }
00164     return $this->content;
00165   }
00166 
00167 
00175   function Parameters( $newparams = null ) {
00176     if ( $newparams != null ) {
00177       $this->parameters = array();
00178       foreach( $newparams AS $k => $v ) {
00179         $this->parameters[strtoupper($k)] = $v;
00180       }
00181       if ( isset($this->rendered) ) unset($this->rendered);
00182     }
00183     return $this->parameters;
00184   }
00185 
00186 
00194   function TextMatch( $search ) {
00195     if ( isset($this->content) ) return strstr( $this->content, $search );
00196     return false;
00197   }
00198 
00199 
00207   function GetParameterValue( $name ) {
00208     $name = strtoupper($name);
00209     if ( isset($this->parameters[$name]) ) return $this->parameters[$name];
00210     return null;
00211   }
00212 
00220   function SetParameterValue( $name, $value ) {
00221     if ( isset($this->rendered) ) unset($this->rendered);
00222     $this->parameters[strtoupper($name)] = $value;
00223 //    dbg_error_log('PUT', $this->name.$this->RenderParameters().':'.$this->content );
00224   }
00225 
00226   
00227   private static function escapeParameter($p) {
00228     if ( strpos($p, ';') === false && strpos($p, ':') === false ) return $p;
00229     return '"'.str_replace('"','\\"',$p).'"';    
00230   }
00231 
00236   function RenderParameters() {
00237     $rendered = "";
00238     foreach( $this->parameters AS $k => $v ) {
00239       if ( is_array($v) ) {
00240         foreach( $v AS $vv ) {
00241           $rendered .= sprintf( ';%s=%s', $k, vProperty::escapeParameter($vv) );
00242         }
00243       }
00244       else {
00245           $rendered .= sprintf( ';%s=%s', $k, vProperty::escapeParameter($v) );
00246       }
00247     }
00248     return $rendered;
00249   }
00250 
00251 
00255   function Render( $force = false ) {
00256     // If we still have the string it was parsed in from, it hasn't been screwed with
00257     // and we can just return that without modification.
00258     if ( $force === false && isset($this->rendered) ) return $this->rendered;
00259 
00260     $property = preg_replace( '/[;].*$/', '', $this->name );
00261     $escaped = $this->content;
00262     switch( $property ) {
00264       case 'ATTACH':                case 'GEO':                       case 'PERCENT-COMPLETE':      case 'PRIORITY':
00265       case 'DURATION':              case 'FREEBUSY':                  case 'TZOFFSETFROM':          case 'TZOFFSETTO':
00266       case 'TZURL':                 case 'ATTENDEE':                  case 'ORGANIZER':             case 'RECURRENCE-ID':
00267       case 'URL':                   case 'EXRULE':                    case 'SEQUENCE':              case 'CREATED':
00268       case 'RRULE':                 case 'REPEAT':                    case 'TRIGGER':               case 'RDATE':
00269       case 'COMPLETED':             case 'DTEND':                     case 'DUE':                   case 'DTSTART':
00270       case 'DTSTAMP':               case 'LAST-MODIFIED':             case 'CREATED':               case 'EXDATE':
00271         break;
00272 
00274       case 'ADR':                case 'N':
00275         // escaping for ';' for these fields also needs to happen to the components they are built from. 
00276         $escaped = str_replace( '\\', '\\\\', $escaped);
00277         $escaped = preg_replace( '/\r?\n/', '\\n', $escaped);
00278         $escaped = str_replace( ',', '\\,', $escaped);
00279         break;
00280         
00282       default:
00283         $escaped = str_replace( '\\', '\\\\', $escaped);
00284         $escaped = preg_replace( '/\r?\n/', '\\n', $escaped);
00285         $escaped = preg_replace( "/([,;])/", '\\\\$1', $escaped);
00286     }
00287 
00288     $property = sprintf( "%s%s:", $this->name, $this->RenderParameters() );
00289     if ( (strlen($property) + strlen($escaped)) <= 72 ) {
00290       $this->rendered = $property . $escaped;
00291     }
00292     else if ( (strlen($property) <= 72) && (strlen($escaped) <= 72) ) {
00293       $this->rendered = $property . "\r\n " . $escaped;
00294     }
00295     else {
00296       $this->rendered = preg_replace( '/(.{72})/u', '$1'."\r\n ", $property.$escaped );
00297     }
00298 //    trace_bug( 'Re-rendered "%s" property.', $this->name );
00299     return $this->rendered;
00300   }
00301 
00302   
00303   public function __toString() {
00304     return $this->Render();
00305   }
00306 
00307   
00317   function TestFilter( $filters ) {
00318     foreach( $filters AS $k => $v ) {
00319       $tag = $v->GetNSTag();
00320 //      dbg_error_log( 'vCalendar', "vProperty:TestFilter: '%s'='%s' => '%s'", $this->name, $tag, $this->content );
00321       switch( $tag ) {
00322         case 'urn:ietf:params:xml:ns:caldav:is-defined':
00323         case 'urn:ietf:params:xml:ns:carddav:is-defined':
00324           if ( empty($this->content) ) return false;
00325           break;
00326         
00327         case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
00328         case 'urn:ietf:params:xml:ns:carddav:is-not-defined':
00329           if ( ! empty($this->content) ) return false;
00330           break;
00331 
00332         case 'urn:ietf:params:xml:ns:caldav:time-range':
00334           break;
00335 
00336         case 'urn:ietf:params:xml:ns:carddav:text-match':
00337         case 'urn:ietf:params:xml:ns:caldav:text-match':
00338           $search = $v->GetContent();
00339           $match = $this->TextMatch($search);
00340           $negate = $v->GetAttribute("negate-condition");
00341           if ( isset($negate) && strtolower($negate) == "yes" ) {
00342             $match = !$match;
00343           }
00344           if ( ! $match ) return false;
00345           break;
00346 
00347         case 'urn:ietf:params:xml:ns:carddav:param-filter':
00348         case 'urn:ietf:params:xml:ns:caldav:param-filter':
00349           $subfilter = $v->GetContent();
00350           $parameter = $this->GetParameterValue($v->GetAttribute("name"));
00351           if ( ! $this->TestParamFilter($subfilter,$parameter) ) return false;
00352           break;
00353 
00354         default:
00355           dbg_error_log( 'vComponent', ' vProperty::TestFilter: unhandled tag "%s"', $tag );
00356           break;
00357       }
00358     }
00359     return true;
00360   }
00361 
00362 
00363   function TestParamFilter( $filters, $parameter_value ) {
00364     foreach( $filters AS $k => $v ) {
00365       $subtag = $v->GetNSTag();
00366 //      dbg_error_log( 'vCalendar', "vProperty:TestParamFilter: '%s'='%s' => '%s'", $this->name, $subtag, $parameter_value );
00367       switch( $subtag ) {
00368         case 'urn:ietf:params:xml:ns:caldav:is-defined':
00369         case 'urn:ietf:params:xml:ns:carddav:is-defined':
00370           if ( empty($parameter_value) ) return false;
00371           break;
00372 
00373         case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
00374         case 'urn:ietf:params:xml:ns:carddav:is-not-defined':
00375           if ( ! empty($parameter_value) ) return false;
00376           break;
00377 
00378         case 'urn:ietf:params:xml:ns:caldav:time-range':
00380           break;
00381 
00382         case 'urn:ietf:params:xml:ns:carddav:text-match':
00383         case 'urn:ietf:params:xml:ns:caldav:text-match':
00384           $search = $v->GetContent();
00385           $match = false;
00386           if ( !empty($parameter_value) ) $match = strstr( $this->content, $search );
00387           $negate = $v->GetAttribute("negate-condition");
00388           if ( isset($negate) && strtolower($negate) == "yes" ) {
00389             $match = !$match;
00390           }
00391           if ( ! $match ) return false;
00392           break;
00393 
00394         default:
00395           dbg_error_log( 'vComponent', ' vProperty::TestParamFilter: unhandled tag "%s"', $tag );
00396           break;
00397       }
00398     }
00399     return true;
00400   }
00401 }
00402 
00403 
00409 class vComponent {
00419   protected $type;
00420 
00426   protected $properties;
00427 
00433   protected $components;
00434 
00440   protected $rendered;
00441 
00447   function __construct( $content = null ) {
00448     $this->type = "";
00449     $this->properties = array();
00450     $this->components = array();
00451     $this->rendered = "";
00452     if ( $content != null && (gettype($content) == 'string' || gettype($content) == 'array') ) {
00453       $this->ParseFrom($content);
00454     }
00455   }
00456 
00457   
00462   function CollectParameterValues( $parameter_name ) {
00463     $values = array();
00464     foreach( $this->components AS $k => $v ) {
00465       $also = $v->CollectParameterValues($parameter_name);
00466       $values = array_merge( $values, $also );
00467     }
00468     foreach( $this->properties AS $k => $v ) {
00469       $also = $v->GetParameterValue($parameter_name);
00470       if ( isset($also) && $also != "" ) {
00471 //        dbg_error_log( 'vComponent', "::CollectParameterValues(%s) : Found '%s'", $parameter_name, $also);
00472         $values[$also] = 1;
00473       }
00474     }
00475     return $values;
00476   }
00477 
00478 
00483   function ParseFrom( $content ) {
00484     $this->rendered = $content;
00485     $content = $this->UnwrapComponent($content);
00486 
00487     $type = false;
00488     $subtype = false;
00489     $finish = null;
00490     $subfinish = null;
00491 
00492     $length = strlen($content);
00493     $linefrom = 0;
00494     while( $linefrom < $length ) {
00495       $lineto = strpos( $content, "\n", $linefrom );
00496       if ( $lineto === false ) {
00497         $lineto = strpos( $content, "\r", $linefrom );
00498       }
00499       if ( $lineto > 0 ) {
00500         $line = substr( $content, $linefrom, $lineto - $linefrom);
00501         $linefrom = $lineto + 1;
00502       }
00503       else {
00504         $line = substr( $content, $linefrom );
00505         $linefrom = $length;
00506       }
00507       if ( preg_match('/^\s*$/', $line ) ) continue;
00508       $line = rtrim( $line, "\r\n" );
00509 //      dbg_error_log( 'vComponent',  "::ParseFrom: Parsing line: $line");
00510 
00511       if ( $type === false ) {
00512         if ( preg_match( '/^BEGIN:(.+)$/i', $line, $matches ) ) {
00513           // We have found the start of the main component
00514           $type = strtoupper($matches[1]);
00515           $finish = 'END:'.$type;
00516           $this->type = $type;
00517 //          dbg_error_log( 'vComponent', "::ParseFrom: Start component of type '%s'", $type);
00518         }
00519         else {
00520           dbg_error_log( 'vComponent', "::ParseFrom: Ignoring crap before start of component: $line");
00521           // unset($lines[$k]);  // The content has crap before the start
00522           if ( $line != "" ) $this->rendered = null;
00523         }
00524       }
00525       else if ( $type == null ) {
00526         dbg_error_log( 'vComponent', "::ParseFrom: Ignoring crap after end of component");
00527         if ( $line != "" ) $this->rendered = null;
00528       }
00529       else if ( strtoupper($line) == $finish ) {
00530 //        dbg_error_log( 'vComponent', "::ParseFrom: End of component");
00531         $type = null;  // We have reached the end of our component
00532       }
00533       else {
00534         if ( $subtype === false && preg_match( '/^BEGIN:(.+)$/i', $line, $matches ) ) {
00535           // We have found the start of a sub-component
00536           $subtype = strtoupper($matches[1]);
00537           $subfinish = "END:$subtype";
00538           $subcomponent = $line . "\r\n";
00539 //          dbg_error_log( 'vComponent', "::ParseFrom: Found a subcomponent '%s'", $subtype);
00540         }
00541         else if ( $subtype ) {
00542           // We are inside a sub-component
00543           $subcomponent .= $this->WrapComponent($line);
00544           if ( strtoupper($line) == $subfinish ) {
00545 //            dbg_error_log( 'vComponent', "::ParseFrom: End of subcomponent '%s'", $subtype);
00546             // We have found the end of a sub-component
00547             $this->components[] = new vComponent($subcomponent);
00548             $subtype = false;
00549           }
00550 //          else
00551 //            dbg_error_log( 'vComponent', "::ParseFrom: Inside a subcomponent '%s'", $subtype );
00552         }
00553         else {
00554 //          dbg_error_log( 'vComponent', "::ParseFrom: Parse property of component");
00555           // It must be a normal property line within a component.
00556           $this->properties[] = new vProperty($line);
00557         }
00558       }
00559     }
00560   }
00561 
00562 
00568   function UnwrapComponent( $content ) {
00569     return preg_replace('/\r?\n[ \t]/', '', $content );
00570   }
00571 
00580   function WrapComponent( $content ) {
00581     $strs = preg_split( "/\r?\n/", $content );
00582     $wrapped = "";
00583     foreach ($strs as $str) {
00584       $wrapped .= preg_replace( '/(.{72})/u', '$1'."\r\n ", $str ) ."\r\n";
00585     }
00586     return $wrapped;
00587   }
00588 
00592   function GetType() {
00593     return $this->type;
00594   }
00595 
00596 
00600   function SetType( $type ) {
00601     if ( isset($this->rendered) ) unset($this->rendered);
00602     $this->type = strtoupper($type);
00603     return $this->type;
00604   }
00605 
00606 
00610   function GetProperty( $type ) {
00611     foreach( $this->properties AS $k => $v ) {
00612       if ( is_object($v) && $v->Name() == $type ) {
00613         return $v;
00614       }
00615       else if ( !is_object($v) ) {
00616         debug_error_log("ERROR", 'vComponent::GetProperty(): Trying to get %s on %s which is not an object!', $type, $v );
00617       }
00618     }
00620     return null;
00621   }
00622 
00623 
00627   function GetPValue( $type ) {
00628     $p = $this->GetProperty($type);
00629     if ( isset($p) ) return $p->Value();
00630     return null;
00631   }
00632 
00633   
00638   function GetProperties( $type = null ) {
00639     $properties = array();
00640     $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
00641     foreach( $this->properties AS $k => $v ) {
00642       if ( $type == null || (isset($testtypes[$v->Name()]) && $testtypes[$v->Name()]) ) {
00643         $properties[] = $v;
00644       }
00645     }
00646     return $properties;
00647   }
00648 
00649 
00655   function ClearProperties( $type = null ) {
00656     if ( $type != null ) {
00657       $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
00658       // First remove all the existing ones of that type
00659       foreach( $this->properties AS $k => $v ) {
00660         if ( isset($testtypes[$v->Name()]) && $testtypes[$v->Name()] ) {
00661           unset($this->properties[$k]);
00662           if ( isset($this->rendered) ) unset($this->rendered);
00663         }
00664       }
00665       $this->properties = array_values($this->properties);
00666     }
00667     else {
00668       if ( isset($this->rendered) ) unset($this->rendered);
00669       $this->properties = array();
00670     }
00671   }
00672 
00673 
00677   function SetProperties( $new_properties, $type = null ) {
00678     if ( isset($this->rendered) && count($new_properties) > 0 ) unset($this->rendered);
00679     $this->ClearProperties($type);
00680     foreach( $new_properties AS $k => $v ) {
00681       $this->properties[] = $v;
00682     }
00683   }
00684 
00685 
00693   function AddProperty( $new_property, $value = null, $parameters = null ) {
00694     if ( isset($this->rendered) ) unset($this->rendered);
00695     if ( isset($value) && gettype($new_property) == 'string' ) {
00696       $new_prop = new vProperty();
00697       $new_prop->Name($new_property);
00698       $new_prop->Value($value);
00699       if ( $parameters != null ) $new_prop->Parameters($parameters);
00700 //      dbg_error_log('vComponent'," Adding new property '%s'", $new_prop->Render() );
00701       $this->properties[] = $new_prop;
00702     }
00703     else if ( $new_property instanceof vProperty ) {
00704       $this->properties[] = $new_property;
00705     }
00706   }
00707 
00708 
00712   function ComponentCount() {
00713     return count($this->components);
00714   }
00715 
00716 
00726   function GetComponents( $type = null, $normal_match = true ) {
00727     $components = $this->components;
00728     if ( $type != null ) {
00729       $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
00730       foreach( $components AS $k => $v ) {
00731 //        printf( "Type: %s, %s, %s\n", $v->GetType(),
00732 //                 ($normal_match && isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] ? 'true':'false'),
00733 //                 ( !$normal_match && (!isset($testtypes[$v->GetType()]) || !$testtypes[$v->GetType()]) ? 'true':'false')
00734 //               );
00735         if ( !($normal_match && isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] )
00736             && !( !$normal_match && (!isset($testtypes[$v->GetType()]) || !$testtypes[$v->GetType()])) ) {
00737           unset($components[$k]);
00738         }
00739       }
00740       $components = array_values($components);
00741     }
00742 //    print_r($components);
00743     return $components;
00744   }
00745 
00746 
00751   function ClearComponents( $type = null ) {
00752     if ( $type != null ) {
00753       $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
00754       // First remove all the existing ones of that type
00755       foreach( $this->components AS $k => $v ) {
00756         if ( isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] ) {
00757           unset($this->components[$k]);
00758           if ( isset($this->rendered) ) unset($this->rendered);
00759         }
00760         else {
00761           if ( ! $this->components[$k]->ClearComponents($testtypes) ) {
00762             if ( isset($this->rendered) ) unset($this->rendered);
00763           }
00764         }
00765       }
00766       return isset($this->rendered);
00767     }
00768     else {
00769       if ( isset($this->rendered) ) unset($this->rendered);
00770       $this->components = array();
00771       return false;
00772     }
00773   }
00774 
00775 
00782   function SetComponents( $new_component, $type = null ) {
00783     if ( isset($this->rendered) ) unset($this->rendered);
00784     $this->ClearComponents($type);
00785     foreach( $new_component AS $k => $v ) {
00786       $this->components[] = $v;
00787     }
00788   }
00789 
00790 
00796   function AddComponent( $new_component ) {
00797     if ( is_array($new_component) && count($new_component) == 0 ) return;
00798     if ( isset($this->rendered) ) unset($this->rendered);
00799     if ( is_array($new_component) ) {
00800       foreach( $new_component AS $k => $v ) {
00801         $this->components[] = $v;
00802       }
00803     }
00804     else {
00805       $this->components[] = $new_component;
00806     }
00807   }
00808 
00809 
00815   function MaskComponents( $keep, $recursive = true ) {
00816     foreach( $this->components AS $k => $v ) {
00817       if ( !isset($keep[$v->GetType()]) ) {
00818         unset($this->components[$k]);
00819         if ( isset($this->rendered) ) unset($this->rendered);
00820       }
00821       else if ( $recursive ) {
00822         $v->MaskComponents($keep);
00823       }
00824     }
00825   }
00826 
00827 
00833   function MaskProperties( $keep, $component_list=null ) {
00834     if ( !isset($component_list) || isset($component_list[$this->type]) ) {
00835       foreach( $this->properties AS $k => $v ) {
00836         if ( !isset($keep[$v->Name()]) || !$keep[$v->Name()] ) {
00837           unset($this->properties[$k]);
00838           if ( isset($this->rendered) ) unset($this->rendered);
00839         }
00840       }
00841     }
00842     foreach( $this->components AS $k => $v ) {
00843       $v->MaskProperties($keep, $component_list);
00844     }
00845   }
00846 
00847 
00851   function Render( $restricted_properties = null, $force_rendering = false ) {
00852 
00853     $unrestricted = (!isset($restricted_properties) || count($restricted_properties) == 0);
00854 
00855     if ( !$force_rendering && isset($this->rendered) && $unrestricted )
00856       return $this->rendered;
00857 
00858     $rendered = "BEGIN:$this->type\r\n";
00859     foreach( $this->properties AS $k => $v ) {
00860       if ( method_exists($v, 'Render') ) {
00861         if ( $unrestricted || isset($restricted_properties[$v]) ) $rendered .= $v->Render() . "\r\n";
00862       }
00863     }
00864     foreach( $this->components AS $v ) {   $rendered .= $v->Render( $restricted_properties, $force_rendering );  }
00865     $rendered .= "END:$this->type\r\n";
00866 
00867     $rendered = preg_replace('{(?<!\r)\n}', "\r\n", $rendered);
00868     if ( $unrestricted ) $this->rendered = $rendered;
00869 
00870     return $rendered;
00871   }
00872 
00873 
00874     
00875   public function __toString() {
00876     return $this->Render();
00877   }
00878 
00879   
00889   function GetPropertiesByPath( $path ) {
00890     $properties = array();
00891     dbg_error_log( 'vComponent', "GetPropertiesByPath: Querying within '%s' for path '%s'", $this->type, $path );
00892     if ( !preg_match( '#(/?)(!?)([^/]+)(/?.*)$#', $path, $matches ) ) return $properties;
00893 
00894     $anchored = ($matches[1] == '/');
00895     $inverted = ($matches[2] == '!');
00896     $ourtest = $matches[3];
00897     $therest = $matches[4];
00898     dbg_error_log( 'vComponent', "GetPropertiesByPath: Matches: %s -- %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3], $matches[4] );
00899     if ( $ourtest == '*' || (($ourtest == $this->type) !== $inverted) && $therest != '' ) {
00900       if ( preg_match( '#^/(!?)([^/]+)$#', $therest, $matches ) ) {
00901         $normmatch = ($matches[1] =='');
00902         $proptest  = $matches[2];
00903         foreach( $this->properties AS $k => $v ) {
00904           if ( $proptest == '*' || (($v->Name() == $proptest) === $normmatch ) ) {
00905             $properties[] = $v;
00906           }
00907         }
00908       }
00909       else {
00913         foreach( $this->components AS $k => $v ) {
00914           $properties = array_merge( $properties, $v->GetPropertiesByPath($therest) );
00915         }
00916       }
00917     }
00918 
00919     if ( ! $anchored ) {
00923       foreach( $this->components AS $k => $v ) {
00924         $properties = array_merge( $properties, $v->GetPropertiesByPath($path) );
00925       }
00926     }
00927     dbg_error_log('vComponent', "GetPropertiesByPath: Found %d within '%s' for path '%s'\n", count($properties), $this->type, $path );
00928     return $properties;
00929   }
00930 
00931 
00932 
00942   function TestFilter( $filters ) {
00943     foreach( $filters AS $k => $v ) {
00944       $tag = $v->GetNSTag();
00945 //      dbg_error_log( 'vCalendar', ":TestFilter: '%s' ", $tag );
00946       switch( $tag ) {
00947         case 'urn:ietf:params:xml:ns:caldav:is-defined':
00948         case 'urn:ietf:params:xml:ns:carddav:is-defined':
00949           if ( count($this->properties) == 0 && count($this->components) == 0 ) return false;
00950           break;
00951         
00952         case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
00953         case 'urn:ietf:params:xml:ns:carddav:is-not-defined':
00954           if ( count($this->properties) > 0 || count($this->components) > 0 ) return false;
00955           break;
00956 
00957         case 'urn:ietf:params:xml:ns:caldav:comp-filter':
00958         case 'urn:ietf:params:xml:ns:carddav:comp-filter':
00959           $subcomponents = $this->GetComponents($v->GetAttribute('name'));
00960           $subfilter = $v->GetContent();
00961 //          dbg_error_log( 'vCalendar', ":TestFilter: Found '%d' (of %d) subs of type '%s'",
00962 //                       count($subcomponents), count($this->components), $v->GetAttribute('name') );
00963           $subtag = $subfilter[0]->GetNSTag(); 
00964           if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-not-defined'
00965                              || $subtag == 'urn:ietf:params:xml:ns:carddav:is-not-defined' ) {
00966             if ( count($properties) > 0 ) {
00967 //              dbg_error_log( 'vComponent', ":TestFilter: Wanted none => false" );
00968               return false;
00969             }
00970           }
00971           else if ( count($subcomponents) == 0 ) {
00972             if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-defined'
00973                              || $subtag == 'urn:ietf:params:xml:ns:carddav:is-defined' ) {
00974 //              dbg_error_log( 'vComponent', ":TestFilter: Wanted some => false" );
00975               return false;
00976             }
00977             else {
00978 //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted something from missing sub-components => false" );
00979               $negate = $subfilter[0]->GetAttribute("negate-condition");
00980               if ( empty($negate) || strtolower($negate) != 'yes' ) return false;
00981             }
00982           }
00983           else {
00984             foreach( $subcomponents AS $kk => $subcomponent ) {
00985               if ( ! $subcomponent->TestFilter($subfilter) ) return false;
00986             }
00987           }
00988           break;
00989 
00990         case 'urn:ietf:params:xml:ns:carddav:prop-filter':
00991         case 'urn:ietf:params:xml:ns:caldav:prop-filter':
00992           $subfilter = $v->GetContent();
00993           $properties = $this->GetProperties($v->GetAttribute("name"));
00994           dbg_error_log( 'vCalendar', ":TestFilter: Found '%d' props of type '%s'", count($properties), $v->GetAttribute('name') );
00995           $subtag = $subfilter[0]->GetNSTag();
00996           if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-not-defined'
00997                              || $subtag == 'urn:ietf:params:xml:ns:carddav:is-not-defined' ) {
00998             if ( count($properties) > 0 ) {
00999 //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted none => false" );
01000               return false;
01001             }
01002           }
01003           else if ( count($properties) == 0 ) {
01004             if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-defined'
01005                              || $subtag == 'urn:ietf:params:xml:ns:carddav:is-defined' ) {
01006 //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted some => false" );
01007               return false;
01008             }
01009             else {
01010 //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted '%s' from missing sub-properties => false", $subtag );
01011               $negate = $subfilter[0]->GetAttribute("negate-condition");
01012               if ( empty($negate) || strtolower($negate) != 'yes' ) return false;
01013             }
01014           }
01015           else {
01016             foreach( $properties AS $kk => $property ) {
01017               if ( !$property->TestFilter($subfilter) ) return false;
01018             }
01019           }
01020           break;
01021       }
01022     }
01023     return true;
01024   }
01025 
01026 }
01027